xref: /freebsd/contrib/bc/build.rig (revision fdc4a7c8012b214986cfa2e2fb6d99731f004b1b)
1/*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 *   list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 *   this list of conditions and the following disclaimer in the documentation
16 *   and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *****************************************************************************
31 *
32 * The build script file.
33 *
34 */
35
36if OS == "Windows" && bool(config["lto"])
37{
38	error("Link-time optimization is not supported on Windows");
39}
40
41if LIBRARY_ENABLED == "0"
42{
43	if OS != "Windows" && NLS_ENABLED != "0"
44	{
45		io.eprint("Testing NLS...\n");
46
47		clang_flags: []str =
48		if CC contains "clang"
49		{
50			@[ "-Wno_unreachable-code" ];
51		};
52
53		flags: []str = clang_flags +~ @[
54			DEFOPT +~ "BC_ENABLE_NLS=1",
55			DEFOPT +~ "BC_ENABLED=" +~ BC_ENABLED,
56			DEFOPT +~ "DC_ENABLED=" +~ DC_ENABLED,
57			DEFOPT +~ "BC_ENABLE_HISTORY=0",
58			DEFOPT +~ "BC_ENABLE_LIBRARY=0",
59			DEFOPT +~ "BC_ENABLE_AFL=0",
60			DEFOPT +~ "BC_ENABLE_EXTRA_MATH=" +~ EXTRA_MATH_ENABLED,
61			DEFOPT +~ "BC_ENABLE_OSSFUZZ=0",
62			DEFOPT +~ "_POSIX_C_SOURCE=200809L",
63			DEFOPT +~ "_XOPEN_SOURCE=700",
64			INCOPT,
65		];
66
67		res := $ $CC %(flags) -c @(path.join(src_dir, "src/vm.c")) -E;
68
69		if res.exitcode != 0
70		{
71			if FORCE
72			{
73				io.eprint("Forcing NLS...\n");
74			}
75			else
76			{
77				error("NLS does not work\n");
78			}
79		}
80		else
81		{
82			if path.isfile("vm.o")
83			{
84				path.rm("vm.o");
85			}
86
87			io.eprint("NLS works.\n\n");
88			io.eprint("Testing gencat...\n");
89
90			res2 := $ gencat ./en_US.cat
91			          @(path.join(src_dir, "locales/en_US.msg"));
92
93			if res2.exitcode != 0
94			{
95				if FORCE
96				{
97					io.eprint("Forcing NLS...\n");
98				}
99				else
100				{
101					error("gencat does not work\n");
102				}
103			}
104			else
105			{
106				io.eprint("gencat works.\n\n");
107
108				if platform != host
109				{
110					error("Cross compiles will not work!\n\n");
111				}
112			}
113		}
114	}
115
116	if OS != "Windows" && sym(config["history"]) != @none
117	{
118		io.eprint("Testing history...\n");
119
120		flags: []str = @[
121			DEFOPT +~ "BC_ENABLE_HISTORY=1",
122			DEFOPT +~ "BC_ENABLED=" +~ BC_ENABLED,
123			DEFOPT +~ "DC_ENABLED=" +~ DC_ENABLED,
124			DEFOPT +~ "BC_ENABLE_NLS=" +~ NLS_ENABLED,
125			DEFOPT +~ "BC_ENABLE_LIBRARY=0",
126			DEFOPT +~ "BC_ENABLE_AFL=0",
127			DEFOPT +~ "BC_ENABLE_EDITLINE=" +~ EDITLINE_ENABLED,
128			DEFOPT +~ "BC_ENABLE_READLINE=" +~ READLINE_ENABLED,
129			DEFOPT +~ "BC_ENABLE_EXTRA_MATH=" +~ EXTRA_MATH_ENABLED,
130			DEFOPT +~ "BC_ENABLE_OSSFUZZ=0",
131			DEFOPT +~ "_POSIX_C_SOURCE=200809L",
132			DEFOPT +~ "_XOPEN_SOURCE=700",
133			INCOPT,
134		];
135
136		res := $ $CC %(flags) -c @(path.join(src_dir, "src/history.c")) -E;
137
138		if res.exitcode != 0
139		{
140			if FORCE
141			{
142				io.eprint("Forcing history...\n");
143			}
144			else
145			{
146				error("History does not work\n");
147			}
148		}
149		else
150		{
151			if path.isfile("history.o")
152			{
153				path.rm("history.o");
154			}
155
156			io.eprint("History works.\n\n");
157		}
158	}
159}
160
161freebsd_flags: []str =
162if OS != "FreeBSD"
163{
164	@[ DEFOPT +~ "_POSIX_C_SOURCE=200809L", DEFOPT +~ "_XOPEN_SOURCE=700" ];
165};
166
167macos: bool = (OS == "Darwin");
168
169macos_flags: []str =
170if macos
171{
172	@[ DEFOPT +~ "_DARWIN_C_SOURCE" ];
173};
174
175openbsd_flags: []str =
176if OS == "OpenBSD"
177{
178	if READLINE_ENABLED != "0"
179	{
180		error("Cannot use readline on OpenBSD");
181	}
182
183	@[ DEFOPT +~ "_BSD_SOURCE" ];
184};
185
186strip_flag: []str =
187if OS != "Windows" && !bool(config["debug"]) && !macos && bool(config["strip"])
188{
189	@[ "-s" ];
190};
191
192lto_flag: []str =
193if bool(config["lto"])
194{
195	@[ "-flto" ];
196};
197
198strict_flags: []str =
199if bool(config["strict"])
200{
201	// Strict build only works for GCC and Clang, so we do want to set that
202	// here.
203	if CC contains "gcc" || CC contains "clang"
204	{
205		// These are the standard strict build flags for both compilers.
206		std_strict: []str = @[ "-Wall", "-Wextra", "-Werror", "-pedantic" ];
207
208		// Clang has -Weverything, which I ensure Yc builds under.
209		//
210		// I also want unlimited errors because Clang is my development
211		// compiler; it caps at 20 by default.
212		compiler_strict: []str =
213		if CC contains "clang"
214		{
215			// Oh, and add the standard.
216			@[ "-Weverything", "-ferror-limit=100000", "-Wno-padded",
217			   "-Wno-unknown-warning-option", "-Wno-unsafe-buffer-usage",
218			   "-Wno-documentation-unknown-command", "-Wno-pre-c11-compat",
219			   "-Wno-enum-enum-conversion", "-Wno-switch-default" ];
220		};
221
222		// Return the combination of the sets.
223		std_strict +~ compiler_strict;
224	}
225	else if OS == "Windows"
226	{
227		// Return the combo of the strict options, the standard, and the
228		// sanitizer defines.
229		@[ "/W4", "/WX", "/wd\"4996\"", "/permissive-" ];
230	}
231};
232
233version_contents: str = io.read_file(path.join(src_dir, "VERSION.txt"));
234version_lines: []str = version_contents.split("\n");
235version: str = version_lines[0];
236
237version_flag: []str = @[ DEFOPT +~ "VERSION=" +~ version ];
238
239other_flags: []str = freebsd_flags +~ macos_flags +~ openbsd_flags +~
240                     lto_flag +~ strict_flags +~ version_flag +~
241if bool(config["debug"])
242{
243	@[ compiler_db["opt.debug"] ];
244};
245
246history_files: []str =
247if HISTORY != @none
248{
249	HISTORY_C_FILES;
250};
251
252c_files: []str =
253if BUILD_MODE == @both
254{
255	COMMON_C_FILES +~ EXEC_C_FILES +~ BC_C_FILES +~ DC_C_FILES +~ history_files;
256}
257else if BUILD_MODE == @bc
258{
259	COMMON_C_FILES +~ EXEC_C_FILES +~ BC_C_FILES +~ history_files;
260}
261else if BUILD_MODE == @dc
262{
263	COMMON_C_FILES +~ EXEC_C_FILES +~ DC_C_FILES +~ history_files;
264}
265else
266{
267	COMMON_C_FILES +~ LIBRARY_C_FILES;
268};
269
270build_config: Gaml = @(gaml){
271	other_cflags: $other_flags
272	strip_flag: $strip_flag
273};
274
275targets: []str =
276push build_config: config_stack
277{
278	gen_o_files: []str =
279	if BUILD_MODE != @library
280	{
281		@[
282			txt2o("gen/lib.bc", "bc_lib", "bc_lib_name", "BC_ENABLED", true),
283			txt2o("gen/lib2.bc", "bc_lib2", "bc_lib2_name",
284			      "BC_ENABLED && BC_ENABLE_EXTRA_MATH", true),
285			txt2o("gen/bc_help.txt", "bc_help", "", "BC_ENABLED", false),
286			txt2o("gen/dc_help.txt", "dc_help", "", "DC_ENABLED", false),
287		];
288	};
289
290	obj_files: []str = gen_o_files +~
291	for f: c_files
292	{
293		c2o(f);
294	};
295
296	if BUILD_MODE == @both || BUILD_MODE == @bc
297	{
298		if OS != "Windows" && bool(config["install_manpages"])
299		{
300			src: str = path.join("manuals/bc", BUILD_TYPE +~ ".1");
301
302			target BC_MANPAGE: src
303			{
304				$ cp -f @(file_dep) @(tgt);
305			}
306		}
307
308		exe(BC_BIN, obj_files);
309	}
310
311	if BUILD_MODE == @both || BUILD_MODE == @dc
312	{
313		if OS != "Windows" && bool(config["install_manpages"])
314		{
315			src: str = path.join("manuals/dc", BUILD_TYPE +~ ".1");
316
317			target DC_MANPAGE: src
318			{
319				$ cp -f @(file_dep) @(tgt);
320			}
321		}
322
323		if BUILD_MODE == @both
324		{
325			ln(DC_BIN, BC_BIN);
326		}
327		else
328		{
329			exe(DC_BIN, obj_files);
330		}
331	}
332
333	if BUILD_MODE == @library
334	{
335		lib(LIBRARY, obj_files);
336	}
337
338	if BUILD_MODE == @both
339	{
340		@[ BC_BIN, DC_BIN ];
341	}
342	else if BUILD_MODE == @bc
343	{
344		@[ DC_BIN ];
345	}
346	else if BUILD_MODE == @dc
347	{
348		@[ DC_BIN ];
349	}
350	else
351	{
352		includedir: str = get_includedir();
353		libdir: str = get_libdir();
354
355		pc_config: Gaml = @(gaml){
356			INCLUDEDIR: $includedir
357			LIBDIR: $libdir
358			VERSION: $version
359		};
360
361		push pc_config: config_stack
362		{
363			target PC_FILE: PC_FILE +~ ".in"
364			{
365				configure_file(file_dep, tgt, "%%");
366			}
367		}
368
369		@[ LIBRARY, PC_FILE ];
370	}
371};
372
373if OS != "Windows"
374{
375	if LIBRARY_ENABLED == "0"
376	{
377		target @install: targets
378		{
379			bindir: str = get_bindir();
380
381			if BC_ENABLED != "0"
382			{
383				$ $SAFE_INSTALL $EXEC_INSTALL_MODE $BC_BIN
384				  @(path.join(bindir, BC_BIN));
385			}
386
387			if DC_ENABLED != "0"
388			{
389				if BC_ENABLED != "0"
390				{
391					$ ln -sf @("./" +~ BC_BIN) @(path.join(bindir, DC_BIN));
392				}
393				else
394				{
395					$ $SAFE_INSTALL $EXEC_INSTALL_MODE $BC_BIN
396					  @(path.join(bindir, BC_BIN));
397				}
398			}
399
400			if NLS_ENABLED != "0"
401			{
402				locale_install_args: []str =
403				if sym(config["locales"]) == @all
404				{
405					@[ "-l" ];
406				};
407
408				if DESTDIR != ""
409				{
410					$ @(path.join(src_dir, "scripts/locale_install.sh"))
411					  %(locale_install_args) @(str(config["nlspath"]))
412					  $MAINEXEC $DESTDIR;
413				}
414				else
415				{
416					$ @(path.join(src_dir, "scripts/locale_install.sh"))
417					  %(locale_install_args) @(str(config["nlspath"]))
418					  $MAINEXEC;
419				}
420			}
421
422			if bool(config["install_manpages"])
423			{
424				man1dir: str = get_man1dir();
425
426				if BC_ENABLED != "0"
427				{
428					$ rm -rf @(path.join(man1dir, BC_MANPAGE));
429				}
430
431				if DC_ENABLED != "0"
432				{
433					$ rm -rf @(path.join(man1dir, DC_MANPAGE));
434				}
435			}
436		}
437
438		target @uninstall
439		{
440			bindir: str = get_bindir();
441
442			if BC_ENABLED != "0"
443			{
444				$ rm -rf @(path.join(bindir, BC_BIN));
445			}
446
447			if DC_ENABLED != "0"
448			{
449				$ rm -rf @(path.join(bindir, DC_BIN));
450			}
451
452			if NLS_ENABLED != "0"
453			{
454				if DESTDIR != ""
455				{
456					$ @(path.join(src_dir, "scripts/locale_uninstall.sh"))
457					  @(str(config["nlspath"])) $MAINEXEC $DESTDIR;
458				}
459				else
460				{
461					$ @(path.join(src_dir, "scripts/locale_uninstall.sh"))
462					  @(str(config["nlspath"])) $MAINEXEC;
463				}
464			}
465
466			if bool(config["install_manpages"])
467			{
468				man1dir: str = get_man1dir();
469				$ rm -rf @(path.join(man1dir, BC_MANPAGE))
470				  @(path.join(man1dir, DC_MANPAGE));
471			}
472		}
473	}
474	else
475	{
476		target @install: targets, BCL_HEADER_PATH
477		{
478			full_libdir: str = get_libdir();
479
480			$ $SAFE_INSTALL $EXEC_INSTALL_MODE @(file_dep)
481			  @(path.join(full_libdir, file_dep));
482
483			full_pc_path: str = get_pc_path();
484			bcl_pc: str = file_deps[1];
485
486			$ $SAFE_INSTALL $MANPAGE_INSTALL_MODE $bcl_pc
487			  @(path.join(full_pc_path, bcl_pc));
488
489			full_includedir: str = get_includedir();
490
491			$ $SAFE_INSTALL $MANPAGE_INSTALL_MODE @(file_deps[2])
492			  @(path.join(full_includedir, BCL_HEADER));
493
494			if bool(config["install_manpages"])
495			{
496				$ $SAFE_INSTALL $MANPAGE_INSTALL_MODE
497				  @(path.join(src_dir, path.join("manuals", BCL_MANPAGE)))
498				  @(path.join(get_man3dir(), BCL_MANPAGE));
499			}
500		}
501
502		target @uninstall
503		{
504			$ rm -rf @(path.join(get_libdir(), LIBRARY))
505			  @(path.join(get_pc_path(), PC_FILE))
506			  @(path.join(get_includedir(), BCL_HEADER));
507
508			if bool(config["install_manpages"])
509			{
510				$ rm -rf @(path.join(get_man3dir(), BCL_MANPAGE));
511			}
512		}
513	}
514}
515
516// If the platform matches the host, we can run the test suite.
517if platform == host
518{
519	// If we have the library, build and run that test.
520	if BUILD_MODE == @library
521	{
522		libtesto: str = c2o("tests/bcl.c");
523
524		libtest: str = "bcl";
525
526		exe(libtest, @[ libtesto, targets[0] ]);
527
528		test @bcl: libtest
529		{
530			$ @(str(tgt_name));
531		}
532	}
533	else
534	{
535		if BUILD_MODE != @dc
536		{
537			exe_tests("bc");
538		}
539
540		if BUILD_MODE != @bc
541		{
542			exe_tests("dc");
543		}
544
545		target @clean_tests
546		{
547			for f: path.find_ext(build_dir, "txt")
548			{
549				path.rm(f);
550			}
551		}
552	}
553}
554
555target "bitfuncgen"
556{
557	error("TODO: Make this");
558}
559
560target @bitfuncgen: "bitfuncgen"
561{
562	error("TODO: Make this");
563}
564
565target "ministat"
566{
567	error("TODO: Make this");
568}
569
570target @ministat: "ministat"
571{
572	error("TODO: Make this");
573}
574
575target @all: targets;
576