xref: /freebsd/contrib/bc/scripts/release.pkg.yao (revision 88b8b7f0c4e9948667a2279e78e975a784049cba)
1/**
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice, this
10 *   list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright notice,
13 *   this list of conditions and the following disclaimer in the documentation
14 *   and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29PROG: str = path.realpath(sys.program);
30REAL: str = path.realpath(PROG +~ "..");
31
32SCRIPTDIR: str = path.dirname(PROG);
33
34WINDOWS: bool = (host.os == "Windows");
35
36DEFOPT: str = if WINDOWS { "/D"; } else { "-D"; };
37C11OPT: str = if WINDOWS { "/std:c11"; } else { "-std=c11"; };
38C99OPT: str = if WINDOWS { "/std:c99"; } else { "-std=c99"; };
39
40/**
41 * Turns a string array into a string by joining all strings together with
42 * spaces.
43 * @param arr  The array to join.
44 * @return     The joined array.
45 */
46fn strv2str(arr: []str) -> str
47{
48	ret: !str = "";
49
50	for item: arr
51	{
52		if ret == ""
53		{
54			ret! = item;
55		}
56		else
57		{
58			ret! = ret +~ " " +~ item;
59		}
60	}
61
62	return ret;
63}
64
65opts: Gaml = @(gaml){
66	help: [
67		"Runs the testing part of the release process.\n"
68		"\n"
69		"Usage: ", $PROG, " [-h|--help] [<options> \n"
70	]
71	options: {
72		no64: {
73			help: "Turn off building and testing 64-bit builds."
74			long: @no-64
75			type: @NONE
76		}
77		no128: {
78			help: "Turn off building and testing builds with 128-bit integers."
79			long: @no-128
80			type: @NONE
81		}
82		C: {
83			help: "Turn off building under Clang."
84			short: @C
85			long: @no-clang
86			type: @NONE
87		}
88		C11: {
89			help: "Turn off building under C11."
90			long: @no-c11
91			type: @NONE
92		}
93		G: {
94			help: "Turn off building under GCC."
95			short: @G
96			long: @no-gcc
97			type: @NONE
98		}
99		GEN: {
100			help: "Turn off running the gen script."
101			short: @G
102			long: @no-gen-script
103			type: @NONE
104		}
105		MAKE: {
106			help: "Turn off running the build with `configure.sh` and `make`."
107			long: @no-make
108			type: @NONE
109		}
110		RIG: {
111			help: "Turn off running the build with Rig."
112			long: @no-rig
113			type: @NONE
114		}
115		T: {
116			help: "Turn off running tests."
117			short: @T
118			long: @no-tests
119			type: @NONE
120		}
121		computed-goto: {
122			help: "Whether to test with computed goto or not."
123			long: @computed-goto
124			type: @NONE
125		}
126		e: {
127			help: "Whether to test with editline or not."
128			short: @e
129			long: @editline
130			type: @NONE
131		}
132		g: {
133			help: "Whether generate tests that are generated or not."
134			short: @g
135			long: @generate
136			type: @NONE
137		}
138		history: {
139			help: "Whether to test with history or not."
140			long: @history
141			type: @NONE
142		}
143		k: {
144			help: "Whether to run the Karatsuba tests or not."
145			short: @k
146			long: @karatsuba
147			type: @NONE
148		}
149		p: {
150			help: "Whether to run problematic tests or not."
151			short: @p
152			long: @problematic-tests
153			type: @NONE
154		}
155		r: {
156			help: "Whether to test with readline or not."
157			short: @r
158			long: @readline
159			type: @NONE
160		}
161		s: {
162			help: "Whether to run tests under sanitizers or not."
163			short: @s
164			long: @sanitizers
165			type: @NONE
166		}
167		settings: {
168			help: "Whether to test settings or not."
169			long: @settings
170			type: @NONE
171		}
172		v: {
173			help: "Whether to run under Valgrind or not."
174			short: @v
175			long: @valgrind
176			type: @NONE
177		}
178	}
179};
180
181// These are the booleans for the command-line flags.
182no64: !bool = false;
183no128: !bool = false;
184clang: !bool = true;
185c11: !bool = true;
186gcc: !bool = true;
187gen_host: !bool = true;
188run_make: !bool = !WINDOWS;
189run_rig: !bool = true;
190tests: !bool = true;
191computed_goto: !bool = false;
192editline: !bool = false;
193generated: !bool = false;
194history: !bool = false;
195karatsuba: !bool = false;
196problematic: !bool = false;
197readline: !bool = false;
198sanitizers: !bool = false;
199settings: !bool = false;
200valgrind: !bool = false;
201
202defcc: str = if clang { "clang"; } else if gcc { "gcc"; } else { "c99"; };
203defbits: usize = if no64 { usize(32); } else { usize(64); };
204
205cgoto_flags: []str = if !computed_goto { @[ "-DBC_NO_COMPUTED_GOTO" ]; };
206
207// Set some strict warning flags. Clang's -Weverything can be way too strict, so
208// we actually have to turn off some things.
209CLANG_FLAGS: []str = @[ "-Weverything", "-Wno-padded",
210                        "-Wno-unsafe-buffer-usage",
211                        "-Wno-poison-system-directories",
212                        "-Wno-switch-default", "-Wno-unknown-warning-option",
213                        "-Wno-pre-c11-compat" ] +~ cgoto_flags;
214GCC_FLAGS: []str = @[ "-Wno-clobbered" ] +~ cgoto_flags;
215
216CLANG_FLAGS_STR: str = strv2str(CLANG_FLAGS);
217
218GCC_FLAGS_STR: str = strv2str(GCC_FLAGS);
219
220// Common CFLAGS.
221CFLAGS: []str = @[ "-Wall", "-Wextra", "-Werror", "-pedantic" ];
222
223CFLAGS_STR: str = strv2str(CFLAGS);
224
225// Common debug and release flags.
226DEBUG_CFLAGS: []str = CFLAGS +~ @[ "-fno-omit-frame-pointer" ];
227DEBUG_CFLAGS_STR: str = CFLAGS_STR +~ " -fno-omit-frame-pointer";
228RELEASE_CFLAGS: []str = CFLAGS +~ @[ "-DNDEBUG" ];
229RELEASE_CFLAGS_STR: str = CFLAGS_STR +~ " -DNDEBUG";
230
231/**
232 * Print a header with a message. This is just to make it easy to track
233 * progress.
234 * @param msg  The message to print in the header.
235 */
236fn header(msg: str) -> void
237{
238	io.eprint("\n*******************\n");
239	io.eprint(msg);
240	io.eprint("\n*******************\n\n");
241}
242
243/**
244 * An easy way to call `make`.
245 * @param args  The arguments to pass to `make`, if any.
246 */
247fn do_make(args: []str) -> void
248{
249	$ make -j64 %(args);
250}
251
252/**
253 * An easy way to call Rig.
254 * @param args  The arguments to pass to Rig, if any.
255 */
256fn do_rig(args: []str) -> void
257{
258	$ rig -j64 %(args);
259}
260
261/**
262 * Runs `configure.sh`.
263 * @param cflags    The C compiler flags.
264 * @param cc        The C compiler.
265 * @param flags     The flags to pass to `configure.sh` itself.
266 * @param gen       The setting for `GEN_HOST`.
267 * @param long_bit  The number of bits in longs.
268 */
269fn configure(
270	cflags: str,
271	cc: str,
272	flags: str,
273	gen: bool,
274	long_bit: usize
275) -> void
276{
277	if !run_make
278	{
279		sys.panic("Cannot run `configure.sh` or make");
280	}
281
282	extra_flags: str =
283	if !generated
284	{
285		if !problematic { "-GP"; } else { "-G"; }
286	}
287	else if !problematic
288	{
289		"-P";
290	};
291
292	extra_cflags: str =
293	if cc == "clang"
294	{
295		// We need to quiet this warning from Clang because the configure.sh
296		// docs have this warning, so people should know. Also, I want this
297		// script to work.
298		CLANG_FLAGS_STR +~ (if !gen_host { " -Wno-overlength-strings"; });
299	}
300	else if cc == "gcc"
301	{
302		// We need to quiet this warning from GCC because the configure.sh docs
303		// have this warning, so people should know. Also, I want this script to
304		// work.
305		GCC_FLAGS_STR +~ (if !gen_host { "-Wno-overlength-strings"; });
306	};
307
308	all_flags: str = flags +~ " " +~ extra_flags;
309	all_cflags: str = cflags +~ " " +~ extra_cflags;
310
311	hdr := "Running configure.sh " +~ all_flags +~
312	       "..." +~
313	       "\n    CC=\"" +~ cc +~ "\"\n" +~
314	       "\n    CFLAGS=\"" +~ all_cflags +~ "\"\n" +~
315	       "\n    LONG_BIT=" +~ str(long_bit) +~
316	       "\n    GEN_HOST=" +~ str(gen);
317
318	// Print the header and do the job.
319	header(hdr);
320
321	env.set env.str("CFLAGS", str(cflags +~ extra_cflags)),
322	        env.str("CC", cc), env.str("GEN_HOST", str(gen)),
323	        env.str("LONG_BIT", str(long_bit))
324	{
325		$ @(path.join(REAL, "configure.sh")) $flags $extra_flags > /dev/null
326		  !> /dev/null;
327	}
328}
329
330/**
331 * Build with `make`. This function also captures and outputs any warnings if
332 * they exist because as far as I am concerned, warnings are not acceptable for
333 * release.
334 * @param cflags    The C compiler flags.
335 * @param cc        The C compiler.
336 * @param flags     The flags to pass to `configure.sh` itself.
337 * @param gen       The setting for `GEN_HOST`.
338 * @param long_bit  The number of bits in longs.
339 */
340fn build_make(
341	cflags: str,
342	cc: str,
343	flags: str,
344	gen: bool,
345	long_bit: usize
346) -> void
347{
348	if !run_make
349	{
350		sys.panic("Cannot run `configure.sh` or make");
351	}
352
353	configure(cflags, cc, flags, gen, long_bit);
354
355	hdr := "Building with `make`...\n    CC=" +~ cc +~
356	       "\n    CFLAGS=\"" +~ cflags +~
357	       "\n    LONG_BIT=" +~ str(long_bit) +~
358	       "\n    GEN_HOST=" +~ str(gen);
359
360	header(hdr);
361
362	res := $ make;
363
364	if res.stderr.len != 0
365	{
366		io.eprint(cc +~ "generated warning(s):\n\n");
367		io.eprint(str(res.stderr));
368		sys.exit(1);
369	}
370
371	if res.exitcode != 0
372	{
373		sys.exit(res.exitcode);
374	}
375}
376
377/**
378 * Build with Rig. This function also captures and outputs any warnings if they
379 * exist because as far as I am concerned, warnings are not acceptable for
380 * release.
381 * @param cflags    The C compiler flags.
382 * @param cc        The C compiler.
383 * @param flags     The flags to pass to `configure.sh` itself.
384 * @param long_bit  The number of bits in longs.
385 */
386fn build_rig(
387	cflags: []str,
388	cc: str,
389	flags: []str,
390	long_bit: usize
391) -> void
392{
393	if !run_rig
394	{
395		sys.panic("Cannot run Rig");
396	}
397
398	cflags_str: str = strv2str(cflags);
399
400	hdr := "Building with Rig...\n    CC=" +~ cc +~
401	       "\n    CFLAGS=\"" +~ cflags_str +~
402	       "\n    LONG_BIT=" +~ str(long_bit);
403
404	header(hdr);
405
406	res := $ rig;
407
408	if res.stderr.len != 0
409	{
410		io.eprint(cc +~ "generated warning(s):\n\n");
411		io.eprint(str(res.stderr));
412		sys.exit(1);
413	}
414
415	if res.exitcode != 0
416	{
417		sys.exit(res.exitcode);
418	}
419}
420
421/**
422 * Run tests with `make`.
423 * @param args  Any arguments to pass to `make`, if any.
424 */
425fn run_test_make(args: []str) -> void
426{
427	if !run_make
428	{
429		sys.panic("Cannot run `configure.sh` or make");
430	}
431
432	header("Running `make` tests");
433
434	if args.len > 0
435	{
436		do_make(args);
437	}
438	else
439	{
440		do_make(@[ "test" ]);
441
442		if history
443		{
444			do_make(@[ "test_history" ]);
445		}
446	}
447}
448
449/**
450 * Run tests with Rig.
451 * @param args  Any arguments to pass to Rig, if any.
452 */
453fn run_test_rig(args: []str) -> void
454{
455	if !run_rig
456	{
457		sys.panic("Cannot run Rig");
458	}
459
460	header("Running Rig tests");
461
462	if args.len > 0
463	{
464		do_rig(args);
465	}
466	else
467	{
468		do_rig(@[ "test" ]);
469
470		if history
471		{
472			do_rig(@[ "test_history" ]);
473		}
474	}
475}
476
477/**
478 * Builds and runs tests using `make` with both calculators, then bc only, then
479 * dc only. If `tests` is false, then it just does the builds.
480 * @param cflags    The C compiler flags.
481 * @param cc        The C compiler.
482 * @param flags     The flags to pass to `configure.sh` itself.
483 * @param gen       The setting for `GEN_HOST`.
484 * @param long_bit  The number of bits in longs.
485 */
486fn run_config_tests_make(
487	cflags: str,
488	cc: str,
489	flags: str,
490	gen: bool,
491	long_bit: usize,
492) -> void
493{
494	if !run_make
495	{
496		sys.panic("Cannot run `configure.sh` or make");
497	}
498
499	header_start: str =
500	if tests
501	{
502		"Running tests with configure flags and `make`";
503	}
504	else
505	{
506		"Building with configure flags and `make`";
507	};
508
509	header("\"" +~ header_start +~ "\" ...\n" +~
510	       "    CC=" +~ cc +~ "\n" +~
511	       "    CFLAGS=\"" +~ cflags +~ "\"\n" +~
512	       "    LONG_BIT=" +~ str(long_bit) +~ "\n" +~
513	       "    GEN_HOST=" +~ str(gen));
514
515	build_make(cflags, cc, flags, gen, long_bit);
516
517	if tests
518	{
519		run_test_make(@[]);
520	}
521
522	do_make(@[ "clean" ]);
523
524	build_make(cflags, cc, flags +~ " -b", gen, long_bit);
525
526	if tests
527	{
528		run_test_make(@[]);
529	}
530
531	do_make(@[ "clean" ]);
532
533	build_make(cflags, cc, flags +~ " -d", gen, long_bit);
534
535	if tests
536	{
537		run_test_make(@[]);
538	}
539
540	do_make(@[ "clean" ]);
541}
542
543/**
544 * Builds and runs tests using Rig with both calculators, then bc only, then dc
545 * only. If `tests` is false, then it just does the builds.
546 * @param cflags    The C compiler flags.
547 * @param cc        The C compiler.
548 * @param flags     The flags to pass to Rig itself.
549 * @param long_bit  The number of bits in longs.
550 */
551fn run_config_tests_rig(
552	cflags: []str,
553	cc: str,
554	flags: []str,
555	long_bit: usize,
556) -> void
557{
558	if !run_rig
559	{
560		sys.panic("Cannot run Rig");
561	}
562
563	header_start: str =
564	if tests
565	{
566		"Running tests with Rig";
567	}
568	else
569	{
570		"Building with Rig";
571	};
572
573	header("\"" +~ header_start +~ "\" ...\n" +~
574	       "    CC=" +~ cc +~ "\n" +~
575	       "    CFLAGS=\"" +~ strv2str(cflags) +~ "\"\n" +~
576	       "    flags=\"" +~ strv2str(flags) +~ "\"\n" +~
577	       "    LONG_BIT=" +~ str(long_bit));
578
579	build_rig(cflags, cc, flags, long_bit);
580
581	if tests
582	{
583		run_test_rig(@[]);
584	}
585
586	do_rig(@[ "clean" ]);
587
588	build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=bc" ], long_bit);
589
590	if tests
591	{
592		run_test_rig(@[]);
593	}
594
595	do_rig(@[ "clean" ]);
596
597	build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=dc" ], long_bit);
598
599	if tests
600	{
601		run_test_rig(@[]);
602	}
603
604	do_rig(@[ "clean" ]);
605}
606
607/**
608 * Builds and runs tests using `run_config_tests_make()` with both calculators,
609 * then bc only, then dc only. If `tests` is false, then it just does the
610 * builds.
611 * @param cflags  The C compiler flags.
612 * @param cc      The C compiler.
613 * @param flags   The flags to pass to `configure.sh` itself.
614 */
615fn run_config_series_make(
616	cflags: str,
617	cc: str,
618	flags: str,
619) -> void
620{
621	if !run_make
622	{
623		sys.panic("Cannot run `configure.sh` or make");
624	}
625
626	if !no64
627	{
628		if !no128
629		{
630			run_config_tests_make(cflags, cc, flags, true, usize(64));
631		}
632
633		if gen_host
634		{
635			run_config_tests_make(cflags, cc, flags, false, usize(64));
636		}
637
638		run_config_tests_make(cflags +~ " " +~ DEFOPT +~ "BC_RAND_BUILTIN=0",
639		                      cc, flags, true, usize(64));
640
641		// Test Editline if history is not turned off.
642		if editline && !(flags contains "H")
643		{
644			run_config_tests_make(cflags +~ " " +~ DEFOPT +~
645			                          "BC_RAND_BUILTIN=0",
646			                      cc, flags +~ " -e", true, usize(64));
647		}
648
649		// Test Readline if history is not turned off.
650		if readline && !(flags contains "H")
651		{
652			run_config_tests_make(cflags +~ " " +~ DEFOPT +~
653			                          "BC_RAND_BUILTIN=0",
654			                      cc, flags +~ " -r", true, usize(64));
655		}
656	}
657
658	run_config_tests_make(cflags, cc, flags, true, usize(32));
659
660	if gen_host
661	{
662		run_config_tests_make(cflags, cc, flags, false, usize(32));
663	}
664}
665
666/**
667 * Builds and runs tests using `run_config_tests_rig()` with both calculators,
668 * then bc only, then dc only. If `tests` is false, then it just does the
669 * builds.
670 * @param cflags  The C compiler flags.
671 * @param cc      The C compiler.
672 * @param flags   The flags to pass to Rig itself.
673 */
674fn run_config_series_rig(
675	cflags: []str,
676	cc: str,
677	flags: []str,
678) -> void
679{
680	if !run_rig
681	{
682		sys.panic("Cannot run Rig");
683	}
684
685	if !no64
686	{
687		if !no128
688		{
689			run_config_tests_rig(cflags, cc, flags, usize(64));
690		}
691
692		run_config_tests_rig(cflags +~ @[ DEFOPT +~ "BC_RAND_BUILTIN=0" ], cc,
693		                     flags, usize(64));
694
695		// Test Editline if history is not turned off.
696		if editline && !(flags contains "-Dhistory=none")
697		{
698			run_config_tests_rig(cflags +~ @[ DEFOPT +~ "BC_RAND_BUILTIN=0" ],
699			                     cc, flags +~ @[ "-Dhistory=editline" ],
700			                     usize(64));
701		}
702
703		// Test Readline if history is not turned off.
704		if readline && !(flags contains "-Dhistory=none")
705		{
706			run_config_tests_rig(cflags +~ @[ DEFOPT +~ "BC_RAND_BUILTIN=0" ],
707			                     cc, flags +~ @[ "-Dhistory=readline" ],
708			                     usize(64));
709		}
710	}
711
712	run_config_tests_rig(cflags, cc, flags, usize(32));
713}
714
715/**
716 * Builds and runs tests with each setting combo running
717 * `run_config_series_make()`. If `tests` is false, it just does the builds.
718 * @param cflags  The C compiler flags.
719 * @param cc      The C compiler.
720 * @param flags   The flags to pass to `configure.sh` itself.
721 */
722fn run_settings_series_make(
723	cflags: str,
724	cc: str,
725	flags: str,
726) -> void
727{
728	if !run_make
729	{
730		sys.panic("Cannot run `configure.sh` or make");
731	}
732
733	if settings
734	{
735		settings_path: str = path.join(SCRIPTDIR, "release_settings_make.txt");
736		settings_txt: str = io.read_file(settings_path);
737		lines: []str = settings_txt.split("\n");
738
739		for line: lines
740		{
741			run_config_series_make(cflags, cc, flags +~ " " +~ line);
742		}
743	}
744	else
745	{
746		run_config_series_make(cflags, cc, flags);
747	}
748}
749
750/**
751 * Builds and runs tests with each setting combo running
752 * `run_config_series_rig()`. If `tests` is false, it just does the builds.
753 * @param cflags  The C compiler flags.
754 * @param cc      The C compiler.
755 * @param flags   The flags to pass to Rig itself.
756 */
757fn run_settings_series_rig(
758	cflags: []str,
759	cc: str,
760	flags: []str,
761) -> void
762{
763	if !run_rig
764	{
765		sys.panic("Cannot run Rig");
766	}
767
768	if settings
769	{
770		settings_path: str = path.join(SCRIPTDIR, "release_settings_rig.txt");
771		settings_txt: str = io.read_file(settings_path);
772		lines: []str = settings_txt.split("\n");
773
774		for line: lines
775		{
776			opts_list: []str =
777			for opt: line.split(" ")
778			{
779				DEFOPT +~ opt;
780			};
781
782			run_config_series_rig(cflags, cc, flags +~ opts_list);
783		}
784	}
785	else
786	{
787		run_config_series_rig(cflags, cc, flags);
788	}
789}
790
791/**
792 * Builds and runs tests with each build type running
793 * `run_settings_series_make()`. If `tests` is false, it just does the builds.
794 * @param cflags  The C compiler flags.
795 * @param cc      The C compiler.
796 * @param flags   The flags to pass to `configure.sh` itself.
797 */
798fn run_test_series_make(
799	cflags: str,
800	cc: str,
801	flags: str,
802) -> void
803{
804	if !run_make
805	{
806		sys.panic("Cannot run `configure.sh` or make");
807	}
808
809	series_flags: []str = @[ "E", "H", "N", "EH", "EN", "HN", "EHN" ];
810
811	run_settings_series_make(cflags, cc, flags);
812
813	for sflag: series_flags
814	{
815		run_settings_series_make(cflags, cc, flags +~ " -" +~ sflag);
816	}
817}
818
819/**
820 * Builds and runs tests with each build type running
821 * `run_settings_series_rig()`. If `tests` is false, it just does the builds.
822 * @param cflags  The C compiler flags.
823 * @param cc      The C compiler.
824 * @param flags   The flags to pass to Rig itself.
825 */
826fn run_test_series_rig(
827	cflags: []str,
828	cc: str,
829	flags: []str,
830) -> void
831{
832	if !run_rig
833	{
834		sys.panic("Cannot run Rig");
835	}
836
837	series_path: str = path.join(SCRIPTDIR, "release_flags_rig.txt");
838	series_txt: str = io.read_file(series_path);
839	series_flags: []str = series_txt.split("\n");
840
841	run_settings_series_rig(cflags, cc, flags);
842
843	for line: series_flags
844	{
845		opts_list: []str =
846		for opt: line.split(" ")
847		{
848			DEFOPT +~ opt;
849		};
850
851		run_settings_series_rig(cflags, cc, flags +~ opts_list);
852	}
853}
854
855/**
856 * Builds and runs tests for `bcl` with `make`. If `tests` is false, it just
857 * does the builds.
858 * @param cflags  The C compiler flags.
859 * @param cc      The C compiler.
860 * @param flags   The flags to pass to `configure.sh` itself.
861 */
862fn run_lib_tests_make(
863	cflags: str,
864	cc: str,
865	flags: str,
866) -> void
867{
868	if !run_make
869	{
870		sys.panic("Cannot run `configure.sh` or make");
871	}
872
873	build_make(cflags +~ " -a", cc, flags, true, usize(64));
874
875	if tests
876	{
877		run_test_make(@[ "test" ]);
878	}
879
880	build_make(cflags +~ " -a", cc, flags, true, usize(32));
881
882	if tests
883	{
884		run_test_make(@[ "test" ]);
885	}
886}
887
888/**
889 * Builds and runs tests for `bcl` with Rig. If `tests` is false, it just does
890 * the builds.
891 * @param cflags  The C compiler flags.
892 * @param cc      The C compiler.
893 * @param flags   The flags to pass to Rig itself.
894 */
895fn run_lib_tests_rig(
896	cflags: []str,
897	cc: str,
898	flags: []str,
899) -> void
900{
901	if !run_rig
902	{
903		sys.panic("Cannot run Rig");
904	}
905
906	build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=library" ], usize(64));
907
908	if tests
909	{
910		run_test_rig(@[ "test" ]);
911	}
912
913	build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=library" ], usize(32));
914
915	if tests
916	{
917		run_test_rig(@[ "test" ]);
918	}
919}
920
921/**
922 * Builds and runs tests under C99, then C11, if requested, using
923 * `run_test_series_make()`. If run_tests is false, it just does the builds.
924 * @param cflags  The C compiler flags.
925 * @param cc      The C compiler.
926 * @param flags   The flags to pass to Rig itself.
927 */
928fn run_tests_make(
929	cflags: str,
930	cc: str,
931	flags: str,
932) -> void
933{
934	if !run_make
935	{
936		sys.panic("Cannot run `configure.sh` or make");
937	}
938
939	run_test_series_make(cflags +~ " " +~ C99OPT, cc, flags);
940
941	if c11
942	{
943		run_test_series_make(cflags +~ " " +~ C11OPT, cc, flags);
944	}
945}
946
947/**
948 * Builds and runs tests under C99, then C11, if requested, using
949 * `run_test_series_rig()`. If run_tests is false, it just does the builds.
950 * @param cflags  The C compiler flags.
951 * @param cc      The C compiler.
952 * @param flags   The flags to pass to Rig itself.
953 */
954fn run_tests_rig(
955	cflags: []str,
956	cc: str,
957	flags: []str,
958) -> void
959{
960	if !run_rig
961	{
962		sys.panic("Cannot run Rig");
963	}
964
965	run_test_series_rig(cflags +~ @[ C99OPT ], cc, flags);
966
967	if c11
968	{
969		run_test_series_rig(cflags +~ @[ C11OPT ], cc, flags);
970	}
971}
972
973/**
974 * Runs the Karatsuba tests with `make`.
975 */
976fn karatsuba_make() -> void
977{
978	if !run_make
979	{
980		sys.panic("Cannot run `configure.sh` or make");
981	}
982
983	header("Running Karatsuba tests");
984
985	do_make(@[ "karatsuba_test" ]);
986}
987
988/**
989 * Runs the Karatsuba tests with Rig.
990 */
991fn karatsuba_rig() -> void
992{
993	if !run_rig
994	{
995		sys.panic("Cannot run Rig");
996	}
997
998	header("Running Karatsuba tests");
999
1000	build_rig(RELEASE_CFLAGS, defcc, @[ "-O3" ], usize(64));
1001
1002	do_rig(@[ "karatsuba_test" ]);
1003}
1004
1005/**
1006 * Builds with `make` and runs under valgrind. It runs both, bc only, then dc
1007 * only, then the library.
1008 */
1009fn vg_make() -> void
1010{
1011	if !run_make
1012	{
1013		sys.panic("Cannot run `configure.sh` or make");
1014	}
1015
1016	header("Running Valgrind under `make`");
1017
1018	build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gv", true,
1019	           defbits);
1020	run_test_make(@[ "test" ]);
1021
1022	do_make(@[ "clean_config" ]);
1023
1024	build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gvb", true,
1025	           defbits);
1026	run_test_make(@[ "test" ]);
1027
1028	do_make(@[ "clean_config" ]);
1029
1030	build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gvd", true,
1031	           defbits);
1032	run_test_make(@[ "test" ]);
1033
1034	do_make(@[ "clean_config" ]);
1035
1036	build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gva", true,
1037	           defbits);
1038	run_test_make(@[ "test" ]);
1039
1040	do_make(@[ "clean_config" ]);
1041}
1042
1043fn vg_rig() -> void
1044{
1045	if !run_rig
1046	{
1047		sys.panic("Cannot run Rig");
1048	}
1049
1050	header("Running Valgrind under Rig");
1051
1052	build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gv" ],
1053	          defbits);
1054	run_test_rig(@[ "test" ]);
1055
1056	do_rig(@[ "clean_config" ]);
1057
1058	build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gvb" ],
1059	          defbits);
1060	run_test_rig(@[ "test" ]);
1061
1062	do_rig(@[ "clean_config" ]);
1063
1064	build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gvd" ],
1065	          defbits);
1066	run_test_rig(@[ "test" ]);
1067
1068	do_rig(@[ "clean_config" ]);
1069
1070	build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gva" ],
1071	          defbits);
1072	run_test_rig(@[ "test" ]);
1073
1074	do_rig(@[ "clean_config" ]);
1075}
1076
1077/**
1078 * Builds the debug series with `make` and runs the tests if `tests` allows.
1079 * @param cc  The C compiler.
1080 */
1081fn debug_make(
1082	cc: str,
1083) -> void
1084{
1085	if !run_make
1086	{
1087		sys.panic("Cannot run `configure.sh` or make");
1088	}
1089
1090	if cc == "clang" && sanitizers
1091	{
1092		run_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=undefined", cc, "-mg");
1093	}
1094	else
1095	{
1096		run_tests_make(DEBUG_CFLAGS_STR, cc, "-g");
1097	}
1098}
1099
1100/**
1101 * Builds the release series with `make` and runs the tests if `tests` allows.
1102 * @param cc  The C compiler.
1103 */
1104fn release_make(
1105	cc: str,
1106) -> void
1107{
1108	if !run_make
1109	{
1110		sys.panic("Cannot run `configure.sh` or make");
1111	}
1112
1113	run_tests_make(RELEASE_CFLAGS_STR, cc, "-O3");
1114	run_lib_tests_make(RELEASE_CFLAGS_STR, cc, "-O3");
1115}
1116
1117/**
1118 * Builds the release debug series with `make` and runs the tests if `tests`
1119 * allows.
1120 * @param cc  The C compiler.
1121 */
1122fn reldebug_make(
1123	cc: str,
1124) -> void
1125{
1126	if !run_make
1127	{
1128		sys.panic("Cannot run `configure.sh` or make");
1129	}
1130
1131	if cc == "clang" && sanitizers
1132	{
1133		run_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=address", cc, "-mgO3");
1134		run_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=memory", cc, "-mgO3");
1135	}
1136	else
1137	{
1138		run_tests_make(DEBUG_CFLAGS_STR, cc, "-gO3");
1139	}
1140
1141	if cc == "clang" && sanitizers
1142	{
1143		run_lib_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=address", cc,
1144		                   "-mgO3");
1145		run_lib_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=memory", cc,
1146		                   "-mgO3");
1147	}
1148	else
1149	{
1150		run_lib_tests_make(DEBUG_CFLAGS_STR, cc, "-gO3");
1151	}
1152}
1153
1154/**
1155 * Builds the min size release with `make` and runs the tests if `tests` allows.
1156 * @param cc  The C compiler.
1157 */
1158fn minsize_make(
1159	cc: str,
1160) -> void
1161{
1162	if !run_make
1163	{
1164		sys.panic("Cannot run `configure.sh` or make");
1165	}
1166
1167	run_tests_make(RELEASE_CFLAGS_STR, cc, "-Os");
1168	run_lib_tests_make(RELEASE_CFLAGS_STR, cc, "-Os");
1169}
1170
1171/**
1172 * Builds all sets with `make`: debug, release, release debug, and min size, and
1173 * runs the tests if `tests` allows.
1174 * @param cc  The C compiler.
1175 */
1176fn build_set_make(
1177	cc: str,
1178) -> void
1179{
1180	if !run_make
1181	{
1182		sys.panic("Cannot run `configure.sh` or make");
1183	}
1184
1185	debug_make(cc);
1186	release_make(cc);
1187	reldebug_make(cc);
1188	minsize_make(cc);
1189
1190	if karatsuba
1191	{
1192		karatsuba_make();
1193	}
1194
1195	if valgrind
1196	{
1197		vg_make();
1198	}
1199}
1200
1201/**
1202 * Builds the debug series with Rig and runs the tests if `tests` allows.
1203 * @param cc  The C compiler.
1204 */
1205fn debug_rig(
1206	cc: str,
1207) -> void
1208{
1209	if !run_rig
1210	{
1211		sys.panic("Cannot run Rig");
1212	}
1213
1214	if cc == "clang" && sanitizers
1215	{
1216		run_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=undefined" ], cc,
1217		              @[ "-Dmemcheck=1", "-Ddebug=1" ]);
1218	}
1219	else
1220	{
1221		run_tests_rig(DEBUG_CFLAGS, cc, @[ "-Ddebug=1" ]);
1222	}
1223}
1224
1225/**
1226 * Builds the release series with Rig and runs the tests if `tests` allows.
1227 * @param cc  The C compiler.
1228 */
1229fn release_rig(
1230	cc: str,
1231) -> void
1232{
1233	if !run_rig
1234	{
1235		sys.panic("Cannot run Rig");
1236	}
1237
1238	run_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=3" ]);
1239	run_lib_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=3" ]);
1240}
1241
1242/**
1243 * Builds the release debug series with Rig and runs the tests if `tests`
1244 * allows.
1245 * @param cc  The C compiler.
1246 */
1247fn reldebug_rig(
1248	cc: str,
1249) -> void
1250{
1251	if !run_rig
1252	{
1253		sys.panic("Cannot run Rig");
1254	}
1255
1256	if cc == "clang" && sanitizers
1257	{
1258		run_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=address" ], cc,
1259		              @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
1260		run_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=memory" ], cc,
1261		              @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
1262	}
1263	else
1264	{
1265		run_tests_rig(DEBUG_CFLAGS, cc, @[ "-gO3" ]);
1266	}
1267
1268	if cc == "clang" && sanitizers
1269	{
1270		run_lib_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=address" ], cc,
1271		                  @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
1272		run_lib_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=memory" ], cc,
1273		                  @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
1274	}
1275	else
1276	{
1277		run_lib_tests_rig(DEBUG_CFLAGS, cc,
1278		                  @[ "-Ddebug=1", "-Doptimization=3" ]);
1279	}
1280}
1281
1282/**
1283 * Builds the min size release with Rig and runs the tests if `tests` allows.
1284 * @param cc  The C compiler.
1285 */
1286fn minsize_rig(
1287	cc: str,
1288) -> void
1289{
1290	if !run_rig
1291	{
1292		sys.panic("Cannot run Rig");
1293	}
1294
1295	run_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=s" ]);
1296	run_lib_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=s" ]);
1297}
1298
1299/**
1300 * Builds all sets with Rig: debug, release, release debug, and min size, and
1301 * runs the tests if `tests` allows.
1302 * @param cc  The C compiler.
1303 */
1304fn build_set_rig(
1305	cc: str,
1306) -> void
1307{
1308	if !run_rig
1309	{
1310		sys.panic("Cannot run Rig");
1311	}
1312
1313	debug_rig(cc);
1314	release_rig(cc);
1315	reldebug_rig(cc);
1316	minsize_rig(cc);
1317
1318	if karatsuba
1319	{
1320		karatsuba_rig();
1321	}
1322
1323	if valgrind
1324	{
1325		vg_rig();
1326	}
1327}
1328
1329/**
1330 * Builds all sets with `make` under all compilers.
1331 */
1332fn build_sets_make() -> void
1333{
1334	header("Running math library under --standard with Rig");
1335
1336	build_make(DEBUG_CFLAGS_STR +~ " -std=c99", defcc, "-g", true,
1337	           defbits);
1338
1339	$ bin/bc -ls << @("quit\n");
1340
1341	do_rig(@[ "clean_tests" ]);
1342
1343	if clang
1344	{
1345		build_set_make("clang");
1346	}
1347
1348	if gcc
1349	{
1350		build_set_make("gcc");
1351	}
1352}
1353
1354/**
1355 * Builds all sets with Rig under all compilers.
1356 */
1357fn build_sets_rig() -> void
1358{
1359	header("Running math library under --standard with Rig");
1360
1361	build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], defcc, @[ "-Ddebug=1" ],
1362	          defbits);
1363
1364	$ build/bc -ls << @("quit\n");
1365
1366	do_rig(@[ "clean_tests" ]);
1367
1368	if clang
1369	{
1370		build_set_rig("clang");
1371	}
1372
1373	if gcc
1374	{
1375		build_set_rig("gcc");
1376	}
1377}
1378
1379fn build_sets() -> void
1380{
1381	if !run_make
1382	{
1383		build_sets_rig();
1384	}
1385	else if !run_rig
1386	{
1387		build_sets_make();
1388	}
1389	else
1390	{
1391		parallel.map @[ "rig", "make" ]:
1392		(builder: str) -> void
1393		{
1394			if builder == "rig"
1395			{
1396				build_sets_rig();
1397			}
1398			else if builder == "make"
1399			{
1400				build_sets_make();
1401			}
1402			else
1403			{
1404				sys.panic("Bad builder: " +~ builder +~ "\n");
1405			}
1406		}
1407	}
1408
1409	io.eprint("\nTests successful.\n");
1410}
1411