authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-31 23:15:59-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-31 23:15:59-05:00
logefa25e7d5bca63e83f6a653058c05dacc771d19e
tree56b34421822584702b1647ab0eb38aec160386b4
parent6f13a725a3249c7f0a0f5258ac00003cd132bf15
parent8d37c6f71c790faecdb6acdd2868823be2bd2496
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14498 from ziglang/zig-build-api

Several enhancements to the build system. Many breaking changes to the API. * combine `std.build` and `std.build.Builder` into `std.Build` * eliminate `setTarget` and `setBuildMode`; use an options struct for `b.addExecutable` and friends * implement passing options to dependency packages. closes #14285 * rename `LibExeObjStep` to `CompileStep` * move src.type.CType to std lib, use it from std.Build, this helps with populating config.h files.

121 files changed, 8599 insertions(+), 8329 deletions(-)

build.zig+55-204
......@@ -1,19 +1,18 @@
11const std = @import("std");
22const builtin = std.builtin;
3const Builder = std.build.Builder;
43const tests = @import("test/tests.zig");
54const BufMap = std.BufMap;
65const mem = std.mem;
76const ArrayList = std.ArrayList;
87const io = std.io;
98const fs = std.fs;
10const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
9const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
1110const assert = std.debug.assert;
1211
1312const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 };
1413const stack_size = 32 * 1024 * 1024;
1514
16pub fn build(b: *Builder) !void {
15pub fn build(b: *std.Build) !void {
1716 const release = b.option(bool, "release", "Build in release mode") orelse false;
1817 const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
1918 const target = t: {
......@@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void {
2322 }
2423 break :t b.standardTargetOptions(.{ .default_target = default_target });
2524 };
26 const mode: std.builtin.Mode = if (release) switch (target.getCpuArch()) {
25 const optimize: std.builtin.OptimizeMode = if (release) switch (target.getCpuArch()) {
2726 .wasm32 => .ReleaseSmall,
2827 else => .ReleaseFast,
2928 } else .Debug;
......@@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void {
3332
3433 const test_step = b.step("test", "Run all the tests");
3534
36 const docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
35 const docgen_exe = b.addExecutable(.{
36 .name = "docgen",
37 .root_source_file = .{ .path = "doc/docgen.zig" },
38 .target = .{},
39 .optimize = .Debug,
40 });
3741 docgen_exe.single_threaded = single_threaded;
3842
3943 const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);
......@@ -53,10 +57,12 @@ pub fn build(b: *Builder) !void {
5357 const docs_step = b.step("docs", "Build documentation");
5458 docs_step.dependOn(&docgen_cmd.step);
5559
56 const test_cases = b.addTest("src/test.zig");
60 const test_cases = b.addTest(.{
61 .root_source_file = .{ .path = "src/test.zig" },
62 .optimize = optimize,
63 });
5764 test_cases.main_pkg_path = ".";
5865 test_cases.stack_size = stack_size;
59 test_cases.setBuildMode(mode);
6066 test_cases.single_threaded = single_threaded;
6167
6268 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
......@@ -149,17 +155,15 @@ pub fn build(b: *Builder) !void {
149155
150156 const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {
151157 if (strip == true) break :blk @as(u32, 0);
152 if (mode != .Debug) break :blk 0;
158 if (optimize != .Debug) break :blk 0;
153159 break :blk 4;
154160 };
155161
156 const exe = addCompilerStep(b);
162 const exe = addCompilerStep(b, optimize, target);
157163 exe.strip = strip;
158164 exe.sanitize_thread = sanitize_thread;
159165 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
160166 exe.install();
161 exe.setBuildMode(mode);
162 exe.setTarget(target);
163167
164168 const compile_step = b.step("compile", "Build the self-hosted compiler");
165169 compile_step.dependOn(&exe.step);
......@@ -195,7 +199,7 @@ pub fn build(b: *Builder) !void {
195199 test_cases.linkLibC();
196200 }
197201
198 const is_debug = mode == .Debug;
202 const is_debug = optimize == .Debug;
199203 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
200204 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
201205
......@@ -360,25 +364,25 @@ pub fn build(b: *Builder) !void {
360364 test_step.dependOn(test_cases_step);
361365 }
362366
363 var chosen_modes: [4]builtin.Mode = undefined;
367 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
364368 var chosen_mode_index: usize = 0;
365369 if (!skip_debug) {
366 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
370 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug;
367371 chosen_mode_index += 1;
368372 }
369373 if (!skip_release_safe) {
370 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
374 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe;
371375 chosen_mode_index += 1;
372376 }
373377 if (!skip_release_fast) {
374 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast;
378 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast;
375379 chosen_mode_index += 1;
376380 }
377381 if (!skip_release_small) {
378 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall;
382 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall;
379383 chosen_mode_index += 1;
380384 }
381 const modes = chosen_modes[0..chosen_mode_index];
385 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
382386
383387 // run stage1 `zig fmt` on this build.zig file just to make sure it works
384388 test_step.dependOn(&fmt_build_zig.step);
......@@ -391,7 +395,7 @@ pub fn build(b: *Builder) !void {
391395 "test/behavior.zig",
392396 "behavior",
393397 "Run the behavior tests",
394 modes,
398 optimization_modes,
395399 skip_single_threaded,
396400 skip_non_native,
397401 skip_libc,
......@@ -405,7 +409,7 @@ pub fn build(b: *Builder) !void {
405409 "lib/compiler_rt.zig",
406410 "compiler-rt",
407411 "Run the compiler_rt tests",
408 modes,
412 optimization_modes,
409413 true, // skip_single_threaded
410414 skip_non_native,
411415 true, // skip_libc
......@@ -419,7 +423,7 @@ pub fn build(b: *Builder) !void {
419423 "lib/c.zig",
420424 "universal-libc",
421425 "Run the universal libc tests",
422 modes,
426 optimization_modes,
423427 true, // skip_single_threaded
424428 skip_non_native,
425429 true, // skip_libc
......@@ -427,11 +431,11 @@ pub fn build(b: *Builder) !void {
427431 skip_stage2_tests or true, // TODO get these all passing
428432 ));
429433
430 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
434 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
431435 test_step.dependOn(tests.addStandaloneTests(
432436 b,
433437 test_filter,
434 modes,
438 optimization_modes,
435439 skip_non_native,
436440 enable_macos_sdk,
437441 target,
......@@ -444,10 +448,10 @@ pub fn build(b: *Builder) !void {
444448 enable_symlinks_windows,
445449 ));
446450 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
447 test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
448 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
449 test_step.dependOn(tests.addCliTests(b, test_filter, modes));
450 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
451 test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
452 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
453 test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
454 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
451455 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
452456 if (!skip_run_translated_c) {
453457 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
......@@ -461,7 +465,7 @@ pub fn build(b: *Builder) !void {
461465 "lib/std/std.zig",
462466 "std",
463467 "Run the standard library tests",
464 modes,
468 optimization_modes,
465469 skip_single_threaded,
466470 skip_non_native,
467471 skip_libc,
......@@ -472,7 +476,7 @@ pub fn build(b: *Builder) !void {
472476 try addWasiUpdateStep(b, version);
473477}
474478
475fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
479fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
476480 const semver = try std.SemanticVersion.parse(version);
477481
478482 var target: std.zig.CrossTarget = .{
......@@ -481,9 +485,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
481485 };
482486 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));
483487
484 const exe = addCompilerStep(b);
485 exe.setBuildMode(.ReleaseSmall);
486 exe.setTarget(target);
488 const exe = addCompilerStep(b, .ReleaseSmall, target);
487489
488490 const exe_options = b.addOptions();
489491 exe.addOptions("build_options", exe_options);
......@@ -510,8 +512,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
510512 update_zig1_step.dependOn(&run_opt.step);
511513}
512514
513fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep {
514 const exe = b.addExecutable("zig", "src/main.zig");
515fn addCompilerStep(
516 b: *std.Build,
517 optimize: std.builtin.OptimizeMode,
518 target: std.zig.CrossTarget,
519) *std.Build.CompileStep {
520 const exe = b.addExecutable(.{
521 .name = "zig",
522 .root_source_file = .{ .path = "src/main.zig" },
523 .target = target,
524 .optimize = optimize,
525 });
515526 exe.stack_size = stack_size;
516527 return exe;
517528}
......@@ -531,9 +542,9 @@ const exe_cflags = [_][]const u8{
531542};
532543
533544fn addCmakeCfgOptionsToExe(
534 b: *Builder,
545 b: *std.Build,
535546 cfg: CMakeConfig,
536 exe: *std.build.LibExeObjStep,
547 exe: *std.Build.CompileStep,
537548 use_zig_libcxx: bool,
538549) !void {
539550 if (exe.target.isDarwin()) {
......@@ -612,7 +623,7 @@ fn addCmakeCfgOptionsToExe(
612623 }
613624}
614625
615fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
626fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void {
616627 // Adds the Zig C++ sources which both stage1 and stage2 need.
617628 //
618629 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
......@@ -649,9 +660,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
649660}
650661
651662fn addCxxKnownPath(
652 b: *Builder,
663 b: *std.Build,
653664 ctx: CMakeConfig,
654 exe: *std.build.LibExeObjStep,
665 exe: *std.Build.CompileStep,
655666 objname: []const u8,
656667 errtxt: ?[]const u8,
657668 need_cpp_includes: bool,
......@@ -684,7 +695,7 @@ fn addCxxKnownPath(
684695 }
685696}
686697
687fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
698fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void {
688699 var it = mem.tokenize(u8, list, ";");
689700 while (it.next()) |lib| {
690701 if (mem.startsWith(u8, lib, "-l")) {
......@@ -698,7 +709,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
698709}
699710
700711const CMakeConfig = struct {
701 llvm_linkage: std.build.LibExeObjStep.Linkage,
712 llvm_linkage: std.Build.CompileStep.Linkage,
702713 cmake_binary_dir: []const u8,
703714 cmake_prefix_path: []const u8,
704715 cmake_static_library_prefix: []const u8,
......@@ -715,7 +726,7 @@ const CMakeConfig = struct {
715726
716727const max_config_h_bytes = 1 * 1024 * 1024;
717728
718fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
729fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
719730 if (config_h_path_option) |path| {
720731 var config_h_or_err = fs.cwd().openFile(path, .{});
721732 if (config_h_or_err) |*file| {
......@@ -761,7 +772,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
761772 } else unreachable; // TODO should not need `else unreachable`.
762773}
763774
764fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
775fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
765776 var ctx: CMakeConfig = .{
766777 .llvm_linkage = undefined,
767778 .cmake_binary_dir = undefined,
......@@ -850,7 +861,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
850861 return ctx;
851862}
852863
853fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
864fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
854865 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
855866 for (duplicated) |*byte| switch (byte.*) {
856867 '/' => byte.* = fs.path.sep,
......@@ -859,166 +870,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
859870 return duplicated;
860871}
861872
862const softfloat_sources = [_][]const u8{
863 "deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c",
864 "deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c",
865 "deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c",
866 "deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c",
867 "deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c",
868 "deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c",
869 "deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c",
870 "deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c",
871 "deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c",
872 "deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c",
873 "deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c",
874 "deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c",
875 "deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c",
876 "deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c",
877 "deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c",
878 "deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c",
879 "deps/SoftFloat-3e/source/f128M_add.c",
880 "deps/SoftFloat-3e/source/f128M_div.c",
881 "deps/SoftFloat-3e/source/f128M_eq.c",
882 "deps/SoftFloat-3e/source/f128M_eq_signaling.c",
883 "deps/SoftFloat-3e/source/f128M_le.c",
884 "deps/SoftFloat-3e/source/f128M_le_quiet.c",
885 "deps/SoftFloat-3e/source/f128M_lt.c",
886 "deps/SoftFloat-3e/source/f128M_lt_quiet.c",
887 "deps/SoftFloat-3e/source/f128M_mul.c",
888 "deps/SoftFloat-3e/source/f128M_mulAdd.c",
889 "deps/SoftFloat-3e/source/f128M_rem.c",
890 "deps/SoftFloat-3e/source/f128M_roundToInt.c",
891 "deps/SoftFloat-3e/source/f128M_sqrt.c",
892 "deps/SoftFloat-3e/source/f128M_sub.c",
893 "deps/SoftFloat-3e/source/f128M_to_f16.c",
894 "deps/SoftFloat-3e/source/f128M_to_f32.c",
895 "deps/SoftFloat-3e/source/f128M_to_f64.c",
896 "deps/SoftFloat-3e/source/f128M_to_extF80M.c",
897 "deps/SoftFloat-3e/source/f128M_to_i32.c",
898 "deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c",
899 "deps/SoftFloat-3e/source/f128M_to_i64.c",
900 "deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c",
901 "deps/SoftFloat-3e/source/f128M_to_ui32.c",
902 "deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c",
903 "deps/SoftFloat-3e/source/f128M_to_ui64.c",
904 "deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c",
905 "deps/SoftFloat-3e/source/extF80M_add.c",
906 "deps/SoftFloat-3e/source/extF80M_div.c",
907 "deps/SoftFloat-3e/source/extF80M_eq.c",
908 "deps/SoftFloat-3e/source/extF80M_le.c",
909 "deps/SoftFloat-3e/source/extF80M_lt.c",
910 "deps/SoftFloat-3e/source/extF80M_mul.c",
911 "deps/SoftFloat-3e/source/extF80M_rem.c",
912 "deps/SoftFloat-3e/source/extF80M_roundToInt.c",
913 "deps/SoftFloat-3e/source/extF80M_sqrt.c",
914 "deps/SoftFloat-3e/source/extF80M_sub.c",
915 "deps/SoftFloat-3e/source/extF80M_to_f16.c",
916 "deps/SoftFloat-3e/source/extF80M_to_f32.c",
917 "deps/SoftFloat-3e/source/extF80M_to_f64.c",
918 "deps/SoftFloat-3e/source/extF80M_to_f128M.c",
919 "deps/SoftFloat-3e/source/f16_add.c",
920 "deps/SoftFloat-3e/source/f16_div.c",
921 "deps/SoftFloat-3e/source/f16_eq.c",
922 "deps/SoftFloat-3e/source/f16_isSignalingNaN.c",
923 "deps/SoftFloat-3e/source/f16_lt.c",
924 "deps/SoftFloat-3e/source/f16_mul.c",
925 "deps/SoftFloat-3e/source/f16_mulAdd.c",
926 "deps/SoftFloat-3e/source/f16_rem.c",
927 "deps/SoftFloat-3e/source/f16_roundToInt.c",
928 "deps/SoftFloat-3e/source/f16_sqrt.c",
929 "deps/SoftFloat-3e/source/f16_sub.c",
930 "deps/SoftFloat-3e/source/f16_to_extF80M.c",
931 "deps/SoftFloat-3e/source/f16_to_f128M.c",
932 "deps/SoftFloat-3e/source/f16_to_f64.c",
933 "deps/SoftFloat-3e/source/f32_to_extF80M.c",
934 "deps/SoftFloat-3e/source/f32_to_f128M.c",
935 "deps/SoftFloat-3e/source/f64_to_extF80M.c",
936 "deps/SoftFloat-3e/source/f64_to_f128M.c",
937 "deps/SoftFloat-3e/source/f64_to_f16.c",
938 "deps/SoftFloat-3e/source/i32_to_f128M.c",
939 "deps/SoftFloat-3e/source/s_add256M.c",
940 "deps/SoftFloat-3e/source/s_addCarryM.c",
941 "deps/SoftFloat-3e/source/s_addComplCarryM.c",
942 "deps/SoftFloat-3e/source/s_addF128M.c",
943 "deps/SoftFloat-3e/source/s_addExtF80M.c",
944 "deps/SoftFloat-3e/source/s_addM.c",
945 "deps/SoftFloat-3e/source/s_addMagsF16.c",
946 "deps/SoftFloat-3e/source/s_addMagsF32.c",
947 "deps/SoftFloat-3e/source/s_addMagsF64.c",
948 "deps/SoftFloat-3e/source/s_approxRecip32_1.c",
949 "deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c",
950 "deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c",
951 "deps/SoftFloat-3e/source/s_approxRecip_1Ks.c",
952 "deps/SoftFloat-3e/source/s_compare128M.c",
953 "deps/SoftFloat-3e/source/s_compare96M.c",
954 "deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c",
955 "deps/SoftFloat-3e/source/s_countLeadingZeros16.c",
956 "deps/SoftFloat-3e/source/s_countLeadingZeros32.c",
957 "deps/SoftFloat-3e/source/s_countLeadingZeros64.c",
958 "deps/SoftFloat-3e/source/s_countLeadingZeros8.c",
959 "deps/SoftFloat-3e/source/s_eq128.c",
960 "deps/SoftFloat-3e/source/s_invalidF128M.c",
961 "deps/SoftFloat-3e/source/s_invalidExtF80M.c",
962 "deps/SoftFloat-3e/source/s_isNaNF128M.c",
963 "deps/SoftFloat-3e/source/s_le128.c",
964 "deps/SoftFloat-3e/source/s_lt128.c",
965 "deps/SoftFloat-3e/source/s_mul128MTo256M.c",
966 "deps/SoftFloat-3e/source/s_mul64To128M.c",
967 "deps/SoftFloat-3e/source/s_mulAddF128M.c",
968 "deps/SoftFloat-3e/source/s_mulAddF16.c",
969 "deps/SoftFloat-3e/source/s_mulAddF32.c",
970 "deps/SoftFloat-3e/source/s_mulAddF64.c",
971 "deps/SoftFloat-3e/source/s_negXM.c",
972 "deps/SoftFloat-3e/source/s_normExtF80SigM.c",
973 "deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c",
974 "deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c",
975 "deps/SoftFloat-3e/source/s_normRoundPackToF16.c",
976 "deps/SoftFloat-3e/source/s_normRoundPackToF32.c",
977 "deps/SoftFloat-3e/source/s_normRoundPackToF64.c",
978 "deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c",
979 "deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c",
980 "deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c",
981 "deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c",
982 "deps/SoftFloat-3e/source/s_remStepMBy32.c",
983 "deps/SoftFloat-3e/source/s_roundMToI64.c",
984 "deps/SoftFloat-3e/source/s_roundMToUI64.c",
985 "deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c",
986 "deps/SoftFloat-3e/source/s_roundPackMToF128M.c",
987 "deps/SoftFloat-3e/source/s_roundPackToF16.c",
988 "deps/SoftFloat-3e/source/s_roundPackToF32.c",
989 "deps/SoftFloat-3e/source/s_roundPackToF64.c",
990 "deps/SoftFloat-3e/source/s_roundToI32.c",
991 "deps/SoftFloat-3e/source/s_roundToI64.c",
992 "deps/SoftFloat-3e/source/s_roundToUI32.c",
993 "deps/SoftFloat-3e/source/s_roundToUI64.c",
994 "deps/SoftFloat-3e/source/s_shiftLeftM.c",
995 "deps/SoftFloat-3e/source/s_shiftNormSigF128M.c",
996 "deps/SoftFloat-3e/source/s_shiftRightJam256M.c",
997 "deps/SoftFloat-3e/source/s_shiftRightJam32.c",
998 "deps/SoftFloat-3e/source/s_shiftRightJam64.c",
999 "deps/SoftFloat-3e/source/s_shiftRightJamM.c",
1000 "deps/SoftFloat-3e/source/s_shiftRightM.c",
1001 "deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c",
1002 "deps/SoftFloat-3e/source/s_shortShiftLeftM.c",
1003 "deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c",
1004 "deps/SoftFloat-3e/source/s_shortShiftRightJam64.c",
1005 "deps/SoftFloat-3e/source/s_shortShiftRightJamM.c",
1006 "deps/SoftFloat-3e/source/s_shortShiftRightM.c",
1007 "deps/SoftFloat-3e/source/s_sub1XM.c",
1008 "deps/SoftFloat-3e/source/s_sub256M.c",
1009 "deps/SoftFloat-3e/source/s_subM.c",
1010 "deps/SoftFloat-3e/source/s_subMagsF16.c",
1011 "deps/SoftFloat-3e/source/s_subMagsF32.c",
1012 "deps/SoftFloat-3e/source/s_subMagsF64.c",
1013 "deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c",
1014 "deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c",
1015 "deps/SoftFloat-3e/source/softfloat_state.c",
1016 "deps/SoftFloat-3e/source/ui32_to_f128M.c",
1017 "deps/SoftFloat-3e/source/ui64_to_f128M.c",
1018 "deps/SoftFloat-3e/source/ui32_to_extF80M.c",
1019 "deps/SoftFloat-3e/source/ui64_to_extF80M.c",
1020};
1021
1022873const zig_cpp_sources = [_][]const u8{
1023874 // These are planned to stay even when we are self-hosted.
1024875 "src/zig_llvm.cpp",
doc/langref.html.in+51-28
......@@ -9528,11 +9528,15 @@ fn foo(comptime T: type, ptr: *T) T {
95289528 To add standard build options to a <code class="file">build.zig</code> file:
95299529 </p>
95309530 {#code_begin|syntax|build#}
9531const Builder = @import("std").build.Builder;
9531const std = @import("std");
95329532
9533pub fn build(b: *Builder) void {
9534 const exe = b.addExecutable("example", "example.zig");
9535 exe.setBuildMode(b.standardReleaseOptions());
9533pub fn build(b: *std.Build) void {
9534 const optimize = b.standardOptimizeOption(.{});
9535 const exe = b.addExecutable(.{
9536 .name = "example",
9537 .root_source_file = .{ .path = "example.zig" },
9538 .optimize = optimize,
9539 });
95369540 b.default_step.dependOn(&exe.step);
95379541}
95389542 {#code_end#}
......@@ -10547,22 +10551,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
1054710551 <p>This <code class="file">build.zig</code> file is automatically generated
1054810552 by <kbd>zig init-exe</kbd>.</p>
1054910553 {#code_begin|syntax|build_executable#}
10550const Builder = @import("std").build.Builder;
10554const std = @import("std");
1055110555
10552pub fn build(b: *Builder) void {
10556pub fn build(b: *std.Build) void {
1055310557 // Standard target options allows the person running `zig build` to choose
1055410558 // what target to build for. Here we do not override the defaults, which
1055510559 // means any target is allowed, and the default is native. Other options
1055610560 // for restricting supported target set are available.
1055710561 const target = b.standardTargetOptions(.{});
1055810562
10559 // Standard release options allow the person running `zig build` to select
10560 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
10561 const mode = b.standardReleaseOptions();
10563 // Standard optimization options allow the person running `zig build` to select
10564 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
10565 // set a preferred release mode, allowing the user to decide how to optimize.
10566 const optimize = b.standardOptimizeOption(.{});
1056210567
10563 const exe = b.addExecutable("example", "src/main.zig");
10564 exe.setTarget(target);
10565 exe.setBuildMode(mode);
10568 const exe = b.addExecutable(.{
10569 .name = "example",
10570 .root_source_file = .{ .path = "src/main.zig" },
10571 .target = target,
10572 .optimize = optimize,
10573 });
1056610574 exe.install();
1056710575
1056810576 const run_cmd = exe.run();
......@@ -10581,16 +10589,21 @@ pub fn build(b: *Builder) void {
1058110589 <p>This <code class="file">build.zig</code> file is automatically generated
1058210590 by <kbd>zig init-lib</kbd>.</p>
1058310591 {#code_begin|syntax|build_library#}
10584const Builder = @import("std").build.Builder;
10592const std = @import("std");
1058510593
10586pub fn build(b: *Builder) void {
10587 const mode = b.standardReleaseOptions();
10588 const lib = b.addStaticLibrary("example", "src/main.zig");
10589 lib.setBuildMode(mode);
10594pub fn build(b: *std.Build) void {
10595 const optimize = b.standardOptimizeOption(.{});
10596 const lib = b.addStaticLibrary(.{
10597 .name = "example",
10598 .root_source_file = .{ .path = "src/main.zig" },
10599 .optimize = optimize,
10600 });
1059010601 lib.install();
1059110602
10592 var main_tests = b.addTest("src/main.zig");
10593 main_tests.setBuildMode(mode);
10603 const main_tests = b.addTest(.{
10604 .root_source_file = .{ .path = "src/main.zig" },
10605 .optimize = optimize,
10606 });
1059410607
1059510608 const test_step = b.step("test", "Run library tests");
1059610609 test_step.dependOn(&main_tests.step);
......@@ -10949,12 +10962,17 @@ int main(int argc, char **argv) {
1094910962}
1095010963 {#end_syntax_block#}
1095110964 {#code_begin|syntax|build_c#}
10952const Builder = @import("std").build.Builder;
10953
10954pub fn build(b: *Builder) void {
10955 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
10965const std = @import("std");
1095610966
10957 const exe = b.addExecutable("test", null);
10967pub fn build(b: *std.Build) void {
10968 const lib = b.addSharedLibrary(.{
10969 .name = "mathtest",
10970 .root_source_file = .{ .path = "mathtest.zig" },
10971 .version = .{ .major = 1, .minor = 0, .patch = 0 },
10972 });
10973 const exe = b.addExecutable(.{
10974 .name = "test",
10975 });
1095810976 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1095910977 exe.linkLibrary(lib);
1096010978 exe.linkSystemLibrary("c");
......@@ -11011,12 +11029,17 @@ int main(int argc, char **argv) {
1101111029}
1101211030 {#end_syntax_block#}
1101311031 {#code_begin|syntax|build_object#}
11014const Builder = @import("std").build.Builder;
11032const std = @import("std");
1101511033
11016pub fn build(b: *Builder) void {
11017 const obj = b.addObject("base64", "base64.zig");
11034pub fn build(b: *std.Build) void {
11035 const obj = b.addObject(.{
11036 .name = "base64",
11037 .root_source_file = .{ .path = "base64.zig" },
11038 });
1101811039
11019 const exe = b.addExecutable("test", null);
11040 const exe = b.addExecutable(.{
11041 .name = "test",
11042 });
1102011043 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1102111044 exe.addObject(obj);
1102211045 exe.linkSystemLibrary("c");
lib/build_runner.zig+7-5
......@@ -3,7 +3,6 @@ const std = @import("std");
33const builtin = @import("builtin");
44const io = std.io;
55const fmt = std.fmt;
6const Builder = std.build.Builder;
76const mem = std.mem;
87const process = std.process;
98const ArrayList = std.ArrayList;
......@@ -42,12 +41,15 @@ pub fn main() !void {
4241 return error.InvalidArgs;
4342 };
4443
45 const builder = try Builder.create(
44 const host = try std.zig.system.NativeTargetInfo.detect(.{});
45
46 const builder = try std.Build.create(
4647 allocator,
4748 zig_exe,
4849 build_root,
4950 cache_root,
5051 global_cache_root,
52 host,
5153 );
5254 defer builder.destroy();
5355
......@@ -58,7 +60,7 @@ pub fn main() !void {
5860 const stdout_stream = io.getStdOut().writer();
5961
6062 var install_prefix: ?[]const u8 = null;
61 var dir_list = Builder.DirList{};
63 var dir_list = std.Build.DirList{};
6264
6365 // before arg parsing, check for the NO_COLOR environment variable
6466 // if it exists, default the color setting to .off
......@@ -230,7 +232,7 @@ pub fn main() !void {
230232 };
231233}
232234
233fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
235fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
234236 // run the build script to collect the options
235237 if (!already_ran_build) {
236238 builder.resolveInstallPrefix(null, .{});
......@@ -330,7 +332,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
330332 );
331333}
332334
333fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void {
335fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void {
334336 usage(builder, already_ran_build, out_stream) catch {};
335337 process.exit(1);
336338}
lib/init-exe/build.zig+43-10
......@@ -1,34 +1,67 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
3// Although this function looks imperative, note that its job is to
4// declaratively construct a build graph that will be executed by an external
5// runner.
6pub fn build(b: *std.Build) void {
47 // Standard target options allows the person running `zig build` to choose
58 // what target to build for. Here we do not override the defaults, which
69 // means any target is allowed, and the default is native. Other options
710 // for restricting supported target set are available.
811 const target = b.standardTargetOptions(.{});
912
10 // Standard release options allow the person running `zig build` to select
11 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
12 const mode = b.standardReleaseOptions();
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
1317
14 const exe = b.addExecutable("$", "src/main.zig");
15 exe.setTarget(target);
16 exe.setBuildMode(mode);
18 const exe = b.addExecutable(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the executable to be installed into the
28 // standard location when the user invokes the "install" step (the default
29 // step when running `zig build`).
1730 exe.install();
1831
32 // This *creates* a RunStep in the build graph, to be executed when another
33 // step is evaluated that depends on it. The next line below will establish
34 // such a dependency.
1935 const run_cmd = exe.run();
36
37 // By making the run step depend on the install step, it will be run from the
38 // installation directory rather than directly from within the cache directory.
39 // This is not necessary, however, if the application depends on other installed
40 // files, this ensures they will be present and in the expected location.
2041 run_cmd.step.dependOn(b.getInstallStep());
42
43 // This allows the user to pass arguments to the application in the build
44 // command itself, like this: `zig build run -- arg1 arg2 etc`
2145 if (b.args) |args| {
2246 run_cmd.addArgs(args);
2347 }
2448
49 // This creates a build step. It will be visible in the `zig build --help` menu,
50 // and can be selected like this: `zig build run`
51 // This will evaluate the `run` step rather than the default, which is "install".
2552 const run_step = b.step("run", "Run the app");
2653 run_step.dependOn(&run_cmd.step);
2754
28 const exe_tests = b.addTest("src/main.zig");
29 exe_tests.setTarget(target);
30 exe_tests.setBuildMode(mode);
55 // Creates a step for unit testing.
56 const exe_tests = b.addTest(.{
57 .root_source_file = .{ .path = "src/main.zig" },
58 .target = target,
59 .optimize = optimize,
60 });
3161
62 // Similar to creating the run step earlier, this exposes a `test` step to
63 // the `zig build --help` menu, providing a way for the user to request
64 // running the unit tests.
3265 const test_step = b.step("test", "Run unit tests");
3366 test_step.dependOn(&exe_tests.step);
3467}
lib/init-lib/build.zig+35-8
......@@ -1,17 +1,44 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 // Standard release options allow the person running `zig build` to select
5 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
6 const mode = b.standardReleaseOptions();
3// Although this function looks imperative, note that its job is to
4// declaratively construct a build graph that will be executed by an external
5// runner.
6pub fn build(b: *std.Build) void {
7 // Standard target options allows the person running `zig build` to choose
8 // what target to build for. Here we do not override the defaults, which
9 // means any target is allowed, and the default is native. Other options
10 // for restricting supported target set are available.
11 const target = b.standardTargetOptions(.{});
712
8 const lib = b.addStaticLibrary("$", "src/main.zig");
9 lib.setBuildMode(mode);
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
17
18 const lib = b.addStaticLibrary(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the library to be installed into the standard
28 // location when the user invokes the "install" step (the default step when
29 // running `zig build`).
1030 lib.install();
1131
12 const main_tests = b.addTest("src/main.zig");
13 main_tests.setBuildMode(mode);
32 // Creates a step for unit testing.
33 const main_tests = b.addTest(.{
34 .root_source_file = .{ .path = "src/main.zig" },
35 .target = target,
36 .optimize = optimize,
37 });
1438
39 // This creates a build step. It will be visible in the `zig build --help` menu,
40 // and can be selected like this: `zig build test`
41 // This will evaluate the `test` step rather than the default, which is "install".
1542 const test_step = b.step("test", "Run library tests");
1643 test_step.dependOn(&main_tests.step);
1744}
lib/std/Build.zig created+1780
......@@ -0,0 +1,1780 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const Build = @This();
21
22/// deprecated: use `CompileStep`.
23pub const LibExeObjStep = CompileStep;
24/// deprecated: use `Build`.
25pub const Builder = Build;
26/// deprecated: use `InstallDirStep.Options`
27pub const InstallDirectoryOptions = InstallDirStep.Options;
28
29pub const Step = @import("Build/Step.zig");
30pub const CheckFileStep = @import("Build/CheckFileStep.zig");
31pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
32pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
33pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
34pub const FmtStep = @import("Build/FmtStep.zig");
35pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
36pub const InstallDirStep = @import("Build/InstallDirStep.zig");
37pub const InstallFileStep = @import("Build/InstallFileStep.zig");
38pub const InstallRawStep = @import("Build/InstallRawStep.zig");
39pub const CompileStep = @import("Build/CompileStep.zig");
40pub const LogStep = @import("Build/LogStep.zig");
41pub const OptionsStep = @import("Build/OptionsStep.zig");
42pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
43pub const RunStep = @import("Build/RunStep.zig");
44pub const TranslateCStep = @import("Build/TranslateCStep.zig");
45pub const WriteFileStep = @import("Build/WriteFileStep.zig");
46
47install_tls: TopLevelStep,
48uninstall_tls: TopLevelStep,
49allocator: Allocator,
50user_input_options: UserInputOptionsMap,
51available_options_map: AvailableOptionsMap,
52available_options_list: ArrayList(AvailableOption),
53verbose: bool,
54verbose_link: bool,
55verbose_cc: bool,
56verbose_air: bool,
57verbose_llvm_ir: bool,
58verbose_cimport: bool,
59verbose_llvm_cpu_features: bool,
60/// The purpose of executing the command is for a human to read compile errors from the terminal
61prominent_compile_errors: bool,
62color: enum { auto, on, off } = .auto,
63reference_trace: ?u32 = null,
64invalid_user_input: bool,
65zig_exe: []const u8,
66default_step: *Step,
67env_map: *EnvMap,
68top_level_steps: ArrayList(*TopLevelStep),
69install_prefix: []const u8,
70dest_dir: ?[]const u8,
71lib_dir: []const u8,
72exe_dir: []const u8,
73h_dir: []const u8,
74install_path: []const u8,
75sysroot: ?[]const u8 = null,
76search_prefixes: ArrayList([]const u8),
77libc_file: ?[]const u8 = null,
78installed_files: ArrayList(InstalledFile),
79/// Path to the directory containing build.zig.
80build_root: []const u8,
81cache_root: []const u8,
82global_cache_root: []const u8,
83/// zig lib dir
84override_lib_dir: ?[]const u8,
85vcpkg_root: VcpkgRoot = .unattempted,
86pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
87args: ?[][]const u8 = null,
88debug_log_scopes: []const []const u8 = &.{},
89debug_compile_errors: bool = false,
90
91/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
92enable_darling: bool = false,
93/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
94enable_qemu: bool = false,
95/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
96enable_rosetta: bool = false,
97/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
98enable_wasmtime: bool = false,
99/// Use system Wine installation to run cross compiled Windows build artifacts.
100enable_wine: bool = false,
101/// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
102/// this will be the directory $glibc-build-dir/install/glibcs
103/// Given the example of the aarch64 target, this is the directory
104/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
105glibc_runtimes_dir: ?[]const u8 = null,
106
107/// Information about the native target. Computed before build() is invoked.
108host: NativeTargetInfo,
109
110dep_prefix: []const u8 = "",
111
112pub const ExecError = error{
113 ReadFailure,
114 ExitCodeFailure,
115 ProcessTerminated,
116 ExecNotSupported,
117} || std.ChildProcess.SpawnError;
118
119pub const PkgConfigError = error{
120 PkgConfigCrashed,
121 PkgConfigFailed,
122 PkgConfigNotInstalled,
123 PkgConfigInvalidOutput,
124};
125
126pub const PkgConfigPkg = struct {
127 name: []const u8,
128 desc: []const u8,
129};
130
131pub const CStd = enum {
132 C89,
133 C99,
134 C11,
135};
136
137const UserInputOptionsMap = StringHashMap(UserInputOption);
138const AvailableOptionsMap = StringHashMap(AvailableOption);
139
140const AvailableOption = struct {
141 name: []const u8,
142 type_id: TypeId,
143 description: []const u8,
144 /// If the `type_id` is `enum` this provides the list of enum options
145 enum_options: ?[]const []const u8,
146};
147
148const UserInputOption = struct {
149 name: []const u8,
150 value: UserValue,
151 used: bool,
152};
153
154const UserValue = union(enum) {
155 flag: void,
156 scalar: []const u8,
157 list: ArrayList([]const u8),
158 map: StringHashMap(*const UserValue),
159};
160
161const TypeId = enum {
162 bool,
163 int,
164 float,
165 @"enum",
166 string,
167 list,
168};
169
170const TopLevelStep = struct {
171 pub const base_id = .top_level;
172
173 step: Step,
174 description: []const u8,
175};
176
177pub const DirList = struct {
178 lib_dir: ?[]const u8 = null,
179 exe_dir: ?[]const u8 = null,
180 include_dir: ?[]const u8 = null,
181};
182
183pub fn create(
184 allocator: Allocator,
185 zig_exe: []const u8,
186 build_root: []const u8,
187 cache_root: []const u8,
188 global_cache_root: []const u8,
189 host: NativeTargetInfo,
190) !*Build {
191 const env_map = try allocator.create(EnvMap);
192 env_map.* = try process.getEnvMap(allocator);
193
194 const self = try allocator.create(Build);
195 self.* = Build{
196 .zig_exe = zig_exe,
197 .build_root = build_root,
198 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
199 .global_cache_root = global_cache_root,
200 .verbose = false,
201 .verbose_link = false,
202 .verbose_cc = false,
203 .verbose_air = false,
204 .verbose_llvm_ir = false,
205 .verbose_cimport = false,
206 .verbose_llvm_cpu_features = false,
207 .prominent_compile_errors = false,
208 .invalid_user_input = false,
209 .allocator = allocator,
210 .user_input_options = UserInputOptionsMap.init(allocator),
211 .available_options_map = AvailableOptionsMap.init(allocator),
212 .available_options_list = ArrayList(AvailableOption).init(allocator),
213 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
214 .default_step = undefined,
215 .env_map = env_map,
216 .search_prefixes = ArrayList([]const u8).init(allocator),
217 .install_prefix = undefined,
218 .lib_dir = undefined,
219 .exe_dir = undefined,
220 .h_dir = undefined,
221 .dest_dir = env_map.get("DESTDIR"),
222 .installed_files = ArrayList(InstalledFile).init(allocator),
223 .install_tls = TopLevelStep{
224 .step = Step.initNoOp(.top_level, "install", allocator),
225 .description = "Copy build artifacts to prefix path",
226 },
227 .uninstall_tls = TopLevelStep{
228 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
229 .description = "Remove build artifacts from prefix path",
230 },
231 .override_lib_dir = null,
232 .install_path = undefined,
233 .args = null,
234 .host = host,
235 };
236 try self.top_level_steps.append(&self.install_tls);
237 try self.top_level_steps.append(&self.uninstall_tls);
238 self.default_step = &self.install_tls.step;
239 return self;
240}
241
242fn createChild(
243 parent: *Build,
244 dep_name: []const u8,
245 build_root: []const u8,
246 args: anytype,
247) !*Build {
248 const child = try createChildOnly(parent, dep_name, build_root);
249 try applyArgs(child, args);
250 return child;
251}
252
253fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
254 const allocator = parent.allocator;
255 const child = try allocator.create(Build);
256 child.* = .{
257 .allocator = allocator,
258 .install_tls = .{
259 .step = Step.initNoOp(.top_level, "install", allocator),
260 .description = "Copy build artifacts to prefix path",
261 },
262 .uninstall_tls = .{
263 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
264 .description = "Remove build artifacts from prefix path",
265 },
266 .user_input_options = UserInputOptionsMap.init(allocator),
267 .available_options_map = AvailableOptionsMap.init(allocator),
268 .available_options_list = ArrayList(AvailableOption).init(allocator),
269 .verbose = parent.verbose,
270 .verbose_link = parent.verbose_link,
271 .verbose_cc = parent.verbose_cc,
272 .verbose_air = parent.verbose_air,
273 .verbose_llvm_ir = parent.verbose_llvm_ir,
274 .verbose_cimport = parent.verbose_cimport,
275 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
276 .prominent_compile_errors = parent.prominent_compile_errors,
277 .color = parent.color,
278 .reference_trace = parent.reference_trace,
279 .invalid_user_input = false,
280 .zig_exe = parent.zig_exe,
281 .default_step = undefined,
282 .env_map = parent.env_map,
283 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
284 .install_prefix = undefined,
285 .dest_dir = parent.dest_dir,
286 .lib_dir = parent.lib_dir,
287 .exe_dir = parent.exe_dir,
288 .h_dir = parent.h_dir,
289 .install_path = parent.install_path,
290 .sysroot = parent.sysroot,
291 .search_prefixes = ArrayList([]const u8).init(allocator),
292 .libc_file = parent.libc_file,
293 .installed_files = ArrayList(InstalledFile).init(allocator),
294 .build_root = build_root,
295 .cache_root = parent.cache_root,
296 .global_cache_root = parent.global_cache_root,
297 .override_lib_dir = parent.override_lib_dir,
298 .debug_log_scopes = parent.debug_log_scopes,
299 .debug_compile_errors = parent.debug_compile_errors,
300 .enable_darling = parent.enable_darling,
301 .enable_qemu = parent.enable_qemu,
302 .enable_rosetta = parent.enable_rosetta,
303 .enable_wasmtime = parent.enable_wasmtime,
304 .enable_wine = parent.enable_wine,
305 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
306 .host = parent.host,
307 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
308 };
309 try child.top_level_steps.append(&child.install_tls);
310 try child.top_level_steps.append(&child.uninstall_tls);
311 child.default_step = &child.install_tls.step;
312 return child;
313}
314
315fn applyArgs(b: *Build, args: anytype) !void {
316 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
317 const v = @field(args, field.name);
318 const T = @TypeOf(v);
319 switch (T) {
320 CrossTarget => {
321 try b.user_input_options.put(field.name, .{
322 .name = field.name,
323 .value = .{ .scalar = try v.zigTriple(b.allocator) },
324 .used = false,
325 });
326 try b.user_input_options.put("cpu", .{
327 .name = "cpu",
328 .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
329 .used = false,
330 });
331 },
332 []const u8 => {
333 try b.user_input_options.put(field.name, .{
334 .name = field.name,
335 .value = .{ .scalar = v },
336 .used = false,
337 });
338 },
339 else => switch (@typeInfo(T)) {
340 .Bool => {
341 try b.user_input_options.put(field.name, .{
342 .name = field.name,
343 .value = .{ .scalar = if (v) "true" else "false" },
344 .used = false,
345 });
346 },
347 .Enum => {
348 try b.user_input_options.put(field.name, .{
349 .name = field.name,
350 .value = .{ .scalar = @tagName(v) },
351 .used = false,
352 });
353 },
354 .Int => {
355 try b.user_input_options.put(field.name, .{
356 .name = field.name,
357 .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
358 .used = false,
359 });
360 },
361 else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
362 },
363 }
364 }
365 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
366 // Random bytes to make unique. Refresh this with new random bytes when
367 // implementation is modified in a non-backwards-compatible way.
368 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
369 hash.update(b.dep_prefix);
370 // TODO additionally update the hash with `args`.
371
372 var digest: [16]u8 = undefined;
373 hash.final(&digest);
374 var hash_basename: [digest.len * 2]u8 = undefined;
375 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
376 unreachable;
377
378 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
379 b.resolveInstallPrefix(install_prefix, .{});
380}
381
382pub fn destroy(self: *Build) void {
383 self.env_map.deinit();
384 self.top_level_steps.deinit();
385 self.allocator.destroy(self);
386}
387
388/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
389pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
390 if (self.dest_dir) |dest_dir| {
391 self.install_prefix = install_prefix orelse "/usr";
392 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
393 } else {
394 self.install_prefix = install_prefix orelse
395 (self.pathJoin(&.{ self.build_root, "zig-out" }));
396 self.install_path = self.install_prefix;
397 }
398
399 var lib_list = [_][]const u8{ self.install_path, "lib" };
400 var exe_list = [_][]const u8{ self.install_path, "bin" };
401 var h_list = [_][]const u8{ self.install_path, "include" };
402
403 if (dir_list.lib_dir) |dir| {
404 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
405 lib_list[1] = dir;
406 }
407
408 if (dir_list.exe_dir) |dir| {
409 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
410 exe_list[1] = dir;
411 }
412
413 if (dir_list.include_dir) |dir| {
414 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
415 h_list[1] = dir;
416 }
417
418 self.lib_dir = self.pathJoin(&lib_list);
419 self.exe_dir = self.pathJoin(&exe_list);
420 self.h_dir = self.pathJoin(&h_list);
421}
422
423pub fn addOptions(self: *Build) *OptionsStep {
424 return OptionsStep.create(self);
425}
426
427pub const ExecutableOptions = struct {
428 name: []const u8,
429 root_source_file: ?FileSource = null,
430 version: ?std.builtin.Version = null,
431 target: CrossTarget = .{},
432 optimize: std.builtin.Mode = .Debug,
433 linkage: ?CompileStep.Linkage = null,
434};
435
436pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
437 return CompileStep.create(b, .{
438 .name = options.name,
439 .root_source_file = options.root_source_file,
440 .version = options.version,
441 .target = options.target,
442 .optimize = options.optimize,
443 .kind = .exe,
444 .linkage = options.linkage,
445 });
446}
447
448pub const ObjectOptions = struct {
449 name: []const u8,
450 root_source_file: ?FileSource = null,
451 target: CrossTarget,
452 optimize: std.builtin.Mode,
453};
454
455pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
456 return CompileStep.create(b, .{
457 .name = options.name,
458 .root_source_file = options.root_source_file,
459 .target = options.target,
460 .optimize = options.optimize,
461 .kind = .obj,
462 });
463}
464
465pub const SharedLibraryOptions = struct {
466 name: []const u8,
467 root_source_file: ?FileSource = null,
468 version: ?std.builtin.Version = null,
469 target: CrossTarget,
470 optimize: std.builtin.Mode,
471};
472
473pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
474 return CompileStep.create(b, .{
475 .name = options.name,
476 .root_source_file = options.root_source_file,
477 .kind = .lib,
478 .linkage = .dynamic,
479 .version = options.version,
480 .target = options.target,
481 .optimize = options.optimize,
482 });
483}
484
485pub const StaticLibraryOptions = struct {
486 name: []const u8,
487 root_source_file: ?FileSource = null,
488 target: CrossTarget,
489 optimize: std.builtin.Mode,
490 version: ?std.builtin.Version = null,
491};
492
493pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
494 return CompileStep.create(b, .{
495 .name = options.name,
496 .root_source_file = options.root_source_file,
497 .kind = .lib,
498 .linkage = .static,
499 .version = options.version,
500 .target = options.target,
501 .optimize = options.optimize,
502 });
503}
504
505pub const TestOptions = struct {
506 name: []const u8 = "test",
507 kind: CompileStep.Kind = .@"test",
508 root_source_file: FileSource,
509 target: CrossTarget = .{},
510 optimize: std.builtin.Mode = .Debug,
511 version: ?std.builtin.Version = null,
512};
513
514pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
515 return CompileStep.create(b, .{
516 .name = options.name,
517 .kind = options.kind,
518 .root_source_file = options.root_source_file,
519 .target = options.target,
520 .optimize = options.optimize,
521 });
522}
523
524pub const AssemblyOptions = struct {
525 name: []const u8,
526 source_file: FileSource,
527 target: CrossTarget,
528 optimize: std.builtin.Mode,
529};
530
531pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
532 const obj_step = CompileStep.create(b, .{
533 .name = options.name,
534 .root_source_file = null,
535 .target = options.target,
536 .optimize = options.optimize,
537 });
538 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
539 return obj_step;
540}
541
542/// Initializes a RunStep with argv, which must at least have the path to the
543/// executable. More command line arguments can be added with `addArg`,
544/// `addArgs`, and `addArtifactArg`.
545/// Be careful using this function, as it introduces a system dependency.
546/// To run an executable built with zig build, see `CompileStep.run`.
547pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
548 assert(argv.len >= 1);
549 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
550 run_step.addArgs(argv);
551 return run_step;
552}
553
554pub fn addConfigHeader(
555 b: *Build,
556 source: FileSource,
557 style: ConfigHeaderStep.Style,
558 values: anytype,
559) *ConfigHeaderStep {
560 const config_header_step = ConfigHeaderStep.create(b, source, style);
561 config_header_step.addValues(values);
562 return config_header_step;
563}
564
565/// Allocator.dupe without the need to handle out of memory.
566pub fn dupe(self: *Build, bytes: []const u8) []u8 {
567 return self.allocator.dupe(u8, bytes) catch @panic("OOM");
568}
569
570/// Duplicates an array of strings without the need to handle out of memory.
571pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 {
572 const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM");
573 for (strings) |s, i| {
574 array[i] = self.dupe(s);
575 }
576 return array;
577}
578
579/// Duplicates a path and converts all slashes to the OS's canonical path separator.
580pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
581 const the_copy = self.dupe(bytes);
582 for (the_copy) |*byte| {
583 switch (byte.*) {
584 '/', '\\' => byte.* = fs.path.sep,
585 else => {},
586 }
587 }
588 return the_copy;
589}
590
591/// Duplicates a package recursively.
592pub fn dupePkg(self: *Build, package: Pkg) Pkg {
593 var the_copy = Pkg{
594 .name = self.dupe(package.name),
595 .source = package.source.dupe(self),
596 };
597
598 if (package.dependencies) |dependencies| {
599 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch @panic("OOM");
600 the_copy.dependencies = new_dependencies;
601
602 for (dependencies) |dep_package, i| {
603 new_dependencies[i] = self.dupePkg(dep_package);
604 }
605 }
606 return the_copy;
607}
608
609pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
610 const write_file_step = self.addWriteFiles();
611 write_file_step.add(file_path, data);
612 return write_file_step;
613}
614
615pub fn addWriteFiles(self: *Build) *WriteFileStep {
616 const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");
617 write_file_step.* = WriteFileStep.init(self);
618 return write_file_step;
619}
620
621pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
622 const data = self.fmt(format, args);
623 const log_step = self.allocator.create(LogStep) catch @panic("OOM");
624 log_step.* = LogStep.init(self, data);
625 return log_step;
626}
627
628pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
629 const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");
630 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
631 return remove_dir_step;
632}
633
634pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
635 return FmtStep.create(self, paths);
636}
637
638pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
639 return TranslateCStep.create(self, options);
640}
641
642pub fn make(self: *Build, step_names: []const []const u8) !void {
643 try self.makePath(self.cache_root);
644
645 var wanted_steps = ArrayList(*Step).init(self.allocator);
646 defer wanted_steps.deinit();
647
648 if (step_names.len == 0) {
649 try wanted_steps.append(self.default_step);
650 } else {
651 for (step_names) |step_name| {
652 const s = try self.getTopLevelStepByName(step_name);
653 try wanted_steps.append(s);
654 }
655 }
656
657 for (wanted_steps.items) |s| {
658 try self.makeOneStep(s);
659 }
660}
661
662pub fn getInstallStep(self: *Build) *Step {
663 return &self.install_tls.step;
664}
665
666pub fn getUninstallStep(self: *Build) *Step {
667 return &self.uninstall_tls.step;
668}
669
670fn makeUninstall(uninstall_step: *Step) anyerror!void {
671 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
672 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
673
674 for (self.installed_files.items) |installed_file| {
675 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
676 if (self.verbose) {
677 log.info("rm {s}", .{full_path});
678 }
679 fs.cwd().deleteTree(full_path) catch {};
680 }
681
682 // TODO remove empty directories
683}
684
685fn makeOneStep(self: *Build, s: *Step) anyerror!void {
686 if (s.loop_flag) {
687 log.err("Dependency loop detected:\n {s}", .{s.name});
688 return error.DependencyLoopDetected;
689 }
690 s.loop_flag = true;
691
692 for (s.dependencies.items) |dep| {
693 self.makeOneStep(dep) catch |err| {
694 if (err == error.DependencyLoopDetected) {
695 log.err(" {s}", .{s.name});
696 }
697 return err;
698 };
699 }
700
701 s.loop_flag = false;
702
703 try s.make();
704}
705
706fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
707 for (self.top_level_steps.items) |top_level_step| {
708 if (mem.eql(u8, top_level_step.step.name, name)) {
709 return &top_level_step.step;
710 }
711 }
712 log.err("Cannot run step '{s}' because it does not exist", .{name});
713 return error.InvalidStepName;
714}
715
716pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
717 const name = self.dupe(name_raw);
718 const description = self.dupe(description_raw);
719 const type_id = comptime typeToEnum(T);
720 const enum_options = if (type_id == .@"enum") blk: {
721 const fields = comptime std.meta.fields(T);
722 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM");
723
724 inline for (fields) |field| {
725 options.appendAssumeCapacity(field.name);
726 }
727
728 break :blk options.toOwnedSlice() catch @panic("OOM");
729 } else null;
730 const available_option = AvailableOption{
731 .name = name,
732 .type_id = type_id,
733 .description = description,
734 .enum_options = enum_options,
735 };
736 if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) {
737 panic("Option '{s}' declared twice", .{name});
738 }
739 self.available_options_list.append(available_option) catch @panic("OOM");
740
741 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
742 option_ptr.used = true;
743 switch (type_id) {
744 .bool => switch (option_ptr.value) {
745 .flag => return true,
746 .scalar => |s| {
747 if (mem.eql(u8, s, "true")) {
748 return true;
749 } else if (mem.eql(u8, s, "false")) {
750 return false;
751 } else {
752 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
753 self.markInvalidUserInput();
754 return null;
755 }
756 },
757 .list, .map => {
758 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
759 name, @tagName(option_ptr.value),
760 });
761 self.markInvalidUserInput();
762 return null;
763 },
764 },
765 .int => switch (option_ptr.value) {
766 .flag, .list, .map => {
767 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
768 name, @tagName(option_ptr.value),
769 });
770 self.markInvalidUserInput();
771 return null;
772 },
773 .scalar => |s| {
774 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
775 error.Overflow => {
776 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
777 self.markInvalidUserInput();
778 return null;
779 },
780 else => {
781 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
782 self.markInvalidUserInput();
783 return null;
784 },
785 };
786 return n;
787 },
788 },
789 .float => switch (option_ptr.value) {
790 .flag, .map, .list => {
791 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
792 name, @tagName(option_ptr.value),
793 });
794 self.markInvalidUserInput();
795 return null;
796 },
797 .scalar => |s| {
798 const n = std.fmt.parseFloat(T, s) catch {
799 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
800 self.markInvalidUserInput();
801 return null;
802 };
803 return n;
804 },
805 },
806 .@"enum" => switch (option_ptr.value) {
807 .flag, .map, .list => {
808 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
809 name, @tagName(option_ptr.value),
810 });
811 self.markInvalidUserInput();
812 return null;
813 },
814 .scalar => |s| {
815 if (std.meta.stringToEnum(T, s)) |enum_lit| {
816 return enum_lit;
817 } else {
818 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
819 self.markInvalidUserInput();
820 return null;
821 }
822 },
823 },
824 .string => switch (option_ptr.value) {
825 .flag, .list, .map => {
826 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
827 name, @tagName(option_ptr.value),
828 });
829 self.markInvalidUserInput();
830 return null;
831 },
832 .scalar => |s| return s,
833 },
834 .list => switch (option_ptr.value) {
835 .flag, .map => {
836 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
837 name, @tagName(option_ptr.value),
838 });
839 self.markInvalidUserInput();
840 return null;
841 },
842 .scalar => |s| {
843 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM");
844 },
845 .list => |lst| return lst.items,
846 },
847 }
848}
849
850pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
851 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
852 step_info.* = TopLevelStep{
853 .step = Step.initNoOp(.top_level, name, self.allocator),
854 .description = self.dupe(description),
855 };
856 self.top_level_steps.append(step_info) catch @panic("OOM");
857 return &step_info.step;
858}
859
860pub const StandardOptimizeOptionOptions = struct {
861 preferred_optimize_mode: ?std.builtin.Mode = null,
862};
863
864pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode {
865 if (options.preferred_optimize_mode) |mode| {
866 if (self.option(bool, "release", "optimize for end users") orelse false) {
867 return mode;
868 } else {
869 return .Debug;
870 }
871 } else {
872 return self.option(
873 std.builtin.Mode,
874 "optimize",
875 "prioritize performance, safety, or binary size (-O flag)",
876 ) orelse .Debug;
877 }
878}
879
880pub const StandardTargetOptionsArgs = struct {
881 whitelist: ?[]const CrossTarget = null,
882
883 default_target: CrossTarget = CrossTarget{},
884};
885
886/// Exposes standard `zig build` options for choosing a target.
887pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
888 const maybe_triple = self.option(
889 []const u8,
890 "target",
891 "The CPU architecture, OS, and ABI to build for",
892 );
893 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
894
895 if (maybe_triple == null and mcpu == null) {
896 return args.default_target;
897 }
898
899 const triple = maybe_triple orelse "native";
900
901 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
902 const selected_target = CrossTarget.parse(.{
903 .arch_os_abi = triple,
904 .cpu_features = mcpu,
905 .diagnostics = &diags,
906 }) catch |err| switch (err) {
907 error.UnknownCpuModel => {
908 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
909 diags.cpu_name.?,
910 @tagName(diags.arch.?),
911 });
912 for (diags.arch.?.allCpuModels()) |cpu| {
913 log.err(" {s}", .{cpu.name});
914 }
915 self.markInvalidUserInput();
916 return args.default_target;
917 },
918 error.UnknownCpuFeature => {
919 log.err(
920 \\Unknown CPU feature: '{s}'
921 \\Available CPU features for architecture '{s}':
922 \\
923 , .{
924 diags.unknown_feature_name.?,
925 @tagName(diags.arch.?),
926 });
927 for (diags.arch.?.allFeaturesList()) |feature| {
928 log.err(" {s}: {s}", .{ feature.name, feature.description });
929 }
930 self.markInvalidUserInput();
931 return args.default_target;
932 },
933 error.UnknownOperatingSystem => {
934 log.err(
935 \\Unknown OS: '{s}'
936 \\Available operating systems:
937 \\
938 , .{diags.os_name.?});
939 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
940 log.err(" {s}", .{field.name});
941 }
942 self.markInvalidUserInput();
943 return args.default_target;
944 },
945 else => |e| {
946 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
947 self.markInvalidUserInput();
948 return args.default_target;
949 },
950 };
951
952 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM");
953
954 if (args.whitelist) |list| whitelist_check: {
955 // Make sure it's a match of one of the list.
956 var mismatch_triple = true;
957 var mismatch_cpu_features = true;
958 var whitelist_item = CrossTarget{};
959 for (list) |t| {
960 mismatch_cpu_features = true;
961 mismatch_triple = true;
962
963 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
964 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
965 mismatch_triple = false;
966 whitelist_item = t;
967 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
968 mismatch_cpu_features = false;
969 break :whitelist_check;
970 } else {
971 break;
972 }
973 }
974 }
975 if (mismatch_triple) {
976 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
977 selected_canonicalized_triple,
978 });
979 for (list) |t| {
980 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
981 log.err(" {s}", .{t_triple});
982 }
983 } else {
984 assert(mismatch_cpu_features);
985 const whitelist_cpu = whitelist_item.getCpu();
986 const selected_cpu = selected_target.getCpu();
987 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
988 selected_cpu.model.name,
989 });
990 log.err(" Supported feature Set: ", .{});
991 const all_features = whitelist_cpu.arch.allFeaturesList();
992 var populated_cpu_features = whitelist_cpu.model.features;
993 populated_cpu_features.populateDependencies(all_features);
994 for (all_features) |feature, i_usize| {
995 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
996 const in_cpu_set = populated_cpu_features.isEnabled(i);
997 if (in_cpu_set) {
998 log.err("{s} ", .{feature.name});
999 }
1000 }
1001 log.err(" Remove: ", .{});
1002 for (all_features) |feature, i_usize| {
1003 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1004 const in_cpu_set = populated_cpu_features.isEnabled(i);
1005 const in_actual_set = selected_cpu.features.isEnabled(i);
1006 if (in_actual_set and !in_cpu_set) {
1007 log.err("{s} ", .{feature.name});
1008 }
1009 }
1010 }
1011 self.markInvalidUserInput();
1012 return args.default_target;
1013 }
1014
1015 return selected_target;
1016}
1017
1018pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool {
1019 const name = self.dupe(name_raw);
1020 const value = self.dupe(value_raw);
1021 const gop = try self.user_input_options.getOrPut(name);
1022 if (!gop.found_existing) {
1023 gop.value_ptr.* = UserInputOption{
1024 .name = name,
1025 .value = .{ .scalar = value },
1026 .used = false,
1027 };
1028 return false;
1029 }
1030
1031 // option already exists
1032 switch (gop.value_ptr.value) {
1033 .scalar => |s| {
1034 // turn it into a list
1035 var list = ArrayList([]const u8).init(self.allocator);
1036 try list.append(s);
1037 try list.append(value);
1038 try self.user_input_options.put(name, .{
1039 .name = name,
1040 .value = .{ .list = list },
1041 .used = false,
1042 });
1043 },
1044 .list => |*list| {
1045 // append to the list
1046 try list.append(value);
1047 try self.user_input_options.put(name, .{
1048 .name = name,
1049 .value = .{ .list = list.* },
1050 .used = false,
1051 });
1052 },
1053 .flag => {
1054 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1055 return true;
1056 },
1057 .map => |*map| {
1058 _ = map;
1059 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
1060 return true;
1061 },
1062 }
1063 return false;
1064}
1065
1066pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
1067 const name = self.dupe(name_raw);
1068 const gop = try self.user_input_options.getOrPut(name);
1069 if (!gop.found_existing) {
1070 gop.value_ptr.* = .{
1071 .name = name,
1072 .value = .{ .flag = {} },
1073 .used = false,
1074 };
1075 return false;
1076 }
1077
1078 // option already exists
1079 switch (gop.value_ptr.value) {
1080 .scalar => |s| {
1081 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1082 return true;
1083 },
1084 .list, .map => {
1085 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1086 return true;
1087 },
1088 .flag => {},
1089 }
1090 return false;
1091}
1092
1093fn typeToEnum(comptime T: type) TypeId {
1094 return switch (@typeInfo(T)) {
1095 .Int => .int,
1096 .Float => .float,
1097 .Bool => .bool,
1098 .Enum => .@"enum",
1099 else => switch (T) {
1100 []const u8 => .string,
1101 []const []const u8 => .list,
1102 else => @compileError("Unsupported type: " ++ @typeName(T)),
1103 },
1104 };
1105}
1106
1107fn markInvalidUserInput(self: *Build) void {
1108 self.invalid_user_input = true;
1109}
1110
1111pub fn validateUserInputDidItFail(self: *Build) bool {
1112 // make sure all args are used
1113 var it = self.user_input_options.iterator();
1114 while (it.next()) |entry| {
1115 if (!entry.value_ptr.used) {
1116 log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
1117 self.markInvalidUserInput();
1118 }
1119 }
1120
1121 return self.invalid_user_input;
1122}
1123
1124pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1125 return self.spawnChildEnvMap(null, self.env_map, argv);
1126}
1127
1128fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1129 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1130 for (argv) |arg| {
1131 std.debug.print("{s} ", .{arg});
1132 }
1133 std.debug.print("\n", .{});
1134}
1135
1136pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1137 if (self.verbose) {
1138 printCmd(cwd, argv);
1139 }
1140
1141 if (!std.process.can_spawn)
1142 return error.ExecNotSupported;
1143
1144 var child = std.ChildProcess.init(argv, self.allocator);
1145 child.cwd = cwd;
1146 child.env_map = env_map;
1147
1148 const term = child.spawnAndWait() catch |err| {
1149 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1150 return err;
1151 };
1152
1153 switch (term) {
1154 .Exited => |code| {
1155 if (code != 0) {
1156 log.err("The following command exited with error code {}:", .{code});
1157 printCmd(cwd, argv);
1158 return error.UncleanExit;
1159 }
1160 },
1161 else => {
1162 log.err("The following command terminated unexpectedly:", .{});
1163 printCmd(cwd, argv);
1164
1165 return error.UncleanExit;
1166 },
1167 }
1168}
1169
1170pub fn makePath(self: *Build, path: []const u8) !void {
1171 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1172 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1173 return err;
1174 };
1175}
1176
1177pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1178 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1179}
1180
1181pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep {
1182 return InstallArtifactStep.create(self, artifact);
1183}
1184
1185///`dest_rel_path` is relative to prefix path
1186pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1187 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1188}
1189
1190pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
1191 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1192}
1193
1194///`dest_rel_path` is relative to bin path
1195pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1196 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1197}
1198
1199///`dest_rel_path` is relative to lib path
1200pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1201 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1202}
1203
1204/// Output format (BIN vs Intel HEX) determined by filename
1205pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1206 const raw = self.addInstallRaw(artifact, dest_filename, options);
1207 self.getInstallStep().dependOn(&raw.step);
1208 return raw;
1209}
1210
1211///`dest_rel_path` is relative to install prefix path
1212pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1213 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1214}
1215
1216///`dest_rel_path` is relative to bin path
1217pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1218 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1219}
1220
1221///`dest_rel_path` is relative to lib path
1222pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1223 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1224}
1225
1226pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1227 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1228}
1229
1230pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1231 return InstallRawStep.create(self, artifact, dest_filename, options);
1232}
1233
1234pub fn addInstallFileWithDir(
1235 self: *Build,
1236 source: FileSource,
1237 install_dir: InstallDir,
1238 dest_rel_path: []const u8,
1239) *InstallFileStep {
1240 if (dest_rel_path.len == 0) {
1241 panic("dest_rel_path must be non-empty", .{});
1242 }
1243 const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM");
1244 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1245 return install_step;
1246}
1247
1248pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
1249 const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM");
1250 install_step.* = InstallDirStep.init(self, options);
1251 return install_step;
1252}
1253
1254pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
1255 const file = InstalledFile{
1256 .dir = dir,
1257 .path = dest_rel_path,
1258 };
1259 self.installed_files.append(file.dupe(self)) catch @panic("OOM");
1260}
1261
1262pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
1263 if (self.verbose) {
1264 log.info("cp {s} {s} ", .{ source_path, dest_path });
1265 }
1266 const cwd = fs.cwd();
1267 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1268 if (self.verbose) switch (prev_status) {
1269 .stale => log.info("# installed", .{}),
1270 .fresh => log.info("# up-to-date", .{}),
1271 };
1272}
1273
1274pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1275 if (self.verbose) {
1276 log.info("truncate {s}", .{dest_path});
1277 }
1278 const cwd = fs.cwd();
1279 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1280 error.FileNotFound => blk: {
1281 if (fs.path.dirname(dest_path)) |dirname| {
1282 try cwd.makePath(dirname);
1283 }
1284 break :blk try cwd.createFile(dest_path, .{});
1285 },
1286 else => |e| return e,
1287 };
1288 src_file.close();
1289}
1290
1291pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
1292 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM");
1293}
1294
1295pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
1296 return fs.path.join(self.allocator, paths) catch @panic("OOM");
1297}
1298
1299pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
1300 return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM");
1301}
1302
1303pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1304 // TODO report error for ambiguous situations
1305 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1306 for (self.search_prefixes.items) |search_prefix| {
1307 for (names) |name| {
1308 if (fs.path.isAbsolute(name)) {
1309 return name;
1310 }
1311 const full_path = self.pathJoin(&.{
1312 search_prefix,
1313 "bin",
1314 self.fmt("{s}{s}", .{ name, exe_extension }),
1315 });
1316 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1317 }
1318 }
1319 if (self.env_map.get("PATH")) |PATH| {
1320 for (names) |name| {
1321 if (fs.path.isAbsolute(name)) {
1322 return name;
1323 }
1324 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1325 while (it.next()) |path| {
1326 const full_path = self.pathJoin(&.{
1327 path,
1328 self.fmt("{s}{s}", .{ name, exe_extension }),
1329 });
1330 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1331 }
1332 }
1333 }
1334 for (names) |name| {
1335 if (fs.path.isAbsolute(name)) {
1336 return name;
1337 }
1338 for (paths) |path| {
1339 const full_path = self.pathJoin(&.{
1340 path,
1341 self.fmt("{s}{s}", .{ name, exe_extension }),
1342 });
1343 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1344 }
1345 }
1346 return error.FileNotFound;
1347}
1348
1349pub fn execAllowFail(
1350 self: *Build,
1351 argv: []const []const u8,
1352 out_code: *u8,
1353 stderr_behavior: std.ChildProcess.StdIo,
1354) ExecError![]u8 {
1355 assert(argv.len != 0);
1356
1357 if (!std.process.can_spawn)
1358 return error.ExecNotSupported;
1359
1360 const max_output_size = 400 * 1024;
1361 var child = std.ChildProcess.init(argv, self.allocator);
1362 child.stdin_behavior = .Ignore;
1363 child.stdout_behavior = .Pipe;
1364 child.stderr_behavior = stderr_behavior;
1365 child.env_map = self.env_map;
1366
1367 try child.spawn();
1368
1369 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1370 return error.ReadFailure;
1371 };
1372 errdefer self.allocator.free(stdout);
1373
1374 const term = try child.wait();
1375 switch (term) {
1376 .Exited => |code| {
1377 if (code != 0) {
1378 out_code.* = @truncate(u8, code);
1379 return error.ExitCodeFailure;
1380 }
1381 return stdout;
1382 },
1383 .Signal, .Stopped, .Unknown => |code| {
1384 out_code.* = @truncate(u8, code);
1385 return error.ProcessTerminated;
1386 },
1387 }
1388}
1389
1390pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1391 assert(argv.len != 0);
1392
1393 if (self.verbose) {
1394 printCmd(null, argv);
1395 }
1396
1397 if (!std.process.can_spawn) {
1398 if (src_step) |s| log.err("{s}...", .{s.name});
1399 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1400 printCmd(null, argv);
1401 std.os.abort();
1402 }
1403
1404 var code: u8 = undefined;
1405 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1406 error.ExecNotSupported => {
1407 if (src_step) |s| log.err("{s}...", .{s.name});
1408 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1409 printCmd(null, argv);
1410 std.os.abort();
1411 },
1412 error.FileNotFound => {
1413 if (src_step) |s| log.err("{s}...", .{s.name});
1414 log.err("Unable to spawn the following command: file not found", .{});
1415 printCmd(null, argv);
1416 std.os.exit(@truncate(u8, code));
1417 },
1418 error.ExitCodeFailure => {
1419 if (src_step) |s| log.err("{s}...", .{s.name});
1420 if (self.prominent_compile_errors) {
1421 log.err("The step exited with error code {d}", .{code});
1422 } else {
1423 log.err("The following command exited with error code {d}:", .{code});
1424 printCmd(null, argv);
1425 }
1426
1427 std.os.exit(@truncate(u8, code));
1428 },
1429 error.ProcessTerminated => {
1430 if (src_step) |s| log.err("{s}...", .{s.name});
1431 log.err("The following command terminated unexpectedly:", .{});
1432 printCmd(null, argv);
1433 std.os.exit(@truncate(u8, code));
1434 },
1435 else => |e| return e,
1436 };
1437}
1438
1439pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1440 return self.execFromStep(argv, null);
1441}
1442
1443pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1444 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
1445}
1446
1447pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1448 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1449 const base_dir = switch (dir) {
1450 .prefix => self.install_path,
1451 .bin => self.exe_dir,
1452 .lib => self.lib_dir,
1453 .header => self.h_dir,
1454 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1455 };
1456 return fs.path.resolve(
1457 self.allocator,
1458 &[_][]const u8{ base_dir, dest_rel_path },
1459 ) catch @panic("OOM");
1460}
1461
1462pub const Dependency = struct {
1463 builder: *Build,
1464
1465 pub fn artifact(d: *Dependency, name: []const u8) *CompileStep {
1466 var found: ?*CompileStep = null;
1467 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1468 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1469 if (mem.eql(u8, inst.artifact.name, name)) {
1470 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1471 found = inst.artifact;
1472 }
1473 }
1474 return found orelse {
1475 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1476 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1477 log.info("available artifact: '{s}'", .{inst.artifact.name});
1478 }
1479 panic("unable to find artifact '{s}'", .{name});
1480 };
1481 }
1482};
1483
1484pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
1485 const build_runner = @import("root");
1486 const deps = build_runner.dependencies;
1487
1488 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1489 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1490 mem.endsWith(u8, decl.name, name) and
1491 decl.name.len == b.dep_prefix.len + name.len)
1492 {
1493 const build_zig = @field(deps.imports, decl.name);
1494 const build_root = @field(deps.build_root, decl.name);
1495 return dependencyInner(b, name, build_root, build_zig, args);
1496 }
1497 }
1498
1499 const full_path = b.pathFromRoot("build.zig.ini");
1500 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1501 std.process.exit(1);
1502}
1503
1504fn dependencyInner(
1505 b: *Build,
1506 name: []const u8,
1507 build_root: []const u8,
1508 comptime build_zig: type,
1509 args: anytype,
1510) *Dependency {
1511 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
1512 sub_builder.runBuild(build_zig) catch @panic("unhandled error");
1513
1514 if (sub_builder.validateUserInputDidItFail()) {
1515 std.debug.dumpCurrentStackTrace(@returnAddress());
1516 }
1517
1518 const dep = b.allocator.create(Dependency) catch @panic("OOM");
1519 dep.* = .{ .builder = sub_builder };
1520 return dep;
1521}
1522
1523pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
1524 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1525 .Void => build_zig.build(b),
1526 .ErrorUnion => try build_zig.build(b),
1527 else => @compileError("expected return type of build to be 'void' or '!void'"),
1528 }
1529}
1530
1531test "builder.findProgram compiles" {
1532 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1533
1534 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1535 defer arena.deinit();
1536
1537 const host = try NativeTargetInfo.detect(.{});
1538
1539 const builder = try Build.create(
1540 arena.allocator(),
1541 "zig",
1542 "zig-cache",
1543 "zig-cache",
1544 "zig-cache",
1545 host,
1546 );
1547 defer builder.destroy();
1548 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1549}
1550
1551pub const Pkg = struct {
1552 name: []const u8,
1553 source: FileSource,
1554 dependencies: ?[]const Pkg = null,
1555};
1556
1557/// A file that is generated by a build step.
1558/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1559pub const GeneratedFile = struct {
1560 /// The step that generates the file
1561 step: *Step,
1562
1563 /// The path to the generated file. Must be either absolute or relative to the build root.
1564 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1565 path: ?[]const u8 = null,
1566
1567 pub fn getPath(self: GeneratedFile) []const u8 {
1568 return self.path orelse std.debug.panic(
1569 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1570 .{self.step.name},
1571 );
1572 }
1573};
1574
1575/// A file source is a reference to an existing or future file.
1576///
1577pub const FileSource = union(enum) {
1578 /// A plain file path, relative to build root or absolute.
1579 path: []const u8,
1580
1581 /// A file that is generated by an interface. Those files usually are
1582 /// not available until built by a build step.
1583 generated: *const GeneratedFile,
1584
1585 /// Returns a new file source that will have a relative path to the build root guaranteed.
1586 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1587 pub fn relative(path: []const u8) FileSource {
1588 std.debug.assert(!std.fs.path.isAbsolute(path));
1589 return FileSource{ .path = path };
1590 }
1591
1592 /// Returns a string that can be shown to represent the file source.
1593 /// Either returns the path or `"generated"`.
1594 pub fn getDisplayName(self: FileSource) []const u8 {
1595 return switch (self) {
1596 .path => self.path,
1597 .generated => "generated",
1598 };
1599 }
1600
1601 /// Adds dependencies this file source implies to the given step.
1602 pub fn addStepDependencies(self: FileSource, other_step: *Step) void {
1603 switch (self) {
1604 .path => {},
1605 .generated => |gen| other_step.dependOn(gen.step),
1606 }
1607 }
1608
1609 /// Should only be called during make(), returns a path relative to the build root or absolute.
1610 pub fn getPath(self: FileSource, builder: *Build) []const u8 {
1611 const path = switch (self) {
1612 .path => |p| builder.pathFromRoot(p),
1613 .generated => |gen| gen.getPath(),
1614 };
1615 return path;
1616 }
1617
1618 /// Duplicates the file source for a given builder.
1619 pub fn dupe(self: FileSource, b: *Build) FileSource {
1620 return switch (self) {
1621 .path => |p| .{ .path = b.dupePath(p) },
1622 .generated => |gen| .{ .generated = gen },
1623 };
1624 }
1625};
1626
1627/// Allocates a new string for assigning a value to a named macro.
1628/// If the value is omitted, it is set to 1.
1629/// `name` and `value` need not live longer than the function call.
1630pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1631 var macro = allocator.alloc(
1632 u8,
1633 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1634 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1635 mem.copy(u8, macro, name);
1636 if (value) |value_slice| {
1637 macro[name.len] = '=';
1638 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1639 }
1640 return macro;
1641}
1642
1643pub const VcpkgRoot = union(VcpkgRootStatus) {
1644 unattempted: void,
1645 not_found: void,
1646 found: []const u8,
1647};
1648
1649pub const VcpkgRootStatus = enum {
1650 unattempted,
1651 not_found,
1652 found,
1653};
1654
1655pub const InstallDir = union(enum) {
1656 prefix: void,
1657 lib: void,
1658 bin: void,
1659 header: void,
1660 /// A path relative to the prefix
1661 custom: []const u8,
1662
1663 /// Duplicates the install directory including the path if set to custom.
1664 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
1665 if (self == .custom) {
1666 // Written with this temporary to avoid RLS problems
1667 const duped_path = builder.dupe(self.custom);
1668 return .{ .custom = duped_path };
1669 } else {
1670 return self;
1671 }
1672 }
1673};
1674
1675pub const InstalledFile = struct {
1676 dir: InstallDir,
1677 path: []const u8,
1678
1679 /// Duplicates the installed file path and directory.
1680 pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile {
1681 return .{
1682 .dir = self.dir.dupe(builder),
1683 .path = builder.dupe(self.path),
1684 };
1685 }
1686};
1687
1688pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1689 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1690 const all_features = cpu.arch.allFeaturesList();
1691 var populated_cpu_features = cpu.model.features;
1692 populated_cpu_features.populateDependencies(all_features);
1693
1694 if (populated_cpu_features.eql(cpu.features)) {
1695 // The CPU name alone is sufficient.
1696 return cpu.model.name;
1697 } else {
1698 var mcpu_buffer = ArrayList(u8).init(allocator);
1699 try mcpu_buffer.appendSlice(cpu.model.name);
1700
1701 for (all_features) |feature, i_usize| {
1702 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1703 const in_cpu_set = populated_cpu_features.isEnabled(i);
1704 const in_actual_set = cpu.features.isEnabled(i);
1705 if (in_cpu_set and !in_actual_set) {
1706 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1707 } else if (!in_cpu_set and in_actual_set) {
1708 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1709 }
1710 }
1711
1712 return try mcpu_buffer.toOwnedSlice();
1713 }
1714}
1715
1716test "dupePkg()" {
1717 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1718
1719 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1720 defer arena.deinit();
1721
1722 const host = try NativeTargetInfo.detect(.{});
1723
1724 var builder = try Build.create(
1725 arena.allocator(),
1726 "test",
1727 "test",
1728 "test",
1729 "test",
1730 host,
1731 );
1732 defer builder.destroy();
1733
1734 var pkg_dep = Pkg{
1735 .name = "pkg_dep",
1736 .source = .{ .path = "/not/a/pkg_dep.zig" },
1737 };
1738 var pkg_top = Pkg{
1739 .name = "pkg_top",
1740 .source = .{ .path = "/not/a/pkg_top.zig" },
1741 .dependencies = &[_]Pkg{pkg_dep},
1742 };
1743 const duped = builder.dupePkg(pkg_top);
1744
1745 const original_deps = pkg_top.dependencies.?;
1746 const dupe_deps = duped.dependencies.?;
1747
1748 // probably the same top level package details
1749 try std.testing.expectEqualStrings(pkg_top.name, duped.name);
1750
1751 // probably the same dependencies
1752 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1753 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1754
1755 // could segfault otherwise if pointers in duplicated package's fields are
1756 // the same as those in stack allocated package's fields
1757 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1758 try std.testing.expect(duped.name.ptr != pkg_top.name.ptr);
1759 try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr);
1760 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1761 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1762}
1763
1764test {
1765 _ = CheckFileStep;
1766 _ = CheckObjectStep;
1767 _ = EmulatableRunStep;
1768 _ = FmtStep;
1769 _ = InstallArtifactStep;
1770 _ = InstallDirStep;
1771 _ = InstallFileStep;
1772 _ = InstallRawStep;
1773 _ = CompileStep;
1774 _ = LogStep;
1775 _ = OptionsStep;
1776 _ = RemoveDirStep;
1777 _ = RunStep;
1778 _ = TranslateCStep;
1779 _ = WriteFileStep;
1780}
lib/std/Build/CheckFileStep.zig created+51
......@@ -0,0 +1,51 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const mem = std.mem;
5
6const CheckFileStep = @This();
7
8pub const base_id = .check_file;
9
10step: Step,
11builder: *std.Build,
12expected_matches: []const []const u8,
13source: std.Build.FileSource,
14max_bytes: usize = 20 * 1024 * 1024,
15
16pub fn create(
17 builder: *std.Build,
18 source: std.Build.FileSource,
19 expected_matches: []const []const u8,
20) *CheckFileStep {
21 const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");
22 self.* = CheckFileStep{
23 .builder = builder,
24 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
25 .source = source.dupe(builder),
26 .expected_matches = builder.dupeStrings(expected_matches),
27 };
28 self.source.addStepDependencies(&self.step);
29 return self;
30}
31
32fn make(step: *Step) !void {
33 const self = @fieldParentPtr(CheckFileStep, "step", step);
34
35 const src_path = self.source.getPath(self.builder);
36 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
37
38 for (self.expected_matches) |expected_match| {
39 if (mem.indexOf(u8, contents, expected_match) == null) {
40 std.debug.print(
41 \\
42 \\========= Expected to find: ===================
43 \\{s}
44 \\========= But file does not contain it: =======
45 \\{s}
46 \\
47 , .{ expected_match, contents });
48 return error.TestFailed;
49 }
50 }
51}
lib/std/Build/CheckObjectStep.zig created+1024
......@@ -0,0 +1,1024 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const fs = std.fs;
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const testing = std.testing;
8
9const CheckObjectStep = @This();
10
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
14
15pub const base_id = .check_object;
16
17step: Step,
18builder: *std.Build,
19source: std.Build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),
22dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,
24
25pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
26 const gpa = builder.allocator;
27 const self = gpa.create(CheckObjectStep) catch @panic("OOM");
28 self.* = .{
29 .builder = builder,
30 .step = Step.init(.check_file, "CheckObject", gpa, make),
31 .source = source.dupe(builder),
32 .checks = std.ArrayList(Check).init(gpa),
33 .obj_format = obj_format,
34 };
35 self.source.addStepDependencies(&self.step);
36 return self;
37}
38
39/// Runs and (optionally) compares the output of a binary.
40/// Asserts `self` was generated from an executable step.
41pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
42 const dependencies_len = self.step.dependencies.items.len;
43 assert(dependencies_len > 0);
44 const exe_step = self.step.dependencies.items[dependencies_len - 1];
45 const exe = exe_step.cast(std.Build.CompileStep).?;
46 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
47 emulatable_step.step.dependOn(&self.step);
48 return emulatable_step;
49}
50
51/// There two types of actions currently suported:
52/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
53/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
54/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
55/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
56/// it should be plenty useful in its current form.
57/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
58/// using the MatchAction. It currently only supports an addition. The operation is required
59/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
60/// to avoid any parsing really).
61/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
62/// they could then be added with this simple program `vmaddr entryoff +`.
63const Action = struct {
64 tag: enum { match, not_present, compute_cmp },
65 phrase: []const u8,
66 expected: ?ComputeCompareExpected = null,
67
68 /// Will return true if the `phrase` was found in the `haystack`.
69 /// Some examples include:
70 ///
71 /// LC 0 => will match in its entirety
72 /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
73 /// and save under `vmaddr` global name (see `global_vars` param)
74 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
75 /// in that order with other letters in between
76 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
77 assert(act.tag == .match or act.tag == .not_present);
78
79 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
80 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
81 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
82
83 while (needle_it.next()) |needle_tok| {
84 const hay_tok = hay_it.next() orelse return false;
85
86 if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
87 // We have fuzzy matchers within the search pattern, so we match substrings.
88 var start = index;
89 var n_tok = needle_tok;
90 var h_tok = hay_tok;
91 while (true) {
92 n_tok = n_tok[start + 3 ..];
93 const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
94 n_tok[0..sub_end]
95 else
96 n_tok;
97 if (mem.indexOf(u8, h_tok, inner) == null) return false;
98 start = mem.indexOf(u8, n_tok, "{*}") orelse break;
99 }
100 } else if (mem.startsWith(u8, needle_tok, "{")) {
101 const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
102 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
103
104 const name = needle_tok[1..closing_brace];
105 if (name.len == 0) return error.MissingBraceValue;
106 const value = try std.fmt.parseInt(u64, hay_tok, 16);
107 candidate_var = .{
108 .name = name,
109 .value = value,
110 };
111 } else {
112 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
113 }
114 }
115
116 if (candidate_var) |v| {
117 try global_vars.putNoClobber(v.name, v.value);
118 }
119
120 return true;
121 }
122
123 /// Will return true if the `phrase` is correctly parsed into an RPN program and
124 /// its reduced, computed value compares using `op` with the expected value, either
125 /// a literal or another extracted variable.
126 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
127 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
128 var values = std.ArrayList(u64).init(gpa);
129
130 var it = mem.tokenize(u8, act.phrase, " ");
131 while (it.next()) |next| {
132 if (mem.eql(u8, next, "+")) {
133 try op_stack.append(.add);
134 } else if (mem.eql(u8, next, "-")) {
135 try op_stack.append(.sub);
136 } else if (mem.eql(u8, next, "%")) {
137 try op_stack.append(.mod);
138 } else if (mem.eql(u8, next, "*")) {
139 try op_stack.append(.mul);
140 } else {
141 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142 break :blk global_vars.get(next) orelse {
143 std.debug.print(
144 \\
145 \\========= Variable was not extracted: ===========
146 \\{s}
147 \\
148 , .{next});
149 return error.UnknownVariable;
150 };
151 };
152 try values.append(val);
153 }
154 }
155
156 var op_i: usize = 1;
157 var reduced: u64 = values.items[0];
158 for (op_stack.items) |op| {
159 const other = values.items[op_i];
160 switch (op) {
161 .add => {
162 reduced += other;
163 },
164 .sub => {
165 reduced -= other;
166 },
167 .mod => {
168 reduced %= other;
169 },
170 .mul => {
171 reduced *= other;
172 },
173 }
174 op_i += 1;
175 }
176
177 const exp_value = switch (act.expected.?.value) {
178 .variable => |name| global_vars.get(name) orelse {
179 std.debug.print(
180 \\
181 \\========= Variable was not extracted: ===========
182 \\{s}
183 \\
184 , .{name});
185 return error.UnknownVariable;
186 },
187 .literal => |x| x,
188 };
189 return math.compare(reduced, act.expected.?.op, exp_value);
190 }
191};
192
193const ComputeCompareExpected = struct {
194 op: math.CompareOperator,
195 value: union(enum) {
196 variable: []const u8,
197 literal: u64,
198 },
199
200 pub fn format(
201 value: @This(),
202 comptime fmt: []const u8,
203 options: std.fmt.FormatOptions,
204 writer: anytype,
205 ) !void {
206 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
207 _ = options;
208 try writer.print("{s} ", .{@tagName(value.op)});
209 switch (value.value) {
210 .variable => |name| try writer.writeAll(name),
211 .literal => |x| try writer.print("{x}", .{x}),
212 }
213 }
214};
215
216const Check = struct {
217 builder: *std.Build,
218 actions: std.ArrayList(Action),
219
220 fn create(b: *std.Build) Check {
221 return .{
222 .builder = b,
223 .actions = std.ArrayList(Action).init(b.allocator),
224 };
225 }
226
227 fn match(self: *Check, phrase: []const u8) void {
228 self.actions.append(.{
229 .tag = .match,
230 .phrase = self.builder.dupe(phrase),
231 }) catch @panic("OOM");
232 }
233
234 fn notPresent(self: *Check, phrase: []const u8) void {
235 self.actions.append(.{
236 .tag = .not_present,
237 .phrase = self.builder.dupe(phrase),
238 }) catch @panic("OOM");
239 }
240
241 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
242 self.actions.append(.{
243 .tag = .compute_cmp,
244 .phrase = self.builder.dupe(phrase),
245 .expected = expected,
246 }) catch @panic("OOM");
247 }
248};
249
250/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
251pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
252 var new_check = Check.create(self.builder);
253 new_check.match(phrase);
254 self.checks.append(new_check) catch @panic("OOM");
255}
256
257/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
258/// Asserts at least one check already exists.
259pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
260 assert(self.checks.items.len > 0);
261 const last = &self.checks.items[self.checks.items.len - 1];
262 last.match(phrase);
263}
264
265/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
266/// however ensures there is no matching phrase in the output.
267/// Asserts at least one check already exists.
268pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
269 assert(self.checks.items.len > 0);
270 const last = &self.checks.items[self.checks.items.len - 1];
271 last.notPresent(phrase);
272}
273
274/// Creates a new check checking specifically symbol table parsed and dumped from the object
275/// file.
276/// Issuing this check will force parsing and dumping of the symbol table.
277pub fn checkInSymtab(self: *CheckObjectStep) void {
278 self.dump_symtab = true;
279 const symtab_label = switch (self.obj_format) {
280 .macho => MachODumper.symtab_label,
281 else => @panic("TODO other parsers"),
282 };
283 self.checkStart(symtab_label);
284}
285
286/// Creates a new standalone, singular check which allows running simple binary operations
287/// on the extracted variables. It will then compare the reduced program with the value of
288/// the expected variable.
289pub fn checkComputeCompare(
290 self: *CheckObjectStep,
291 program: []const u8,
292 expected: ComputeCompareExpected,
293) void {
294 var new_check = Check.create(self.builder);
295 new_check.computeCmp(program, expected);
296 self.checks.append(new_check) catch @panic("OOM");
297}
298
299fn make(step: *Step) !void {
300 const self = @fieldParentPtr(CheckObjectStep, "step", step);
301
302 const gpa = self.builder.allocator;
303 const src_path = self.source.getPath(self.builder);
304 const contents = try fs.cwd().readFileAllocOptions(
305 gpa,
306 src_path,
307 self.max_bytes,
308 null,
309 @alignOf(u64),
310 null,
311 );
312
313 const output = switch (self.obj_format) {
314 .macho => try MachODumper.parseAndDump(contents, .{
315 .gpa = gpa,
316 .dump_symtab = self.dump_symtab,
317 }),
318 .elf => @panic("TODO elf parser"),
319 .coff => @panic("TODO coff parser"),
320 .wasm => try WasmDumper.parseAndDump(contents, .{
321 .gpa = gpa,
322 .dump_symtab = self.dump_symtab,
323 }),
324 else => unreachable,
325 };
326
327 var vars = std.StringHashMap(u64).init(gpa);
328
329 for (self.checks.items) |chk| {
330 var it = mem.tokenize(u8, output, "\r\n");
331 for (chk.actions.items) |act| {
332 switch (act.tag) {
333 .match => {
334 while (it.next()) |line| {
335 if (try act.match(line, &vars)) break;
336 } else {
337 std.debug.print(
338 \\
339 \\========= Expected to find: ==========================
340 \\{s}
341 \\========= But parsed file does not contain it: =======
342 \\{s}
343 \\
344 , .{ act.phrase, output });
345 return error.TestFailed;
346 }
347 },
348 .not_present => {
349 while (it.next()) |line| {
350 if (try act.match(line, &vars)) {
351 std.debug.print(
352 \\
353 \\========= Expected not to find: ===================
354 \\{s}
355 \\========= But parsed file does contain it: ========
356 \\{s}
357 \\
358 , .{ act.phrase, output });
359 return error.TestFailed;
360 }
361 }
362 },
363 .compute_cmp => {
364 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
365 error.UnknownVariable => {
366 std.debug.print(
367 \\========= From parsed file: =====================
368 \\{s}
369 \\
370 , .{output});
371 return error.TestFailed;
372 },
373 else => |e| return e,
374 };
375 if (!res) {
376 std.debug.print(
377 \\
378 \\========= Comparison failed for action: ===========
379 \\{s} {}
380 \\========= From parsed file: =======================
381 \\{s}
382 \\
383 , .{ act.phrase, act.expected.?, output });
384 return error.TestFailed;
385 }
386 },
387 }
388 }
389 }
390}
391
392const Opts = struct {
393 gpa: ?Allocator = null,
394 dump_symtab: bool = false,
395};
396
397const MachODumper = struct {
398 const LoadCommandIterator = macho.LoadCommandIterator;
399 const symtab_label = "symtab";
400
401 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
402 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
403 var stream = std.io.fixedBufferStream(bytes);
404 const reader = stream.reader();
405
406 const hdr = try reader.readStruct(macho.mach_header_64);
407 if (hdr.magic != macho.MH_MAGIC_64) {
408 return error.InvalidMagicNumber;
409 }
410
411 var output = std.ArrayList(u8).init(gpa);
412 const writer = output.writer();
413
414 var symtab: []const macho.nlist_64 = undefined;
415 var strtab: []const u8 = undefined;
416 var sections = std.ArrayList(macho.section_64).init(gpa);
417 var imports = std.ArrayList([]const u8).init(gpa);
418
419 var it = LoadCommandIterator{
420 .ncmds = hdr.ncmds,
421 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
422 };
423 var i: usize = 0;
424 while (it.next()) |cmd| {
425 switch (cmd.cmd()) {
426 .SEGMENT_64 => {
427 const seg = cmd.cast(macho.segment_command_64).?;
428 try sections.ensureUnusedCapacity(seg.nsects);
429 for (cmd.getSections()) |sect| {
430 sections.appendAssumeCapacity(sect);
431 }
432 },
433 .SYMTAB => if (opts.dump_symtab) {
434 const lc = cmd.cast(macho.symtab_command).?;
435 symtab = @ptrCast(
436 [*]const macho.nlist_64,
437 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
438 )[0..lc.nsyms];
439 strtab = bytes[lc.stroff..][0..lc.strsize];
440 },
441 .LOAD_DYLIB,
442 .LOAD_WEAK_DYLIB,
443 .REEXPORT_DYLIB,
444 => {
445 try imports.append(cmd.getDylibPathName());
446 },
447 else => {},
448 }
449
450 try dumpLoadCommand(cmd, i, writer);
451 try writer.writeByte('\n');
452
453 i += 1;
454 }
455
456 if (opts.dump_symtab) {
457 try writer.print("{s}\n", .{symtab_label});
458 for (symtab) |sym| {
459 if (sym.stab()) continue;
460 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
461 if (sym.sect()) {
462 const sect = sections.items[sym.n_sect - 1];
463 try writer.print("{x} ({s},{s})", .{
464 sym.n_value,
465 sect.segName(),
466 sect.sectName(),
467 });
468 if (sym.ext()) {
469 try writer.writeAll(" external");
470 }
471 try writer.print(" {s}\n", .{sym_name});
472 } else if (sym.undf()) {
473 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
474 const import_name = blk: {
475 if (ordinal <= 0) {
476 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
477 break :blk "self import";
478 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
479 break :blk "main executable";
480 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
481 break :blk "flat lookup";
482 unreachable;
483 }
484 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
485 const basename = fs.path.basename(full_path);
486 assert(basename.len > 0);
487 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
488 break :blk basename[0..ext];
489 };
490 try writer.writeAll("(undefined)");
491 if (sym.weakRef()) {
492 try writer.writeAll(" weak");
493 }
494 if (sym.ext()) {
495 try writer.writeAll(" external");
496 }
497 try writer.print(" {s} (from {s})\n", .{
498 sym_name,
499 import_name,
500 });
501 } else unreachable;
502 }
503 }
504
505 return output.toOwnedSlice();
506 }
507
508 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
509 // print header first
510 try writer.print(
511 \\LC {d}
512 \\cmd {s}
513 \\cmdsize {d}
514 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
515
516 switch (lc.cmd()) {
517 .SEGMENT_64 => {
518 const seg = lc.cast(macho.segment_command_64).?;
519 try writer.writeByte('\n');
520 try writer.print(
521 \\segname {s}
522 \\vmaddr {x}
523 \\vmsize {x}
524 \\fileoff {x}
525 \\filesz {x}
526 , .{
527 seg.segName(),
528 seg.vmaddr,
529 seg.vmsize,
530 seg.fileoff,
531 seg.filesize,
532 });
533
534 for (lc.getSections()) |sect| {
535 try writer.writeByte('\n');
536 try writer.print(
537 \\sectname {s}
538 \\addr {x}
539 \\size {x}
540 \\offset {x}
541 \\align {x}
542 , .{
543 sect.sectName(),
544 sect.addr,
545 sect.size,
546 sect.offset,
547 sect.@"align",
548 });
549 }
550 },
551
552 .ID_DYLIB,
553 .LOAD_DYLIB,
554 .LOAD_WEAK_DYLIB,
555 .REEXPORT_DYLIB,
556 => {
557 const dylib = lc.cast(macho.dylib_command).?;
558 try writer.writeByte('\n');
559 try writer.print(
560 \\name {s}
561 \\timestamp {d}
562 \\current version {x}
563 \\compatibility version {x}
564 , .{
565 lc.getDylibPathName(),
566 dylib.dylib.timestamp,
567 dylib.dylib.current_version,
568 dylib.dylib.compatibility_version,
569 });
570 },
571
572 .MAIN => {
573 const main = lc.cast(macho.entry_point_command).?;
574 try writer.writeByte('\n');
575 try writer.print(
576 \\entryoff {x}
577 \\stacksize {x}
578 , .{ main.entryoff, main.stacksize });
579 },
580
581 .RPATH => {
582 try writer.writeByte('\n');
583 try writer.print(
584 \\path {s}
585 , .{
586 lc.getRpathPathName(),
587 });
588 },
589
590 .UUID => {
591 const uuid = lc.cast(macho.uuid_command).?;
592 try writer.writeByte('\n');
593 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
594 },
595
596 .DATA_IN_CODE,
597 .FUNCTION_STARTS,
598 .CODE_SIGNATURE,
599 => {
600 const llc = lc.cast(macho.linkedit_data_command).?;
601 try writer.writeByte('\n');
602 try writer.print(
603 \\dataoff {x}
604 \\datasize {x}
605 , .{ llc.dataoff, llc.datasize });
606 },
607
608 .DYLD_INFO_ONLY => {
609 const dlc = lc.cast(macho.dyld_info_command).?;
610 try writer.writeByte('\n');
611 try writer.print(
612 \\rebaseoff {x}
613 \\rebasesize {x}
614 \\bindoff {x}
615 \\bindsize {x}
616 \\weakbindoff {x}
617 \\weakbindsize {x}
618 \\lazybindoff {x}
619 \\lazybindsize {x}
620 \\exportoff {x}
621 \\exportsize {x}
622 , .{
623 dlc.rebase_off,
624 dlc.rebase_size,
625 dlc.bind_off,
626 dlc.bind_size,
627 dlc.weak_bind_off,
628 dlc.weak_bind_size,
629 dlc.lazy_bind_off,
630 dlc.lazy_bind_size,
631 dlc.export_off,
632 dlc.export_size,
633 });
634 },
635
636 .SYMTAB => {
637 const slc = lc.cast(macho.symtab_command).?;
638 try writer.writeByte('\n');
639 try writer.print(
640 \\symoff {x}
641 \\nsyms {x}
642 \\stroff {x}
643 \\strsize {x}
644 , .{
645 slc.symoff,
646 slc.nsyms,
647 slc.stroff,
648 slc.strsize,
649 });
650 },
651
652 .DYSYMTAB => {
653 const dlc = lc.cast(macho.dysymtab_command).?;
654 try writer.writeByte('\n');
655 try writer.print(
656 \\ilocalsym {x}
657 \\nlocalsym {x}
658 \\iextdefsym {x}
659 \\nextdefsym {x}
660 \\iundefsym {x}
661 \\nundefsym {x}
662 \\indirectsymoff {x}
663 \\nindirectsyms {x}
664 , .{
665 dlc.ilocalsym,
666 dlc.nlocalsym,
667 dlc.iextdefsym,
668 dlc.nextdefsym,
669 dlc.iundefsym,
670 dlc.nundefsym,
671 dlc.indirectsymoff,
672 dlc.nindirectsyms,
673 });
674 },
675
676 else => {},
677 }
678 }
679};
680
681const WasmDumper = struct {
682 const symtab_label = "symbols";
683
684 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
685 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
686 if (opts.dump_symtab) {
687 @panic("TODO: Implement symbol table parsing and dumping");
688 }
689
690 var fbs = std.io.fixedBufferStream(bytes);
691 const reader = fbs.reader();
692
693 const buf = try reader.readBytesNoEof(8);
694 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
695 return error.InvalidMagicByte;
696 }
697 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
698 return error.UnsupportedWasmVersion;
699 }
700
701 var output = std.ArrayList(u8).init(gpa);
702 errdefer output.deinit();
703 const writer = output.writer();
704
705 while (reader.readByte()) |current_byte| {
706 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
707 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
708 return err;
709 };
710
711 const section_length = try std.leb.readULEB128(u32, reader);
712 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
713 fbs.pos += section_length;
714 } else |_| {} // reached end of stream
715
716 return output.toOwnedSlice();
717 }
718
719 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
720 var fbs = std.io.fixedBufferStream(data);
721 const reader = fbs.reader();
722
723 try writer.print(
724 \\Section {s}
725 \\size {d}
726 , .{ @tagName(section), data.len });
727
728 switch (section) {
729 .type,
730 .import,
731 .function,
732 .table,
733 .memory,
734 .global,
735 .@"export",
736 .element,
737 .code,
738 .data,
739 => {
740 const entries = try std.leb.readULEB128(u32, reader);
741 try writer.print("\nentries {d}\n", .{entries});
742 try dumpSection(section, data[fbs.pos..], entries, writer);
743 },
744 .custom => {
745 const name_length = try std.leb.readULEB128(u32, reader);
746 const name = data[fbs.pos..][0..name_length];
747 fbs.pos += name_length;
748 try writer.print("\nname {s}\n", .{name});
749
750 if (mem.eql(u8, name, "name")) {
751 try parseDumpNames(reader, writer, data);
752 } else if (mem.eql(u8, name, "producers")) {
753 try parseDumpProducers(reader, writer, data);
754 } else if (mem.eql(u8, name, "target_features")) {
755 try parseDumpFeatures(reader, writer, data);
756 }
757 // TODO: Implement parsing and dumping other custom sections (such as relocations)
758 },
759 .start => {
760 const start = try std.leb.readULEB128(u32, reader);
761 try writer.print("\nstart {d}\n", .{start});
762 },
763 else => {}, // skip unknown sections
764 }
765 }
766
767 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
768 var fbs = std.io.fixedBufferStream(data);
769 const reader = fbs.reader();
770
771 switch (section) {
772 .type => {
773 var i: u32 = 0;
774 while (i < entries) : (i += 1) {
775 const func_type = try reader.readByte();
776 if (func_type != std.wasm.function_type) {
777 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
778 return error.UnexpectedByte;
779 }
780 const params = try std.leb.readULEB128(u32, reader);
781 try writer.print("params {d}\n", .{params});
782 var index: u32 = 0;
783 while (index < params) : (index += 1) {
784 try parseDumpType(std.wasm.Valtype, reader, writer);
785 } else index = 0;
786 const returns = try std.leb.readULEB128(u32, reader);
787 try writer.print("returns {d}\n", .{returns});
788 while (index < returns) : (index += 1) {
789 try parseDumpType(std.wasm.Valtype, reader, writer);
790 }
791 }
792 },
793 .import => {
794 var i: u32 = 0;
795 while (i < entries) : (i += 1) {
796 const module_name_len = try std.leb.readULEB128(u32, reader);
797 const module_name = data[fbs.pos..][0..module_name_len];
798 fbs.pos += module_name_len;
799 const name_len = try std.leb.readULEB128(u32, reader);
800 const name = data[fbs.pos..][0..name_len];
801 fbs.pos += name_len;
802
803 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
804 std.debug.print("Invalid import kind\n", .{});
805 return err;
806 };
807
808 try writer.print(
809 \\module {s}
810 \\name {s}
811 \\kind {s}
812 , .{ module_name, name, @tagName(kind) });
813 try writer.writeByte('\n');
814 switch (kind) {
815 .function => {
816 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
817 },
818 .memory => {
819 try parseDumpLimits(reader, writer);
820 },
821 .global => {
822 try parseDumpType(std.wasm.Valtype, reader, writer);
823 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
824 },
825 .table => {
826 try parseDumpType(std.wasm.RefType, reader, writer);
827 try parseDumpLimits(reader, writer);
828 },
829 }
830 }
831 },
832 .function => {
833 var i: u32 = 0;
834 while (i < entries) : (i += 1) {
835 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
836 }
837 },
838 .table => {
839 var i: u32 = 0;
840 while (i < entries) : (i += 1) {
841 try parseDumpType(std.wasm.RefType, reader, writer);
842 try parseDumpLimits(reader, writer);
843 }
844 },
845 .memory => {
846 var i: u32 = 0;
847 while (i < entries) : (i += 1) {
848 try parseDumpLimits(reader, writer);
849 }
850 },
851 .global => {
852 var i: u32 = 0;
853 while (i < entries) : (i += 1) {
854 try parseDumpType(std.wasm.Valtype, reader, writer);
855 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
856 try parseDumpInit(reader, writer);
857 }
858 },
859 .@"export" => {
860 var i: u32 = 0;
861 while (i < entries) : (i += 1) {
862 const name_len = try std.leb.readULEB128(u32, reader);
863 const name = data[fbs.pos..][0..name_len];
864 fbs.pos += name_len;
865 const kind_byte = try std.leb.readULEB128(u8, reader);
866 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
867 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
868 return err;
869 };
870 const index = try std.leb.readULEB128(u32, reader);
871 try writer.print(
872 \\name {s}
873 \\kind {s}
874 \\index {d}
875 , .{ name, @tagName(kind), index });
876 try writer.writeByte('\n');
877 }
878 },
879 .element => {
880 var i: u32 = 0;
881 while (i < entries) : (i += 1) {
882 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
883 try parseDumpInit(reader, writer);
884
885 const function_indexes = try std.leb.readULEB128(u32, reader);
886 var function_index: u32 = 0;
887 try writer.print("indexes {d}\n", .{function_indexes});
888 while (function_index < function_indexes) : (function_index += 1) {
889 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
890 }
891 }
892 },
893 .code => {}, // code section is considered opaque to linker
894 .data => {
895 var i: u32 = 0;
896 while (i < entries) : (i += 1) {
897 const index = try std.leb.readULEB128(u32, reader);
898 try writer.print("memory index 0x{x}\n", .{index});
899 try parseDumpInit(reader, writer);
900 const size = try std.leb.readULEB128(u32, reader);
901 try writer.print("size {d}\n", .{size});
902 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
903 }
904 },
905 else => unreachable,
906 }
907 }
908
909 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
910 const type_byte = try reader.readByte();
911 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
912 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
913 return err;
914 };
915 try writer.print("type {s}\n", .{@tagName(valtype)});
916 }
917
918 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
919 const flags = try std.leb.readULEB128(u8, reader);
920 const min = try std.leb.readULEB128(u32, reader);
921
922 try writer.print("min {x}\n", .{min});
923 if (flags != 0) {
924 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
925 }
926 }
927
928 fn parseDumpInit(reader: anytype, writer: anytype) !void {
929 const byte = try std.leb.readULEB128(u8, reader);
930 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
931 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
932 return err;
933 };
934 switch (opcode) {
935 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
936 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
937 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
938 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
939 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
940 else => unreachable,
941 }
942 const end_opcode = try std.leb.readULEB128(u8, reader);
943 if (end_opcode != std.wasm.opcode(.end)) {
944 std.debug.print("expected 'end' opcode in init expression\n", .{});
945 return error.MissingEndOpcode;
946 }
947 }
948
949 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
950 while (reader.context.pos < data.len) {
951 try parseDumpType(std.wasm.NameSubsection, reader, writer);
952 const size = try std.leb.readULEB128(u32, reader);
953 const entries = try std.leb.readULEB128(u32, reader);
954 try writer.print(
955 \\size {d}
956 \\names {d}
957 , .{ size, entries });
958 try writer.writeByte('\n');
959 var i: u32 = 0;
960 while (i < entries) : (i += 1) {
961 const index = try std.leb.readULEB128(u32, reader);
962 const name_len = try std.leb.readULEB128(u32, reader);
963 const pos = reader.context.pos;
964 const name = data[pos..][0..name_len];
965 reader.context.pos += name_len;
966
967 try writer.print(
968 \\index {d}
969 \\name {s}
970 , .{ index, name });
971 try writer.writeByte('\n');
972 }
973 }
974 }
975
976 fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
977 const field_count = try std.leb.readULEB128(u32, reader);
978 try writer.print("fields {d}\n", .{field_count});
979 var current_field: u32 = 0;
980 while (current_field < field_count) : (current_field += 1) {
981 const field_name_length = try std.leb.readULEB128(u32, reader);
982 const field_name = data[reader.context.pos..][0..field_name_length];
983 reader.context.pos += field_name_length;
984
985 const value_count = try std.leb.readULEB128(u32, reader);
986 try writer.print(
987 \\field_name {s}
988 \\values {d}
989 , .{ field_name, value_count });
990 try writer.writeByte('\n');
991 var current_value: u32 = 0;
992 while (current_value < value_count) : (current_value += 1) {
993 const value_length = try std.leb.readULEB128(u32, reader);
994 const value = data[reader.context.pos..][0..value_length];
995 reader.context.pos += value_length;
996
997 const version_length = try std.leb.readULEB128(u32, reader);
998 const version = data[reader.context.pos..][0..version_length];
999 reader.context.pos += version_length;
1000
1001 try writer.print(
1002 \\value_name {s}
1003 \\version {s}
1004 , .{ value, version });
1005 try writer.writeByte('\n');
1006 }
1007 }
1008 }
1009
1010 fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
1011 const feature_count = try std.leb.readULEB128(u32, reader);
1012 try writer.print("features {d}\n", .{feature_count});
1013
1014 var index: u32 = 0;
1015 while (index < feature_count) : (index += 1) {
1016 const prefix_byte = try std.leb.readULEB128(u8, reader);
1017 const name_length = try std.leb.readULEB128(u32, reader);
1018 const feature_name = data[reader.context.pos..][0..name_length];
1019 reader.context.pos += name_length;
1020
1021 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
1022 }
1023 }
1024};
lib/std/Build/CompileStep.zig created+2053
......@@ -0,0 +1,2053 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const CrossTarget = std.zig.CrossTarget;
14const NativeTargetInfo = std.zig.system.NativeTargetInfo;
15const FileSource = std.Build.FileSource;
16const PkgConfigPkg = std.Build.PkgConfigPkg;
17const PkgConfigError = std.Build.PkgConfigError;
18const ExecError = std.Build.ExecError;
19const Pkg = std.Build.Pkg;
20const VcpkgRoot = std.Build.VcpkgRoot;
21const InstallDir = std.Build.InstallDir;
22const InstallArtifactStep = std.Build.InstallArtifactStep;
23const GeneratedFile = std.Build.GeneratedFile;
24const InstallRawStep = std.Build.InstallRawStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
26const CheckObjectStep = std.Build.CheckObjectStep;
27const RunStep = std.Build.RunStep;
28const OptionsStep = std.Build.OptionsStep;
29const ConfigHeaderStep = std.Build.ConfigHeaderStep;
30const CompileStep = @This();
31
32pub const base_id: Step.Id = .compile;
33
34step: Step,
35builder: *std.Build,
36name: []const u8,
37target: CrossTarget,
38target_info: NativeTargetInfo,
39optimize: std.builtin.Mode,
40linker_script: ?FileSource = null,
41version_script: ?[]const u8 = null,
42out_filename: []const u8,
43linkage: ?Linkage = null,
44version: ?std.builtin.Version,
45kind: Kind,
46major_only_filename: ?[]const u8,
47name_only_filename: ?[]const u8,
48strip: ?bool,
49unwind_tables: ?bool,
50// keep in sync with src/link.zig:CompressDebugSections
51compress_debug_sections: enum { none, zlib } = .none,
52lib_paths: ArrayList([]const u8),
53rpaths: ArrayList([]const u8),
54framework_dirs: ArrayList([]const u8),
55frameworks: StringHashMap(FrameworkLinkInfo),
56verbose_link: bool,
57verbose_cc: bool,
58emit_analysis: EmitOption = .default,
59emit_asm: EmitOption = .default,
60emit_bin: EmitOption = .default,
61emit_docs: EmitOption = .default,
62emit_implib: EmitOption = .default,
63emit_llvm_bc: EmitOption = .default,
64emit_llvm_ir: EmitOption = .default,
65// Lots of things depend on emit_h having a consistent path,
66// so it is not an EmitOption for now.
67emit_h: bool = false,
68bundle_compiler_rt: ?bool = null,
69single_threaded: ?bool = null,
70stack_protector: ?bool = null,
71disable_stack_probing: bool,
72disable_sanitize_c: bool,
73sanitize_thread: bool,
74rdynamic: bool,
75import_memory: bool = false,
76/// For WebAssembly targets, this will allow for undefined symbols to
77/// be imported from the host environment.
78import_symbols: bool = false,
79import_table: bool = false,
80export_table: bool = false,
81initial_memory: ?u64 = null,
82max_memory: ?u64 = null,
83shared_memory: bool = false,
84global_base: ?u64 = null,
85c_std: std.Build.CStd,
86override_lib_dir: ?[]const u8,
87main_pkg_path: ?[]const u8,
88exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,
90filter: ?[]const u8,
91test_evented_io: bool = false,
92test_runner: ?[]const u8,
93code_model: std.builtin.CodeModel = .default,
94wasi_exec_model: ?std.builtin.WasiExecModel = null,
95/// Symbols to be exported when compiling to wasm
96export_symbol_names: []const []const u8 = &.{},
97
98root_src: ?FileSource,
99out_h_filename: []const u8,
100out_lib_filename: []const u8,
101out_pdb_filename: []const u8,
102packages: ArrayList(Pkg),
103
104object_src: []const u8,
105
106link_objects: ArrayList(LinkObject),
107include_dirs: ArrayList(IncludeDir),
108c_macros: ArrayList([]const u8),
109installed_headers: ArrayList(*Step),
110output_dir: ?[]const u8,
111is_linking_libc: bool = false,
112is_linking_libcpp: bool = false,
113vcpkg_bin_path: ?[]const u8 = null,
114
115/// This may be set in order to override the default install directory
116override_dest_dir: ?InstallDir,
117installed_path: ?[]const u8,
118install_step: ?*InstallArtifactStep,
119
120/// Base address for an executable image.
121image_base: ?u64 = null,
122
123libc_file: ?FileSource = null,
124
125valgrind_support: ?bool = null,
126each_lib_rpath: ?bool = null,
127/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
128/// which can be used to coordinate a stripped binary with its debug symbols.
129/// As an example, the bloaty project refuses to work unless its inputs have
130/// build ids, in order to prevent accidental mismatches.
131/// The default is to not include this section because it slows down linking.
132build_id: ?bool = null,
133
134/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
135/// file.
136link_eh_frame_hdr: bool = false,
137link_emit_relocs: bool = false,
138
139/// Place every function in its own section so that unused ones may be
140/// safely garbage-collected during the linking phase.
141link_function_sections: bool = false,
142
143/// Remove functions and data that are unreachable by the entry point or
144/// exported symbols.
145link_gc_sections: ?bool = null,
146
147linker_allow_shlib_undefined: ?bool = null,
148
149/// Permit read-only relocations in read-only segments. Disallowed by default.
150link_z_notext: bool = false,
151
152/// Force all relocations to be read-only after processing.
153link_z_relro: bool = true,
154
155/// Allow relocations to be lazily processed after load.
156link_z_lazy: bool = false,
157
158/// Common page size
159link_z_common_page_size: ?u64 = null,
160
161/// Maximum page size
162link_z_max_page_size: ?u64 = null,
163
164/// (Darwin) Install name for the dylib
165install_name: ?[]const u8 = null,
166
167/// (Darwin) Path to entitlements file
168entitlements: ?[]const u8 = null,
169
170/// (Darwin) Size of the pagezero segment.
171pagezero_size: ?u64 = null,
172
173/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
174/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
175/// option.
176/// By default, if no option is specified, the linker assumes `paths_first` as the default
177/// search strategy.
178search_strategy: ?enum { paths_first, dylibs_first } = null,
179
180/// (Darwin) Set size of the padding between the end of load commands
181/// and start of `__TEXT,__text` section.
182headerpad_size: ?u32 = null,
183
184/// (Darwin) Automatically Set size of the padding between the end of load commands
185/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
186headerpad_max_install_names: bool = false,
187
188/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
189dead_strip_dylibs: bool = false,
190
191/// Position Independent Code
192force_pic: ?bool = null,
193
194/// Position Independent Executable
195pie: ?bool = null,
196
197red_zone: ?bool = null,
198
199omit_frame_pointer: ?bool = null,
200dll_export_fns: ?bool = null,
201
202subsystem: ?std.Target.SubSystem = null,
203
204entry_symbol_name: ?[]const u8 = null,
205
206/// Overrides the default stack size
207stack_size: ?u64 = null,
208
209want_lto: ?bool = null,
210use_llvm: ?bool = null,
211use_lld: ?bool = null,
212
213output_path_source: GeneratedFile,
214output_lib_path_source: GeneratedFile,
215output_h_path_source: GeneratedFile,
216output_pdb_path_source: GeneratedFile,
217
218pub const CSourceFiles = struct {
219 files: []const []const u8,
220 flags: []const []const u8,
221};
222
223pub const CSourceFile = struct {
224 source: FileSource,
225 args: []const []const u8,
226
227 pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile {
228 return .{
229 .source = self.source.dupe(b),
230 .args = b.dupeStrings(self.args),
231 };
232 }
233};
234
235pub const LinkObject = union(enum) {
236 static_path: FileSource,
237 other_step: *CompileStep,
238 system_lib: SystemLib,
239 assembly_file: FileSource,
240 c_source_file: *CSourceFile,
241 c_source_files: *CSourceFiles,
242};
243
244pub const SystemLib = struct {
245 name: []const u8,
246 needed: bool,
247 weak: bool,
248 use_pkg_config: enum {
249 /// Don't use pkg-config, just pass -lfoo where foo is name.
250 no,
251 /// Try to get information on how to link the library from pkg-config.
252 /// If that fails, fall back to passing -lfoo where foo is name.
253 yes,
254 /// Try to get information on how to link the library from pkg-config.
255 /// If that fails, error out.
256 force,
257 },
258};
259
260const FrameworkLinkInfo = struct {
261 needed: bool = false,
262 weak: bool = false,
263};
264
265pub const IncludeDir = union(enum) {
266 raw_path: []const u8,
267 raw_path_system: []const u8,
268 other_step: *CompileStep,
269 config_header_step: *ConfigHeaderStep,
270};
271
272pub const Options = struct {
273 name: []const u8,
274 root_source_file: ?FileSource = null,
275 target: CrossTarget,
276 optimize: std.builtin.Mode,
277 kind: Kind,
278 linkage: ?Linkage = null,
279 version: ?std.builtin.Version = null,
280};
281
282pub const Kind = enum {
283 exe,
284 lib,
285 obj,
286 @"test",
287 test_exe,
288};
289
290pub const Linkage = enum { dynamic, static };
291
292pub const EmitOption = union(enum) {
293 default: void,
294 no_emit: void,
295 emit: void,
296 emit_to: []const u8,
297
298 fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 {
299 return switch (self) {
300 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
301 .default => null,
302 .emit => b.fmt("-f{s}", .{arg_name}),
303 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
304 };
305 }
306};
307
308pub fn create(builder: *std.Build, options: Options) *CompileStep {
309 const name = builder.dupe(options.name);
310 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
311 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
312 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
313 }
314
315 const self = builder.allocator.create(CompileStep) catch @panic("OOM");
316 self.* = CompileStep{
317 .strip = null,
318 .unwind_tables = null,
319 .builder = builder,
320 .verbose_link = false,
321 .verbose_cc = false,
322 .optimize = options.optimize,
323 .target = options.target,
324 .linkage = options.linkage,
325 .kind = options.kind,
326 .root_src = root_src,
327 .name = name,
328 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
329 .step = Step.init(base_id, name, builder.allocator, make),
330 .version = options.version,
331 .out_filename = undefined,
332 .out_h_filename = builder.fmt("{s}.h", .{name}),
333 .out_lib_filename = undefined,
334 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
335 .major_only_filename = null,
336 .name_only_filename = null,
337 .packages = ArrayList(Pkg).init(builder.allocator),
338 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
339 .link_objects = ArrayList(LinkObject).init(builder.allocator),
340 .c_macros = ArrayList([]const u8).init(builder.allocator),
341 .lib_paths = ArrayList([]const u8).init(builder.allocator),
342 .rpaths = ArrayList([]const u8).init(builder.allocator),
343 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
344 .installed_headers = ArrayList(*Step).init(builder.allocator),
345 .object_src = undefined,
346 .c_std = std.Build.CStd.C99,
347 .override_lib_dir = null,
348 .main_pkg_path = null,
349 .exec_cmd_args = null,
350 .name_prefix = "",
351 .filter = null,
352 .test_runner = null,
353 .disable_stack_probing = false,
354 .disable_sanitize_c = false,
355 .sanitize_thread = false,
356 .rdynamic = false,
357 .output_dir = null,
358 .override_dest_dir = null,
359 .installed_path = null,
360 .install_step = null,
361
362 .output_path_source = GeneratedFile{ .step = &self.step },
363 .output_lib_path_source = GeneratedFile{ .step = &self.step },
364 .output_h_path_source = GeneratedFile{ .step = &self.step },
365 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
366
367 .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"),
368 };
369 self.computeOutFileNames();
370 if (root_src) |rs| rs.addStepDependencies(&self.step);
371 return self;
372}
373
374fn computeOutFileNames(self: *CompileStep) void {
375 const target = self.target_info.target;
376
377 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
378 .root_name = self.name,
379 .target = target,
380 .output_mode = switch (self.kind) {
381 .lib => .Lib,
382 .obj => .Obj,
383 .exe, .@"test", .test_exe => .Exe,
384 },
385 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
386 .dynamic => .Dynamic,
387 .static => .Static,
388 }) else null,
389 .version = self.version,
390 }) catch @panic("OOM");
391
392 if (self.kind == .lib) {
393 if (self.linkage != null and self.linkage.? == .static) {
394 self.out_lib_filename = self.out_filename;
395 } else if (self.version) |version| {
396 if (target.isDarwin()) {
397 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
398 self.name,
399 version.major,
400 });
401 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
402 self.out_lib_filename = self.out_filename;
403 } else if (target.os.tag == .windows) {
404 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
405 } else {
406 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
407 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
408 self.out_lib_filename = self.out_filename;
409 }
410 } else {
411 if (target.isDarwin()) {
412 self.out_lib_filename = self.out_filename;
413 } else if (target.os.tag == .windows) {
414 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
415 } else {
416 self.out_lib_filename = self.out_filename;
417 }
418 }
419 if (self.output_dir != null) {
420 self.output_lib_path_source.path = self.builder.pathJoin(
421 &.{ self.output_dir.?, self.out_lib_filename },
422 );
423 }
424 }
425}
426
427pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
428 self.output_dir = self.builder.dupePath(dir);
429}
430
431pub fn install(self: *CompileStep) void {
432 self.builder.installArtifact(self);
433}
434
435pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
436 return self.builder.installRaw(self, dest_filename, options);
437}
438
439pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
440 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
441 a.builder.getInstallStep().dependOn(&install_file.step);
442 a.installed_headers.append(&install_file.step) catch @panic("OOM");
443}
444
445pub fn installHeadersDirectory(
446 a: *CompileStep,
447 src_dir_path: []const u8,
448 dest_rel_path: []const u8,
449) void {
450 return installHeadersDirectoryOptions(a, .{
451 .source_dir = src_dir_path,
452 .install_dir = .header,
453 .install_subdir = dest_rel_path,
454 });
455}
456
457pub fn installHeadersDirectoryOptions(
458 a: *CompileStep,
459 options: std.Build.InstallDirStep.Options,
460) void {
461 const install_dir = a.builder.addInstallDirectory(options);
462 a.builder.getInstallStep().dependOn(&install_dir.step);
463 a.installed_headers.append(&install_dir.step) catch @panic("OOM");
464}
465
466pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
467 assert(l.kind == .lib);
468 const install_step = a.builder.getInstallStep();
469 // Copy each element from installed_headers, modifying the builder
470 // to be the new parent's builder.
471 for (l.installed_headers.items) |step| {
472 const step_copy = switch (step.id) {
473 inline .install_file, .install_dir => |id| blk: {
474 const T = id.Type();
475 const ptr = a.builder.allocator.create(T) catch @panic("OOM");
476 ptr.* = step.cast(T).?.*;
477 ptr.override_source_builder = ptr.builder;
478 ptr.builder = a.builder;
479 break :blk &ptr.step;
480 },
481 else => unreachable,
482 };
483 a.installed_headers.append(step_copy) catch @panic("OOM");
484 install_step.dependOn(step_copy);
485 }
486 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
487}
488
489/// Creates a `RunStep` with an executable built with `addExecutable`.
490/// Add command line arguments with `addArg`.
491pub fn run(exe: *CompileStep) *RunStep {
492 assert(exe.kind == .exe or exe.kind == .test_exe);
493
494 // It doesn't have to be native. We catch that if you actually try to run it.
495 // Consider that this is declarative; the run step may not be run unless a user
496 // option is supplied.
497 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
498 run_step.addArtifactArg(exe);
499
500 if (exe.kind == .test_exe) {
501 run_step.addArg(exe.builder.zig_exe);
502 }
503
504 if (exe.vcpkg_bin_path) |path| {
505 run_step.addPathDir(path);
506 }
507
508 return run_step;
509}
510
511/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
512/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
513/// When a binary cannot be ran through emulation or the option is disabled, a warning
514/// will be printed and the binary will *NOT* be ran.
515pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
516 assert(exe.kind == .exe or exe.kind == .test_exe);
517
518 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
519 if (exe.vcpkg_bin_path) |path| {
520 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
521 }
522 return run_step;
523}
524
525pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
526 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
527}
528
529pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
530 self.linker_script = source.dupe(self.builder);
531 source.addStepDependencies(&self.step);
532}
533
534pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
535 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");
536}
537
538pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
539 self.frameworks.put(self.builder.dupe(framework_name), .{
540 .needed = true,
541 }) catch @panic("OOM");
542}
543
544pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
545 self.frameworks.put(self.builder.dupe(framework_name), .{
546 .weak = true,
547 }) catch @panic("OOM");
548}
549
550/// Returns whether the library, executable, or object depends on a particular system library.
551pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool {
552 if (isLibCLibrary(name)) {
553 return self.is_linking_libc;
554 }
555 if (isLibCppLibrary(name)) {
556 return self.is_linking_libcpp;
557 }
558 for (self.link_objects.items) |link_object| {
559 switch (link_object) {
560 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
561 else => continue,
562 }
563 }
564 return false;
565}
566
567pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void {
568 assert(lib.kind == .lib);
569 self.linkLibraryOrObject(lib);
570}
571
572pub fn isDynamicLibrary(self: *CompileStep) bool {
573 return self.kind == .lib and self.linkage == Linkage.dynamic;
574}
575
576pub fn isStaticLibrary(self: *CompileStep) bool {
577 return self.kind == .lib and self.linkage != Linkage.dynamic;
578}
579
580pub fn producesPdbFile(self: *CompileStep) bool {
581 if (!self.target.isWindows() and !self.target.isUefi()) return false;
582 if (self.target.getObjectFormat() == .c) return false;
583 if (self.strip == true) return false;
584 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
585}
586
587pub fn linkLibC(self: *CompileStep) void {
588 self.is_linking_libc = true;
589}
590
591pub fn linkLibCpp(self: *CompileStep) void {
592 self.is_linking_libcpp = true;
593}
594
595/// If the value is omitted, it is set to 1.
596/// `name` and `value` need not live longer than the function call.
597pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
598 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
599 self.c_macros.append(macro) catch @panic("OOM");
600}
601
602/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
603pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
604 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
605}
606
607/// This one has no integration with anything, it just puts -lname on the command line.
608/// Prefer to use `linkSystemLibrary` instead.
609pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
610 self.link_objects.append(.{
611 .system_lib = .{
612 .name = self.builder.dupe(name),
613 .needed = false,
614 .weak = false,
615 .use_pkg_config = .no,
616 },
617 }) catch @panic("OOM");
618}
619
620/// This one has no integration with anything, it just puts -needed-lname on the command line.
621/// Prefer to use `linkSystemLibraryNeeded` instead.
622pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
623 self.link_objects.append(.{
624 .system_lib = .{
625 .name = self.builder.dupe(name),
626 .needed = true,
627 .weak = false,
628 .use_pkg_config = .no,
629 },
630 }) catch @panic("OOM");
631}
632
633/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
634/// command line. Prefer to use `linkSystemLibraryWeak` instead.
635pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
636 self.link_objects.append(.{
637 .system_lib = .{
638 .name = self.builder.dupe(name),
639 .needed = false,
640 .weak = true,
641 .use_pkg_config = .no,
642 },
643 }) catch @panic("OOM");
644}
645
646/// This links against a system library, exclusively using pkg-config to find the library.
647/// Prefer to use `linkSystemLibrary` instead.
648pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
649 self.link_objects.append(.{
650 .system_lib = .{
651 .name = self.builder.dupe(lib_name),
652 .needed = false,
653 .weak = false,
654 .use_pkg_config = .force,
655 },
656 }) catch @panic("OOM");
657}
658
659/// This links against a system library, exclusively using pkg-config to find the library.
660/// Prefer to use `linkSystemLibraryNeeded` instead.
661pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
662 self.link_objects.append(.{
663 .system_lib = .{
664 .name = self.builder.dupe(lib_name),
665 .needed = true,
666 .weak = false,
667 .use_pkg_config = .force,
668 },
669 }) catch @panic("OOM");
670}
671
672/// Run pkg-config for the given library name and parse the output, returning the arguments
673/// that should be passed to zig to link the given library.
674pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
675 const pkg_name = match: {
676 // First we have to map the library name to pkg config name. Unfortunately,
677 // there are several examples where this is not straightforward:
678 // -lSDL2 -> pkg-config sdl2
679 // -lgdk-3 -> pkg-config gdk-3.0
680 // -latk-1.0 -> pkg-config atk
681 const pkgs = try getPkgConfigList(self.builder);
682
683 // Exact match means instant winner.
684 for (pkgs) |pkg| {
685 if (mem.eql(u8, pkg.name, lib_name)) {
686 break :match pkg.name;
687 }
688 }
689
690 // Next we'll try ignoring case.
691 for (pkgs) |pkg| {
692 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
693 break :match pkg.name;
694 }
695 }
696
697 // Now try appending ".0".
698 for (pkgs) |pkg| {
699 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
700 if (pos != 0) continue;
701 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
702 break :match pkg.name;
703 }
704 }
705 }
706
707 // Trimming "-1.0".
708 if (mem.endsWith(u8, lib_name, "-1.0")) {
709 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
710 for (pkgs) |pkg| {
711 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
712 break :match pkg.name;
713 }
714 }
715 }
716
717 return error.PackageNotFound;
718 };
719
720 var code: u8 = undefined;
721 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
722 "pkg-config",
723 pkg_name,
724 "--cflags",
725 "--libs",
726 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
727 error.ProcessTerminated => return error.PkgConfigCrashed,
728 error.ExecNotSupported => return error.PkgConfigFailed,
729 error.ExitCodeFailure => return error.PkgConfigFailed,
730 error.FileNotFound => return error.PkgConfigNotInstalled,
731 error.ChildExecFailed => return error.PkgConfigFailed,
732 else => return err,
733 };
734
735 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
736 defer zig_args.deinit();
737
738 var it = mem.tokenize(u8, stdout, " \r\n\t");
739 while (it.next()) |tok| {
740 if (mem.eql(u8, tok, "-I")) {
741 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
742 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
743 } else if (mem.startsWith(u8, tok, "-I")) {
744 try zig_args.append(tok);
745 } else if (mem.eql(u8, tok, "-L")) {
746 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
747 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
748 } else if (mem.startsWith(u8, tok, "-L")) {
749 try zig_args.append(tok);
750 } else if (mem.eql(u8, tok, "-l")) {
751 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
752 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
753 } else if (mem.startsWith(u8, tok, "-l")) {
754 try zig_args.append(tok);
755 } else if (mem.eql(u8, tok, "-D")) {
756 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
757 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
758 } else if (mem.startsWith(u8, tok, "-D")) {
759 try zig_args.append(tok);
760 } else if (self.builder.verbose) {
761 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
762 }
763 }
764
765 return zig_args.toOwnedSlice();
766}
767
768pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void {
769 self.linkSystemLibraryInner(name, .{});
770}
771
772pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void {
773 self.linkSystemLibraryInner(name, .{ .needed = true });
774}
775
776pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void {
777 self.linkSystemLibraryInner(name, .{ .weak = true });
778}
779
780fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
781 needed: bool = false,
782 weak: bool = false,
783}) void {
784 if (isLibCLibrary(name)) {
785 self.linkLibC();
786 return;
787 }
788 if (isLibCppLibrary(name)) {
789 self.linkLibCpp();
790 return;
791 }
792
793 self.link_objects.append(.{
794 .system_lib = .{
795 .name = self.builder.dupe(name),
796 .needed = opts.needed,
797 .weak = opts.weak,
798 .use_pkg_config = .yes,
799 },
800 }) catch @panic("OOM");
801}
802
803pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
804 assert(self.kind == .@"test" or self.kind == .test_exe);
805 self.name_prefix = self.builder.dupe(text);
806}
807
808pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
809 assert(self.kind == .@"test" or self.kind == .test_exe);
810 self.filter = if (text) |t| self.builder.dupe(t) else null;
811}
812
813pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
814 assert(self.kind == .@"test" or self.kind == .test_exe);
815 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
816}
817
818/// Handy when you have many C/C++ source files and want them all to have the same flags.
819pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
820 const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");
821
822 const files_copy = self.builder.dupeStrings(files);
823 const flags_copy = self.builder.dupeStrings(flags);
824
825 c_source_files.* = .{
826 .files = files_copy,
827 .flags = flags_copy,
828 };
829 self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM");
830}
831
832pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void {
833 self.addCSourceFileSource(.{
834 .args = flags,
835 .source = .{ .path = file },
836 });
837}
838
839pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
840 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");
841 c_source_file.* = source.dupe(self.builder);
842 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
843 source.source.addStepDependencies(&self.step);
844}
845
846pub fn setVerboseLink(self: *CompileStep, value: bool) void {
847 self.verbose_link = value;
848}
849
850pub fn setVerboseCC(self: *CompileStep, value: bool) void {
851 self.verbose_cc = value;
852}
853
854pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
855 self.override_lib_dir = self.builder.dupePath(dir_path);
856}
857
858pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
859 self.main_pkg_path = self.builder.dupePath(dir_path);
860}
861
862pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
863 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
864}
865
866/// Returns the generated executable, library or object file.
867/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
868pub fn getOutputSource(self: *CompileStep) FileSource {
869 return FileSource{ .generated = &self.output_path_source };
870}
871
872/// Returns the generated import library. This function can only be called for libraries.
873pub fn getOutputLibSource(self: *CompileStep) FileSource {
874 assert(self.kind == .lib);
875 return FileSource{ .generated = &self.output_lib_path_source };
876}
877
878/// Returns the generated header file.
879/// This function can only be called for libraries or object files which have `emit_h` set.
880pub fn getOutputHSource(self: *CompileStep) FileSource {
881 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
882 assert(self.emit_h);
883 return FileSource{ .generated = &self.output_h_path_source };
884}
885
886/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
887pub fn getOutputPdbSource(self: *CompileStep) FileSource {
888 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
889 assert(self.target.isWindows() or self.target.isUefi());
890 return FileSource{ .generated = &self.output_pdb_path_source };
891}
892
893pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
894 self.link_objects.append(.{
895 .assembly_file = .{ .path = self.builder.dupe(path) },
896 }) catch @panic("OOM");
897}
898
899pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
900 const source_duped = source.dupe(self.builder);
901 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
902 source_duped.addStepDependencies(&self.step);
903}
904
905pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
906 self.addObjectFileSource(.{ .path = source_file });
907}
908
909pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
910 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");
911 source.addStepDependencies(&self.step);
912}
913
914pub fn addObject(self: *CompileStep, obj: *CompileStep) void {
915 assert(obj.kind == .obj);
916 self.linkLibraryOrObject(obj);
917}
918
919pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
920pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
921pub const addLibPath = @compileError("deprecated, use addLibraryPath");
922pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
923
924pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
925 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");
926}
927
928pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
929 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");
930}
931
932pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
933 self.step.dependOn(&config_header.step);
934 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
935}
936
937pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
938 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");
939}
940
941pub fn addRPath(self: *CompileStep, path: []const u8) void {
942 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");
943}
944
945pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
946 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
947}
948
949pub fn addPackage(self: *CompileStep, package: Pkg) void {
950 self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM");
951 self.addRecursiveBuildDeps(package);
952}
953
954pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void {
955 self.addPackage(options.getPackage(package_name));
956}
957
958fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void {
959 package.source.addStepDependencies(&self.step);
960 if (package.dependencies) |deps| {
961 for (deps) |dep| {
962 self.addRecursiveBuildDeps(dep);
963 }
964 }
965}
966
967pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void {
968 self.addPackage(Pkg{
969 .name = self.builder.dupe(name),
970 .source = .{ .path = self.builder.dupe(pkg_index_path) },
971 });
972}
973
974/// If Vcpkg was found on the system, it will be added to include and lib
975/// paths for the specified target.
976pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
977 // Ideally in the Unattempted case we would call the function recursively
978 // after findVcpkgRoot and have only one switch statement, but the compiler
979 // cannot resolve the error set.
980 switch (self.builder.vcpkg_root) {
981 .unattempted => {
982 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
983 VcpkgRoot{ .found = root }
984 else
985 .not_found;
986 },
987 .not_found => return error.VcpkgNotFound,
988 .found => {},
989 }
990
991 switch (self.builder.vcpkg_root) {
992 .unattempted => unreachable,
993 .not_found => return error.VcpkgNotFound,
994 .found => |root| {
995 const allocator = self.builder.allocator;
996 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
997 defer self.builder.allocator.free(triplet);
998
999 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1000 errdefer allocator.free(include_path);
1001 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1002
1003 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1004 try self.lib_paths.append(lib_path);
1005
1006 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1007 },
1008 }
1009}
1010
1011pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1012 assert(self.kind == .@"test");
1013 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1014 for (args) |arg, i| {
1015 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1016 }
1017 self.exec_cmd_args = duped_args;
1018}
1019
1020fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
1021 self.step.dependOn(&other.step);
1022 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
1023 self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
1024}
1025
1026fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1027 const builder = self.builder;
1028
1029 try zig_args.append("--pkg-begin");
1030 try zig_args.append(pkg.name);
1031 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1032
1033 if (pkg.dependencies) |dependencies| {
1034 for (dependencies) |sub_pkg| {
1035 try self.makePackageCmd(sub_pkg, zig_args);
1036 }
1037 }
1038
1039 try zig_args.append("--pkg-end");
1040}
1041
1042fn make(step: *Step) !void {
1043 const self = @fieldParentPtr(CompileStep, "step", step);
1044 const builder = self.builder;
1045
1046 if (self.root_src == null and self.link_objects.items.len == 0) {
1047 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1048 return error.NeedAnObject;
1049 }
1050
1051 var zig_args = ArrayList([]const u8).init(builder.allocator);
1052 defer zig_args.deinit();
1053
1054 try zig_args.append(builder.zig_exe);
1055
1056 const cmd = switch (self.kind) {
1057 .lib => "build-lib",
1058 .exe => "build-exe",
1059 .obj => "build-obj",
1060 .@"test" => "test",
1061 .test_exe => "test",
1062 };
1063 try zig_args.append(cmd);
1064
1065 if (builder.color != .auto) {
1066 try zig_args.append("--color");
1067 try zig_args.append(@tagName(builder.color));
1068 }
1069
1070 if (builder.reference_trace) |some| {
1071 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1072 }
1073
1074 try addFlag(&zig_args, "LLVM", self.use_llvm);
1075 try addFlag(&zig_args, "LLD", self.use_lld);
1076
1077 if (self.target.ofmt) |ofmt| {
1078 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1079 }
1080
1081 if (self.entry_symbol_name) |entry| {
1082 try zig_args.append("--entry");
1083 try zig_args.append(entry);
1084 }
1085
1086 if (self.stack_size) |stack_size| {
1087 try zig_args.append("--stack");
1088 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1089 }
1090
1091 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1092
1093 // We will add link objects from transitive dependencies, but we want to keep
1094 // all link objects in the same order provided.
1095 // This array is used to keep self.link_objects immutable.
1096 var transitive_deps: TransitiveDeps = .{
1097 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1098 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1099 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1100 .is_linking_libcpp = self.is_linking_libcpp,
1101 .is_linking_libc = self.is_linking_libc,
1102 .frameworks = &self.frameworks,
1103 };
1104
1105 try transitive_deps.seen_steps.put(&self.step, {});
1106 try transitive_deps.add(self.link_objects.items);
1107
1108 var prev_has_extra_flags = false;
1109
1110 for (transitive_deps.link_objects.items) |link_object| {
1111 switch (link_object) {
1112 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1113
1114 .other_step => |other| switch (other.kind) {
1115 .exe => @panic("Cannot link with an executable build artifact"),
1116 .test_exe => @panic("Cannot link with an executable build artifact"),
1117 .@"test" => @panic("Cannot link with a test"),
1118 .obj => {
1119 try zig_args.append(other.getOutputSource().getPath(builder));
1120 },
1121 .lib => l: {
1122 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1123 // Avoid putting a static library inside a static library.
1124 break :l;
1125 }
1126
1127 const full_path_lib = other.getOutputLibSource().getPath(builder);
1128 try zig_args.append(full_path_lib);
1129
1130 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1131 if (fs.path.dirname(full_path_lib)) |dirname| {
1132 try zig_args.append("-rpath");
1133 try zig_args.append(dirname);
1134 }
1135 }
1136 },
1137 },
1138
1139 .system_lib => |system_lib| {
1140 const prefix: []const u8 = prefix: {
1141 if (system_lib.needed) break :prefix "-needed-l";
1142 if (system_lib.weak) {
1143 if (self.target.isDarwin()) break :prefix "-weak-l";
1144 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1145 }
1146 break :prefix "-l";
1147 };
1148 switch (system_lib.use_pkg_config) {
1149 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1150 .yes, .force => {
1151 if (self.runPkgConfig(system_lib.name)) |args| {
1152 try zig_args.appendSlice(args);
1153 } else |err| switch (err) {
1154 error.PkgConfigInvalidOutput,
1155 error.PkgConfigCrashed,
1156 error.PkgConfigFailed,
1157 error.PkgConfigNotInstalled,
1158 error.PackageNotFound,
1159 => switch (system_lib.use_pkg_config) {
1160 .yes => {
1161 // pkg-config failed, so fall back to linking the library
1162 // by name directly.
1163 try zig_args.append(builder.fmt("{s}{s}", .{
1164 prefix,
1165 system_lib.name,
1166 }));
1167 },
1168 .force => {
1169 panic("pkg-config failed for library {s}", .{system_lib.name});
1170 },
1171 .no => unreachable,
1172 },
1173
1174 else => |e| return e,
1175 }
1176 },
1177 }
1178 },
1179
1180 .assembly_file => |asm_file| {
1181 if (prev_has_extra_flags) {
1182 try zig_args.append("-extra-cflags");
1183 try zig_args.append("--");
1184 prev_has_extra_flags = false;
1185 }
1186 try zig_args.append(asm_file.getPath(builder));
1187 },
1188
1189 .c_source_file => |c_source_file| {
1190 if (c_source_file.args.len == 0) {
1191 if (prev_has_extra_flags) {
1192 try zig_args.append("-cflags");
1193 try zig_args.append("--");
1194 prev_has_extra_flags = false;
1195 }
1196 } else {
1197 try zig_args.append("-cflags");
1198 for (c_source_file.args) |arg| {
1199 try zig_args.append(arg);
1200 }
1201 try zig_args.append("--");
1202 }
1203 try zig_args.append(c_source_file.source.getPath(builder));
1204 },
1205
1206 .c_source_files => |c_source_files| {
1207 if (c_source_files.flags.len == 0) {
1208 if (prev_has_extra_flags) {
1209 try zig_args.append("-cflags");
1210 try zig_args.append("--");
1211 prev_has_extra_flags = false;
1212 }
1213 } else {
1214 try zig_args.append("-cflags");
1215 for (c_source_files.flags) |flag| {
1216 try zig_args.append(flag);
1217 }
1218 try zig_args.append("--");
1219 }
1220 for (c_source_files.files) |file| {
1221 try zig_args.append(builder.pathFromRoot(file));
1222 }
1223 },
1224 }
1225 }
1226
1227 if (transitive_deps.is_linking_libcpp) {
1228 try zig_args.append("-lc++");
1229 }
1230
1231 if (transitive_deps.is_linking_libc) {
1232 try zig_args.append("-lc");
1233 }
1234
1235 if (self.image_base) |image_base| {
1236 try zig_args.append("--image-base");
1237 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1238 }
1239
1240 if (self.filter) |filter| {
1241 try zig_args.append("--test-filter");
1242 try zig_args.append(filter);
1243 }
1244
1245 if (self.test_evented_io) {
1246 try zig_args.append("--test-evented-io");
1247 }
1248
1249 if (self.name_prefix.len != 0) {
1250 try zig_args.append("--test-name-prefix");
1251 try zig_args.append(self.name_prefix);
1252 }
1253
1254 if (self.test_runner) |test_runner| {
1255 try zig_args.append("--test-runner");
1256 try zig_args.append(builder.pathFromRoot(test_runner));
1257 }
1258
1259 for (builder.debug_log_scopes) |log_scope| {
1260 try zig_args.append("--debug-log");
1261 try zig_args.append(log_scope);
1262 }
1263
1264 if (builder.debug_compile_errors) {
1265 try zig_args.append("--debug-compile-errors");
1266 }
1267
1268 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");
1269 if (builder.verbose_air) try zig_args.append("--verbose-air");
1270 if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1271 if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1272 if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
1273 if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
1274
1275 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1276 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1277 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1278 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1279 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1280 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1281 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1282
1283 if (self.emit_h) try zig_args.append("-femit-h");
1284
1285 try addFlag(&zig_args, "strip", self.strip);
1286 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1287
1288 switch (self.compress_debug_sections) {
1289 .none => {},
1290 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1291 }
1292
1293 if (self.link_eh_frame_hdr) {
1294 try zig_args.append("--eh-frame-hdr");
1295 }
1296 if (self.link_emit_relocs) {
1297 try zig_args.append("--emit-relocs");
1298 }
1299 if (self.link_function_sections) {
1300 try zig_args.append("-ffunction-sections");
1301 }
1302 if (self.link_gc_sections) |x| {
1303 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1304 }
1305 if (self.linker_allow_shlib_undefined) |x| {
1306 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1307 }
1308 if (self.link_z_notext) {
1309 try zig_args.append("-z");
1310 try zig_args.append("notext");
1311 }
1312 if (!self.link_z_relro) {
1313 try zig_args.append("-z");
1314 try zig_args.append("norelro");
1315 }
1316 if (self.link_z_lazy) {
1317 try zig_args.append("-z");
1318 try zig_args.append("lazy");
1319 }
1320 if (self.link_z_common_page_size) |size| {
1321 try zig_args.append("-z");
1322 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1323 }
1324 if (self.link_z_max_page_size) |size| {
1325 try zig_args.append("-z");
1326 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1327 }
1328
1329 if (self.libc_file) |libc_file| {
1330 try zig_args.append("--libc");
1331 try zig_args.append(libc_file.getPath(builder));
1332 } else if (builder.libc_file) |libc_file| {
1333 try zig_args.append("--libc");
1334 try zig_args.append(libc_file);
1335 }
1336
1337 switch (self.optimize) {
1338 .Debug => {}, // Skip since it's the default.
1339 else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),
1340 }
1341
1342 try zig_args.append("--cache-dir");
1343 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1344
1345 try zig_args.append("--global-cache-dir");
1346 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1347
1348 try zig_args.append("--name");
1349 try zig_args.append(self.name);
1350
1351 if (self.linkage) |some| switch (some) {
1352 .dynamic => try zig_args.append("-dynamic"),
1353 .static => try zig_args.append("-static"),
1354 };
1355 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1356 if (self.version) |version| {
1357 try zig_args.append("--version");
1358 try zig_args.append(builder.fmt("{}", .{version}));
1359 }
1360
1361 if (self.target.isDarwin()) {
1362 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1363 self.target.libPrefix(),
1364 self.name,
1365 self.target.dynamicLibSuffix(),
1366 });
1367 try zig_args.append("-install_name");
1368 try zig_args.append(install_name);
1369 }
1370 }
1371
1372 if (self.entitlements) |entitlements| {
1373 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1374 }
1375 if (self.pagezero_size) |pagezero_size| {
1376 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1377 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1378 }
1379 if (self.search_strategy) |strat| switch (strat) {
1380 .paths_first => try zig_args.append("-search_paths_first"),
1381 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1382 };
1383 if (self.headerpad_size) |headerpad_size| {
1384 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1385 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1386 }
1387 if (self.headerpad_max_install_names) {
1388 try zig_args.append("-headerpad_max_install_names");
1389 }
1390 if (self.dead_strip_dylibs) {
1391 try zig_args.append("-dead_strip_dylibs");
1392 }
1393
1394 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1395 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1396 if (self.disable_stack_probing) {
1397 try zig_args.append("-fno-stack-check");
1398 }
1399 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1400 if (self.red_zone) |red_zone| {
1401 if (red_zone) {
1402 try zig_args.append("-mred-zone");
1403 } else {
1404 try zig_args.append("-mno-red-zone");
1405 }
1406 }
1407 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1408 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1409
1410 if (self.disable_sanitize_c) {
1411 try zig_args.append("-fno-sanitize-c");
1412 }
1413 if (self.sanitize_thread) {
1414 try zig_args.append("-fsanitize-thread");
1415 }
1416 if (self.rdynamic) {
1417 try zig_args.append("-rdynamic");
1418 }
1419 if (self.import_memory) {
1420 try zig_args.append("--import-memory");
1421 }
1422 if (self.import_symbols) {
1423 try zig_args.append("--import-symbols");
1424 }
1425 if (self.import_table) {
1426 try zig_args.append("--import-table");
1427 }
1428 if (self.export_table) {
1429 try zig_args.append("--export-table");
1430 }
1431 if (self.initial_memory) |initial_memory| {
1432 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1433 }
1434 if (self.max_memory) |max_memory| {
1435 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1436 }
1437 if (self.shared_memory) {
1438 try zig_args.append("--shared-memory");
1439 }
1440 if (self.global_base) |global_base| {
1441 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1442 }
1443
1444 if (self.code_model != .default) {
1445 try zig_args.append("-mcmodel");
1446 try zig_args.append(@tagName(self.code_model));
1447 }
1448 if (self.wasi_exec_model) |model| {
1449 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1450 }
1451 for (self.export_symbol_names) |symbol_name| {
1452 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1453 }
1454
1455 if (!self.target.isNative()) {
1456 try zig_args.appendSlice(&.{
1457 "-target", try self.target.zigTriple(builder.allocator),
1458 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
1459 });
1460
1461 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1462 try zig_args.append("--dynamic-linker");
1463 try zig_args.append(dynamic_linker);
1464 }
1465 }
1466
1467 if (self.linker_script) |linker_script| {
1468 try zig_args.append("--script");
1469 try zig_args.append(linker_script.getPath(builder));
1470 }
1471
1472 if (self.version_script) |version_script| {
1473 try zig_args.append("--version-script");
1474 try zig_args.append(builder.pathFromRoot(version_script));
1475 }
1476
1477 if (self.kind == .@"test") {
1478 if (self.exec_cmd_args) |exec_cmd_args| {
1479 for (exec_cmd_args) |cmd_arg| {
1480 if (cmd_arg) |arg| {
1481 try zig_args.append("--test-cmd");
1482 try zig_args.append(arg);
1483 } else {
1484 try zig_args.append("--test-cmd-bin");
1485 }
1486 }
1487 } else {
1488 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1489
1490 switch (builder.host.getExternalExecutor(self.target_info, .{
1491 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1492 .link_libc = transitive_deps.is_linking_libc,
1493 })) {
1494 .native => {},
1495 .bad_dl, .bad_os_or_cpu => {
1496 try zig_args.append("--test-no-exec");
1497 },
1498 .rosetta => if (builder.enable_rosetta) {
1499 try zig_args.append("--test-cmd-bin");
1500 } else {
1501 try zig_args.append("--test-no-exec");
1502 },
1503 .qemu => |bin_name| ok: {
1504 if (builder.enable_qemu) qemu: {
1505 const glibc_dir_arg = if (need_cross_glibc)
1506 builder.glibc_runtimes_dir orelse break :qemu
1507 else
1508 null;
1509 try zig_args.append("--test-cmd");
1510 try zig_args.append(bin_name);
1511 if (glibc_dir_arg) |dir| {
1512 // TODO look into making this a call to `linuxTriple`. This
1513 // needs the directory to be called "i686" rather than
1514 // "x86" which is why we do it manually here.
1515 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1516 const cpu_arch = self.target.getCpuArch();
1517 const os_tag = self.target.getOsTag();
1518 const abi = self.target.getAbi();
1519 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1520 "i686"
1521 else
1522 @tagName(cpu_arch);
1523 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1524 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1525 });
1526
1527 try zig_args.append("--test-cmd");
1528 try zig_args.append("-L");
1529 try zig_args.append("--test-cmd");
1530 try zig_args.append(full_dir);
1531 }
1532 try zig_args.append("--test-cmd-bin");
1533 break :ok;
1534 }
1535 try zig_args.append("--test-no-exec");
1536 },
1537 .wine => |bin_name| if (builder.enable_wine) {
1538 try zig_args.append("--test-cmd");
1539 try zig_args.append(bin_name);
1540 try zig_args.append("--test-cmd-bin");
1541 } else {
1542 try zig_args.append("--test-no-exec");
1543 },
1544 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1545 try zig_args.append("--test-cmd");
1546 try zig_args.append(bin_name);
1547 try zig_args.append("--test-cmd");
1548 try zig_args.append("--dir=.");
1549 try zig_args.append("--test-cmd-bin");
1550 } else {
1551 try zig_args.append("--test-no-exec");
1552 },
1553 .darling => |bin_name| if (builder.enable_darling) {
1554 try zig_args.append("--test-cmd");
1555 try zig_args.append(bin_name);
1556 try zig_args.append("--test-cmd-bin");
1557 } else {
1558 try zig_args.append("--test-no-exec");
1559 },
1560 }
1561 }
1562 } else if (self.kind == .test_exe) {
1563 try zig_args.append("--test-no-exec");
1564 }
1565
1566 for (self.packages.items) |pkg| {
1567 try self.makePackageCmd(pkg, &zig_args);
1568 }
1569
1570 for (self.include_dirs.items) |include_dir| {
1571 switch (include_dir) {
1572 .raw_path => |include_path| {
1573 try zig_args.append("-I");
1574 try zig_args.append(builder.pathFromRoot(include_path));
1575 },
1576 .raw_path_system => |include_path| {
1577 if (builder.sysroot != null) {
1578 try zig_args.append("-iwithsysroot");
1579 } else {
1580 try zig_args.append("-isystem");
1581 }
1582
1583 const resolved_include_path = builder.pathFromRoot(include_path);
1584
1585 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1586 // We need to check for disk designator and strip it out from dir path so
1587 // that zig/clang can concat resolved_include_path with sysroot.
1588 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1589
1590 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1591 break :blk resolved_include_path[where + disk_designator.len ..];
1592 }
1593
1594 break :blk resolved_include_path;
1595 } else resolved_include_path;
1596
1597 try zig_args.append(common_include_path);
1598 },
1599 .other_step => |other| {
1600 if (other.emit_h) {
1601 const h_path = other.getOutputHSource().getPath(builder);
1602 try zig_args.append("-isystem");
1603 try zig_args.append(fs.path.dirname(h_path).?);
1604 }
1605 if (other.installed_headers.items.len > 0) {
1606 for (other.installed_headers.items) |install_step| {
1607 try install_step.make();
1608 }
1609 try zig_args.append("-I");
1610 try zig_args.append(builder.pathJoin(&.{
1611 other.builder.install_prefix, "include",
1612 }));
1613 }
1614 },
1615 .config_header_step => |config_header| {
1616 try zig_args.append("-I");
1617 try zig_args.append(config_header.output_dir);
1618 },
1619 }
1620 }
1621
1622 for (self.lib_paths.items) |lib_path| {
1623 try zig_args.append("-L");
1624 try zig_args.append(lib_path);
1625 }
1626
1627 for (self.rpaths.items) |rpath| {
1628 try zig_args.append("-rpath");
1629 try zig_args.append(rpath);
1630 }
1631
1632 for (self.c_macros.items) |c_macro| {
1633 try zig_args.append("-D");
1634 try zig_args.append(c_macro);
1635 }
1636
1637 if (self.target.isDarwin()) {
1638 for (self.framework_dirs.items) |dir| {
1639 if (builder.sysroot != null) {
1640 try zig_args.append("-iframeworkwithsysroot");
1641 } else {
1642 try zig_args.append("-iframework");
1643 }
1644 try zig_args.append(dir);
1645 try zig_args.append("-F");
1646 try zig_args.append(dir);
1647 }
1648
1649 var it = self.frameworks.iterator();
1650 while (it.next()) |entry| {
1651 const name = entry.key_ptr.*;
1652 const info = entry.value_ptr.*;
1653 if (info.needed) {
1654 try zig_args.append("-needed_framework");
1655 } else if (info.weak) {
1656 try zig_args.append("-weak_framework");
1657 } else {
1658 try zig_args.append("-framework");
1659 }
1660 try zig_args.append(name);
1661 }
1662 } else {
1663 if (self.framework_dirs.items.len > 0) {
1664 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1665 }
1666
1667 if (self.frameworks.count() > 0) {
1668 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1669 }
1670 }
1671
1672 if (builder.sysroot) |sysroot| {
1673 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1674 }
1675
1676 for (builder.search_prefixes.items) |search_prefix| {
1677 try zig_args.append("-L");
1678 try zig_args.append(builder.pathJoin(&.{
1679 search_prefix, "lib",
1680 }));
1681 try zig_args.append("-I");
1682 try zig_args.append(builder.pathJoin(&.{
1683 search_prefix, "include",
1684 }));
1685 }
1686
1687 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1688 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1689 try addFlag(&zig_args, "build-id", self.build_id);
1690
1691 if (self.override_lib_dir) |dir| {
1692 try zig_args.append("--zig-lib-dir");
1693 try zig_args.append(builder.pathFromRoot(dir));
1694 } else if (builder.override_lib_dir) |dir| {
1695 try zig_args.append("--zig-lib-dir");
1696 try zig_args.append(builder.pathFromRoot(dir));
1697 }
1698
1699 if (self.main_pkg_path) |dir| {
1700 try zig_args.append("--main-pkg-path");
1701 try zig_args.append(builder.pathFromRoot(dir));
1702 }
1703
1704 try addFlag(&zig_args, "PIC", self.force_pic);
1705 try addFlag(&zig_args, "PIE", self.pie);
1706 try addFlag(&zig_args, "lto", self.want_lto);
1707
1708 if (self.subsystem) |subsystem| {
1709 try zig_args.append("--subsystem");
1710 try zig_args.append(switch (subsystem) {
1711 .Console => "console",
1712 .Windows => "windows",
1713 .Posix => "posix",
1714 .Native => "native",
1715 .EfiApplication => "efi_application",
1716 .EfiBootServiceDriver => "efi_boot_service_driver",
1717 .EfiRom => "efi_rom",
1718 .EfiRuntimeDriver => "efi_runtime_driver",
1719 });
1720 }
1721
1722 try zig_args.append("--enable-cache");
1723
1724 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1725 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1726 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1727 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1728 var args_length: usize = 0;
1729 for (zig_args.items) |arg| {
1730 args_length += arg.len + 1; // +1 to account for null terminator
1731 }
1732 if (args_length >= 30 * 1024) {
1733 const args_dir = try fs.path.join(
1734 builder.allocator,
1735 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1736 );
1737 try std.fs.cwd().makePath(args_dir);
1738
1739 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1740 defer args_arena.deinit();
1741
1742 const args_to_escape = zig_args.items[2..];
1743 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1744
1745 arg_blk: for (args_to_escape) |arg| {
1746 for (arg) |c, arg_idx| {
1747 if (c == '\\' or c == '"') {
1748 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1749 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1750 const writer = escaped.writer();
1751 try writer.writeAll(arg[0..arg_idx]);
1752 for (arg[arg_idx..]) |to_escape| {
1753 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1754 try writer.writeByte(to_escape);
1755 }
1756 escaped_args.appendAssumeCapacity(escaped.items);
1757 continue :arg_blk;
1758 }
1759 }
1760 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1761 }
1762
1763 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1764 // other zig build commands running in parallel.
1765 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1766 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1767
1768 var args_hash: [Sha256.digest_length]u8 = undefined;
1769 Sha256.hash(args, &args_hash, .{});
1770 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1771 _ = try std.fmt.bufPrint(
1772 &args_hex_hash,
1773 "{s}",
1774 .{std.fmt.fmtSliceHexLower(&args_hash)},
1775 );
1776
1777 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1778 try std.fs.cwd().writeFile(args_file, args);
1779
1780 zig_args.shrinkRetainingCapacity(2);
1781 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1782 }
1783
1784 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1785 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1786
1787 if (self.output_dir) |output_dir| {
1788 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1789 defer src_dir.close();
1790
1791 // Create the output directory if it doesn't exist.
1792 try std.fs.cwd().makePath(output_dir);
1793
1794 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1795 defer dest_dir.close();
1796
1797 var it = src_dir.iterate();
1798 while (try it.next()) |entry| {
1799 // The compiler can put these files into the same directory, but we don't
1800 // want to copy them over.
1801 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1802 mem.eql(u8, entry.name, "libs.txt") or
1803 mem.eql(u8, entry.name, "builtin.zig") or
1804 mem.eql(u8, entry.name, "zld.id") or
1805 mem.eql(u8, entry.name, "lld.id")) continue;
1806
1807 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1808 }
1809 } else {
1810 self.output_dir = build_output_dir;
1811 }
1812
1813 // This will ensure all output filenames will now have the output_dir available!
1814 self.computeOutFileNames();
1815
1816 // Update generated files
1817 if (self.output_dir != null) {
1818 self.output_path_source.path = builder.pathJoin(
1819 &.{ self.output_dir.?, self.out_filename },
1820 );
1821
1822 if (self.emit_h) {
1823 self.output_h_path_source.path = builder.pathJoin(
1824 &.{ self.output_dir.?, self.out_h_filename },
1825 );
1826 }
1827
1828 if (self.target.isWindows() or self.target.isUefi()) {
1829 self.output_pdb_path_source.path = builder.pathJoin(
1830 &.{ self.output_dir.?, self.out_pdb_filename },
1831 );
1832 }
1833 }
1834
1835 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1836 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1837 }
1838}
1839
1840fn isLibCLibrary(name: []const u8) bool {
1841 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1842 for (libc_libraries) |libc_lib_name| {
1843 if (mem.eql(u8, name, libc_lib_name))
1844 return true;
1845 }
1846 return false;
1847}
1848
1849fn isLibCppLibrary(name: []const u8) bool {
1850 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1851 for (libcpp_libraries) |libcpp_lib_name| {
1852 if (mem.eql(u8, name, libcpp_lib_name))
1853 return true;
1854 }
1855 return false;
1856}
1857
1858/// Returned slice must be freed by the caller.
1859fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1860 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1861 defer allocator.free(appdata_path);
1862
1863 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1864 defer allocator.free(path_file);
1865
1866 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1867 defer file.close();
1868
1869 const size = @intCast(usize, try file.getEndPos());
1870 const vcpkg_path = try allocator.alloc(u8, size);
1871 const size_read = try file.read(vcpkg_path);
1872 std.debug.assert(size == size_read);
1873
1874 return vcpkg_path;
1875}
1876
1877pub fn doAtomicSymLinks(
1878 allocator: Allocator,
1879 output_path: []const u8,
1880 filename_major_only: []const u8,
1881 filename_name_only: []const u8,
1882) !void {
1883 const out_dir = fs.path.dirname(output_path) orelse ".";
1884 const out_basename = fs.path.basename(output_path);
1885 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1886 const major_only_path = try fs.path.join(
1887 allocator,
1888 &[_][]const u8{ out_dir, filename_major_only },
1889 );
1890 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1891 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1892 return err;
1893 };
1894 // sym link for libfoo.so to libfoo.so.1
1895 const name_only_path = try fs.path.join(
1896 allocator,
1897 &[_][]const u8{ out_dir, filename_name_only },
1898 );
1899 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1900 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1901 return err;
1902 };
1903}
1904
1905fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1906 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1907 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1908 errdefer list.deinit();
1909 var line_it = mem.tokenize(u8, stdout, "\r\n");
1910 while (line_it.next()) |line| {
1911 if (mem.trim(u8, line, " \t").len == 0) continue;
1912 var tok_it = mem.tokenize(u8, line, " \t");
1913 try list.append(PkgConfigPkg{
1914 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1915 .desc = tok_it.rest(),
1916 });
1917 }
1918 return list.toOwnedSlice();
1919}
1920
1921fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
1922 if (self.pkg_config_pkg_list) |res| {
1923 return res;
1924 }
1925 var code: u8 = undefined;
1926 if (execPkgConfigList(self, &code)) |list| {
1927 self.pkg_config_pkg_list = list;
1928 return list;
1929 } else |err| {
1930 const result = switch (err) {
1931 error.ProcessTerminated => error.PkgConfigCrashed,
1932 error.ExecNotSupported => error.PkgConfigFailed,
1933 error.ExitCodeFailure => error.PkgConfigFailed,
1934 error.FileNotFound => error.PkgConfigNotInstalled,
1935 error.InvalidName => error.PkgConfigNotInstalled,
1936 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
1937 error.ChildExecFailed => error.PkgConfigFailed,
1938 else => return err,
1939 };
1940 self.pkg_config_pkg_list = result;
1941 return result;
1942 }
1943}
1944
1945test "addPackage" {
1946 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1947
1948 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1949 defer arena.deinit();
1950
1951 const host = try NativeTargetInfo.detect(.{});
1952
1953 var builder = try std.Build.create(
1954 arena.allocator(),
1955 "test",
1956 "test",
1957 "test",
1958 "test",
1959 host,
1960 );
1961 defer builder.destroy();
1962
1963 const pkg_dep = Pkg{
1964 .name = "pkg_dep",
1965 .source = .{ .path = "/not/a/pkg_dep.zig" },
1966 };
1967 const pkg_top = Pkg{
1968 .name = "pkg_dep",
1969 .source = .{ .path = "/not/a/pkg_top.zig" },
1970 .dependencies = &[_]Pkg{pkg_dep},
1971 };
1972
1973 var exe = builder.addExecutable(.{
1974 .name = "not_an_executable",
1975 .root_source_file = .{ .path = "/not/an/executable.zig" },
1976 });
1977 exe.addPackage(pkg_top);
1978
1979 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
1980
1981 const dupe = exe.packages.items[0];
1982 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1983}
1984
1985fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
1986 const cond = opt orelse return;
1987 try args.ensureUnusedCapacity(1);
1988 if (cond) {
1989 args.appendAssumeCapacity("-f" ++ name);
1990 } else {
1991 args.appendAssumeCapacity("-fno-" ++ name);
1992 }
1993}
1994
1995const TransitiveDeps = struct {
1996 link_objects: ArrayList(LinkObject),
1997 seen_system_libs: StringHashMap(void),
1998 seen_steps: std.AutoHashMap(*const Step, void),
1999 is_linking_libcpp: bool,
2000 is_linking_libc: bool,
2001 frameworks: *StringHashMap(FrameworkLinkInfo),
2002
2003 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
2004 try td.link_objects.ensureUnusedCapacity(link_objects.len);
2005
2006 for (link_objects) |link_object| {
2007 try td.link_objects.append(link_object);
2008 switch (link_object) {
2009 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2010 else => {},
2011 }
2012 }
2013 }
2014
2015 fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void {
2016 // Inherit dependency on libc and libc++
2017 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2018 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2019
2020 // Inherit dependencies on darwin frameworks
2021 if (!dyn) {
2022 var it = other.frameworks.iterator();
2023 while (it.next()) |framework| {
2024 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2025 }
2026 }
2027
2028 // Inherit dependencies on system libraries and static libraries.
2029 for (other.link_objects.items) |other_link_object| {
2030 switch (other_link_object) {
2031 .system_lib => |system_lib| {
2032 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2033 continue;
2034
2035 if (dyn)
2036 continue;
2037
2038 try td.link_objects.append(other_link_object);
2039 },
2040 .other_step => |inner_other| {
2041 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2042 continue;
2043
2044 if (!dyn)
2045 try td.link_objects.append(other_link_object);
2046
2047 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2048 },
2049 else => continue,
2050 }
2051 }
2052 }
2053};
lib/std/Build/ConfigHeaderStep.zig created+299
......@@ -0,0 +1,299 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.Build.Step;
4
5pub const base_id: Step.Id = .config_header;
6
7pub const Style = enum {
8 /// The configure format supported by autotools. It uses `#undef foo` to
9 /// mark lines that can be substituted with different values.
10 autoconf,
11 /// The configure format supported by CMake. It uses `@@FOO@@` and
12 /// `#cmakedefine` for template substitution.
13 cmake,
14};
15
16pub const Value = union(enum) {
17 undef,
18 defined,
19 boolean: bool,
20 int: i64,
21 ident: []const u8,
22 string: []const u8,
23};
24
25step: Step,
26builder: *std.Build,
27source: std.Build.FileSource,
28style: Style,
29values: std.StringHashMap(Value),
30max_bytes: usize = 2 * 1024 * 1024,
31output_dir: []const u8,
32output_basename: []const u8,
33
34pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep {
35 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
36 const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
37 self.* = .{
38 .builder = builder,
39 .step = Step.init(base_id, name, builder.allocator, make),
40 .source = source,
41 .style = style,
42 .values = std.StringHashMap(Value).init(builder.allocator),
43 .output_dir = undefined,
44 .output_basename = "config.h",
45 };
46 switch (source) {
47 .path => |p| {
48 const basename = std.fs.path.basename(p);
49 if (std.mem.endsWith(u8, basename, ".h.in")) {
50 self.output_basename = basename[0 .. basename.len - 3];
51 }
52 },
53 else => {},
54 }
55 return self;
56}
57
58pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
59 return addValuesInner(self, values) catch @panic("OOM");
60}
61
62fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
63 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
64 try putValue(self, field.name, field.type, @field(values, field.name));
65 }
66}
67
68fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v: T) !void {
69 switch (@typeInfo(T)) {
70 .Null => {
71 try self.values.put(field_name, .undef);
72 },
73 .Void => {
74 try self.values.put(field_name, .defined);
75 },
76 .Bool => {
77 try self.values.put(field_name, .{ .boolean = v });
78 },
79 .Int => {
80 try self.values.put(field_name, .{ .int = v });
81 },
82 .ComptimeInt => {
83 try self.values.put(field_name, .{ .int = v });
84 },
85 .EnumLiteral => {
86 try self.values.put(field_name, .{ .ident = @tagName(v) });
87 },
88 .Optional => {
89 if (v) |x| {
90 return putValue(self, field_name, @TypeOf(x), x);
91 } else {
92 try self.values.put(field_name, .undef);
93 }
94 },
95 .Pointer => |ptr| {
96 switch (@typeInfo(ptr.child)) {
97 .Array => |array| {
98 if (ptr.size == .One and array.child == u8) {
99 try self.values.put(field_name, .{ .string = v });
100 return;
101 }
102 },
103 else => {},
104 }
105
106 @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T));
107 },
108 else => @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)),
109 }
110}
111
112fn make(step: *Step) !void {
113 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
114 const gpa = self.builder.allocator;
115 const src_path = self.source.getPath(self.builder);
116 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
117
118 // The cache is used here not really as a way to speed things up - because writing
119 // the data to a file would probably be very fast - but as a way to find a canonical
120 // location to put build artifacts.
121
122 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
123 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
124
125 // TODO port the cache system from the compiler to zig std lib. Until then
126 // we construct the path directly, and no "cache hit" detection happens;
127 // the files are always written.
128 // Note there is very similar code over in WriteFileStep
129 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
130 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
131 // random bytes when ConfigHeaderStep implementation is modified in a
132 // non-backwards-compatible way.
133 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
134 hash.update(self.source.getDisplayName());
135 hash.update(contents);
136
137 var digest: [16]u8 = undefined;
138 hash.final(&digest);
139 var hash_basename: [digest.len * 2]u8 = undefined;
140 _ = std.fmt.bufPrint(
141 &hash_basename,
142 "{s}",
143 .{std.fmt.fmtSliceHexLower(&digest)},
144 ) catch unreachable;
145
146 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
147 self.builder.cache_root, "o", &hash_basename,
148 });
149 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
150 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
151 return err;
152 };
153 defer dir.close();
154
155 var values_copy = try self.values.clone();
156 defer values_copy.deinit();
157
158 var output = std.ArrayList(u8).init(gpa);
159 defer output.deinit();
160 try output.ensureTotalCapacity(contents.len);
161
162 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
163
164 switch (self.style) {
165 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
166 .cmake => try render_cmake(contents, &output, &values_copy, src_path),
167 }
168
169 try dir.writeFile(self.output_basename, output.items);
170}
171
172fn render_autoconf(
173 contents: []const u8,
174 output: *std.ArrayList(u8),
175 values_copy: *std.StringHashMap(Value),
176 src_path: []const u8,
177) !void {
178 var any_errors = false;
179 var line_index: u32 = 0;
180 var line_it = std.mem.split(u8, contents, "\n");
181 while (line_it.next()) |line| : (line_index += 1) {
182 if (!std.mem.startsWith(u8, line, "#")) {
183 try output.appendSlice(line);
184 try output.appendSlice("\n");
185 continue;
186 }
187 var it = std.mem.tokenize(u8, line[1..], " \t\r");
188 const undef = it.next().?;
189 if (!std.mem.eql(u8, undef, "undef")) {
190 try output.appendSlice(line);
191 try output.appendSlice("\n");
192 continue;
193 }
194 const name = it.rest();
195 const kv = values_copy.fetchRemove(name) orelse {
196 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
197 src_path, line_index + 1, name,
198 });
199 any_errors = true;
200 continue;
201 };
202 try renderValue(output, name, kv.value);
203 }
204
205 {
206 var it = values_copy.iterator();
207 while (it.next()) |entry| {
208 const name = entry.key_ptr.*;
209 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
210 }
211 }
212
213 if (any_errors) {
214 return error.HeaderConfigFailed;
215 }
216}
217
218fn render_cmake(
219 contents: []const u8,
220 output: *std.ArrayList(u8),
221 values_copy: *std.StringHashMap(Value),
222 src_path: []const u8,
223) !void {
224 var any_errors = false;
225 var line_index: u32 = 0;
226 var line_it = std.mem.split(u8, contents, "\n");
227 while (line_it.next()) |line| : (line_index += 1) {
228 if (!std.mem.startsWith(u8, line, "#")) {
229 try output.appendSlice(line);
230 try output.appendSlice("\n");
231 continue;
232 }
233 var it = std.mem.tokenize(u8, line[1..], " \t\r");
234 const cmakedefine = it.next().?;
235 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
236 try output.appendSlice(line);
237 try output.appendSlice("\n");
238 continue;
239 }
240 const name = it.next() orelse {
241 std.debug.print("{s}:{d}: error: missing define name\n", .{
242 src_path, line_index + 1,
243 });
244 any_errors = true;
245 continue;
246 };
247 const kv = values_copy.fetchRemove(name) orelse {
248 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
249 src_path, line_index + 1, name,
250 });
251 any_errors = true;
252 continue;
253 };
254 try renderValue(output, name, kv.value);
255 }
256
257 {
258 var it = values_copy.iterator();
259 while (it.next()) |entry| {
260 const name = entry.key_ptr.*;
261 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
262 }
263 }
264
265 if (any_errors) {
266 return error.HeaderConfigFailed;
267 }
268}
269
270fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
271 switch (value) {
272 .undef => {
273 try output.appendSlice("/* #undef ");
274 try output.appendSlice(name);
275 try output.appendSlice(" */\n");
276 },
277 .defined => {
278 try output.appendSlice("#define ");
279 try output.appendSlice(name);
280 try output.appendSlice("\n");
281 },
282 .boolean => |b| {
283 try output.appendSlice("#define ");
284 try output.appendSlice(name);
285 try output.appendSlice(" ");
286 try output.appendSlice(if (b) "true\n" else "false\n");
287 },
288 .int => |i| {
289 try output.writer().print("#define {s} {d}\n", .{ name, i });
290 },
291 .ident => |ident| {
292 try output.writer().print("#define {s} {s}\n", .{ name, ident });
293 },
294 .string => |string| {
295 // TODO: use C-specific escaping instead of zig string literals
296 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
297 },
298 }
299}
lib/std/Build/EmulatableRunStep.zig created+213
......@@ -0,0 +1,213 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const Step = std.Build.Step;
9const CompileStep = std.Build.CompileStep;
10const RunStep = std.Build.RunStep;
11
12const fs = std.fs;
13const process = std.process;
14const EnvMap = process.EnvMap;
15
16const EmulatableRunStep = @This();
17
18pub const base_id = .emulatable_run;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22step: Step,
23builder: *std.Build,
24
25/// The artifact (executable) to be run by this step
26exe: *CompileStep,
27
28/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
29expected_exit_code: ?u8 = 0,
30
31/// Override this field to modify the environment
32env_map: ?*EnvMap,
33
34/// Set this to modify the current working directory
35cwd: ?[]const u8,
36
37stdout_action: RunStep.StdIoAction = .inherit,
38stderr_action: RunStep.StdIoAction = .inherit,
39
40/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
41/// or through emulation.
42hide_foreign_binaries_warning: bool,
43
44/// Creates a step that will execute the given artifact. This step will allow running the
45/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
46/// When set to false, and the binary is foreign, running the executable is skipped.
47/// Asserts given artifact is an executable.
48pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
49 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
50 const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM");
51
52 const option_name = "hide-foreign-warnings";
53 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
54 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
55 } else false;
56
57 self.* = .{
58 .builder = builder,
59 .step = Step.init(.emulatable_run, name, builder.allocator, make),
60 .exe = artifact,
61 .env_map = null,
62 .cwd = null,
63 .hide_foreign_binaries_warning = hide_warnings,
64 };
65 self.step.dependOn(&artifact.step);
66
67 return self;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
72 const host_info = self.builder.host;
73
74 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
75 defer argv_list.deinit();
76
77 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
78 switch (host_info.getExternalExecutor(self.exe.target_info, .{
79 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
80 .link_libc = self.exe.is_linking_libc,
81 })) {
82 .native => {},
83 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
84 .wine => |bin_name| if (self.builder.enable_wine) {
85 try argv_list.append(bin_name);
86 } else return,
87 .qemu => |bin_name| if (self.builder.enable_qemu) {
88 const glibc_dir_arg = if (need_cross_glibc)
89 self.builder.glibc_runtimes_dir orelse return
90 else
91 null;
92 try argv_list.append(bin_name);
93 if (glibc_dir_arg) |dir| {
94 // TODO look into making this a call to `linuxTriple`. This
95 // needs the directory to be called "i686" rather than
96 // "x86" which is why we do it manually here.
97 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
98 const cpu_arch = self.exe.target.getCpuArch();
99 const os_tag = self.exe.target.getOsTag();
100 const abi = self.exe.target.getAbi();
101 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
102 "i686"
103 else
104 @tagName(cpu_arch);
105 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
106 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
107 });
108
109 try argv_list.append("-L");
110 try argv_list.append(full_dir);
111 }
112 } else return warnAboutForeignBinaries(self),
113 .darling => |bin_name| if (self.builder.enable_darling) {
114 try argv_list.append(bin_name);
115 } else return warnAboutForeignBinaries(self),
116 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
117 try argv_list.append(bin_name);
118 try argv_list.append("--dir=.");
119 } else return warnAboutForeignBinaries(self),
120 else => return warnAboutForeignBinaries(self),
121 }
122
123 if (self.exe.target.isWindows()) {
124 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
125 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
126 }
127
128 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
129 try argv_list.append(executable_path);
130
131 try RunStep.runCommand(
132 argv_list.items,
133 self.builder,
134 self.expected_exit_code,
135 self.stdout_action,
136 self.stderr_action,
137 .Inherit,
138 self.env_map,
139 self.cwd,
140 false,
141 );
142}
143
144pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
145 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
146}
147
148pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
149 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
153 if (step.hide_foreign_binaries_warning) return;
154 const builder = step.builder;
155 const artifact = step.exe;
156
157 const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error");
158 const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error");
159 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error");
160 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
161 switch (builder.host.getExternalExecutor(target_info, .{
162 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
163 .link_libc = artifact.is_linking_libc,
164 })) {
165 .native => unreachable,
166 .bad_dl => |foreign_dl| {
167 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
168 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
169 host_dl, foreign_dl, host_dl,
170 });
171 },
172 .bad_os_or_cpu => {
173 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
174 host_name, foreign_name,
175 });
176 },
177 .darling => if (!builder.enable_darling) {
178 std.debug.print(
179 "the host system ({s}) does not appear to be capable of executing binaries " ++
180 "from the target ({s}). Consider enabling darling.\n",
181 .{ host_name, foreign_name },
182 );
183 },
184 .rosetta => if (!builder.enable_rosetta) {
185 std.debug.print(
186 "the host system ({s}) does not appear to be capable of executing binaries " ++
187 "from the target ({s}). Consider enabling rosetta.\n",
188 .{ host_name, foreign_name },
189 );
190 },
191 .wine => if (!builder.enable_wine) {
192 std.debug.print(
193 "the host system ({s}) does not appear to be capable of executing binaries " ++
194 "from the target ({s}). Consider enabling wine.\n",
195 .{ host_name, foreign_name },
196 );
197 },
198 .qemu => if (!builder.enable_qemu) {
199 std.debug.print(
200 "the host system ({s}) does not appear to be capable of executing binaries " ++
201 "from the target ({s}). Consider enabling qemu.\n",
202 .{ host_name, foreign_name },
203 );
204 },
205 .wasmtime => {
206 std.debug.print(
207 "the host system ({s}) does not appear to be capable of executing binaries " ++
208 "from the target ({s}). Consider enabling wasmtime.\n",
209 .{ host_name, foreign_name },
210 );
211 },
212 }
213}
lib/std/Build/FmtStep.zig created+32
......@@ -0,0 +1,32 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FmtStep = @This();
4
5pub const base_id = .fmt;
6
7step: Step,
8builder: *std.Build,
9argv: [][]const u8,
10
11pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {
12 const self = builder.allocator.create(FmtStep) catch @panic("OOM");
13 const name = "zig fmt";
14 self.* = FmtStep{
15 .step = Step.init(.fmt, name, builder.allocator, make),
16 .builder = builder,
17 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"),
18 };
19
20 self.argv[0] = builder.zig_exe;
21 self.argv[1] = "fmt";
22 for (paths) |path, i| {
23 self.argv[2 + i] = builder.pathFromRoot(path);
24 }
25 return self;
26}
27
28fn make(step: *Step) !void {
29 const self = @fieldParentPtr(FmtStep, "step", step);
30
31 return self.builder.spawnChild(self.argv);
32}
lib/std/Build/InstallArtifactStep.zig created+85
......@@ -0,0 +1,85 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const InstallDir = std.Build.InstallDir;
5const InstallArtifactStep = @This();
6
7pub const base_id = .install_artifact;
8
9step: Step,
10builder: *std.Build,
11artifact: *CompileStep,
12dest_dir: InstallDir,
13pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,
15
16pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
17 if (artifact.install_step) |s| return s;
18
19 const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM");
20 self.* = InstallArtifactStep{
21 .builder = builder,
22 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
23 .artifact = artifact,
24 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
25 .obj => @panic("Cannot install a .obj build artifact."),
26 .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
27 .exe, .test_exe => InstallDir{ .bin = {} },
28 .lib => InstallDir{ .lib = {} },
29 },
30 .pdb_dir = if (artifact.producesPdbFile()) blk: {
31 if (artifact.kind == .exe or artifact.kind == .test_exe) {
32 break :blk InstallDir{ .bin = {} };
33 } else {
34 break :blk InstallDir{ .lib = {} };
35 }
36 } else null,
37 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
38 };
39 self.step.dependOn(&artifact.step);
40 artifact.install_step = self;
41
42 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
43 if (self.artifact.isDynamicLibrary()) {
44 if (artifact.major_only_filename) |name| {
45 builder.pushInstalledFile(.lib, name);
46 }
47 if (artifact.name_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
49 }
50 if (self.artifact.target.isWindows()) {
51 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
52 }
53 }
54 if (self.pdb_dir) |pdb_dir| {
55 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
56 }
57 if (self.h_dir) |h_dir| {
58 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
59 }
60 return self;
61}
62
63fn make(step: *Step) !void {
64 const self = @fieldParentPtr(InstallArtifactStep, "step", step);
65 const builder = self.builder;
66
67 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
68 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
69 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
70 try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
71 }
72 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
73 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
74 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
75 }
76 if (self.pdb_dir) |pdb_dir| {
77 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
78 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
79 }
80 if (self.h_dir) |h_dir| {
81 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
82 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
83 }
84 self.artifact.installed_path = full_dest_path;
85}
lib/std/Build/InstallDirStep.zig created+93
......@@ -0,0 +1,93 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const Step = std.Build.Step;
5const InstallDir = std.Build.InstallDir;
6const InstallDirStep = @This();
7const log = std.log;
8
9step: Step,
10builder: *std.Build,
11options: Options,
12/// This is used by the build system when a file being installed comes from one
13/// package but is being installed by another.
14override_source_builder: ?*std.Build = null,
15
16pub const base_id = .install_dir;
17
18pub const Options = struct {
19 source_dir: []const u8,
20 install_dir: InstallDir,
21 install_subdir: []const u8,
22 /// File paths which end in any of these suffixes will be excluded
23 /// from being installed.
24 exclude_extensions: []const []const u8 = &.{},
25 /// File paths which end in any of these suffixes will result in
26 /// empty files being installed. This is mainly intended for large
27 /// test.zig files in order to prevent needless installation bloat.
28 /// However if the files were not present at all, then
29 /// `@import("test.zig")` would be a compile error.
30 blank_extensions: []const []const u8 = &.{},
31
32 fn dupe(self: Options, b: *std.Build) Options {
33 return .{
34 .source_dir = b.dupe(self.source_dir),
35 .install_dir = self.install_dir.dupe(b),
36 .install_subdir = b.dupe(self.install_subdir),
37 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
38 .blank_extensions = b.dupeStrings(self.blank_extensions),
39 };
40 }
41};
42
43pub fn init(
44 builder: *std.Build,
45 options: Options,
46) InstallDirStep {
47 builder.pushInstalledFile(options.install_dir, options.install_subdir);
48 return InstallDirStep{
49 .builder = builder,
50 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
51 .options = options.dupe(builder),
52 };
53}
54
55fn make(step: *Step) !void {
56 const self = @fieldParentPtr(InstallDirStep, "step", step);
57 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
58 const src_builder = self.override_source_builder orelse self.builder;
59 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
60 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
61 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
62 full_src_dir, @errorName(err),
63 });
64 return error.StepFailed;
65 };
66 defer src_dir.close();
67 var it = try src_dir.walk(self.builder.allocator);
68 next_entry: while (try it.next()) |entry| {
69 for (self.options.exclude_extensions) |ext| {
70 if (mem.endsWith(u8, entry.path, ext)) {
71 continue :next_entry;
72 }
73 }
74
75 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
76 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
77
78 switch (entry.kind) {
79 .Directory => try fs.cwd().makePath(dest_path),
80 .File => {
81 for (self.options.blank_extensions) |ext| {
82 if (mem.endsWith(u8, entry.path, ext)) {
83 try self.builder.truncateFile(dest_path);
84 continue :next_entry;
85 }
86 }
87
88 try self.builder.updateFile(full_path, dest_path);
89 },
90 else => continue,
91 }
92 }
93}
lib/std/Build/InstallFileStep.zig created+40
......@@ -0,0 +1,40 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FileSource = std.Build.FileSource;
4const InstallDir = std.Build.InstallDir;
5const InstallFileStep = @This();
6
7pub const base_id = .install_file;
8
9step: Step,
10builder: *std.Build,
11source: FileSource,
12dir: InstallDir,
13dest_rel_path: []const u8,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16override_source_builder: ?*std.Build = null,
17
18pub fn init(
19 builder: *std.Build,
20 source: FileSource,
21 dir: InstallDir,
22 dest_rel_path: []const u8,
23) InstallFileStep {
24 builder.pushInstalledFile(dir, dest_rel_path);
25 return InstallFileStep{
26 .builder = builder,
27 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
28 .source = source.dupe(builder),
29 .dir = dir.dupe(builder),
30 .dest_rel_path = builder.dupePath(dest_rel_path),
31 };
32}
33
34fn make(step: *Step) !void {
35 const self = @fieldParentPtr(InstallFileStep, "step", step);
36 const src_builder = self.override_source_builder orelse self.builder;
37 const full_src_path = self.source.getPath(src_builder);
38 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
39 try self.builder.updateFile(full_src_path, full_dest_path);
40}
lib/std/Build/InstallRawStep.zig created+110
......@@ -0,0 +1,110 @@
1//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
2//! subcommand rather than containing an implementation directly.
3
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const File = std.fs.File;
11const InstallDir = std.Build.InstallDir;
12const CompileStep = std.Build.CompileStep;
13const Step = std.Build.Step;
14const elf = std.elf;
15const fs = std.fs;
16const io = std.io;
17const sort = std.sort;
18
19pub const base_id = .install_raw;
20
21pub const RawFormat = enum {
22 bin,
23 hex,
24};
25
26step: Step,
27builder: *std.Build,
28artifact: *CompileStep,
29dest_dir: InstallDir,
30dest_filename: []const u8,
31options: CreateOptions,
32output_file: std.Build.GeneratedFile,
33
34pub const CreateOptions = struct {
35 format: ?RawFormat = null,
36 dest_dir: ?InstallDir = null,
37 only_section: ?[]const u8 = null,
38 pad_to: ?u64 = null,
39};
40
41pub fn create(
42 builder: *std.Build,
43 artifact: *CompileStep,
44 dest_filename: []const u8,
45 options: CreateOptions,
46) *InstallRawStep {
47 const self = builder.allocator.create(InstallRawStep) catch @panic("OOM");
48 self.* = InstallRawStep{
49 .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
50 .builder = builder,
51 .artifact = artifact,
52 .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
53 .obj => unreachable,
54 .@"test" => unreachable,
55 .exe, .test_exe => .bin,
56 .lib => unreachable,
57 },
58 .dest_filename = dest_filename,
59 .options = options,
60 .output_file = std.Build.GeneratedFile{ .step = &self.step },
61 };
62 self.step.dependOn(&artifact.step);
63
64 builder.pushInstalledFile(self.dest_dir, dest_filename);
65 return self;
66}
67
68pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource {
69 return std.Build.FileSource{ .generated = &self.output_file };
70}
71
72fn make(step: *Step) !void {
73 const self = @fieldParentPtr(InstallRawStep, "step", step);
74 const b = self.builder;
75
76 if (self.artifact.target.getObjectFormat() != .elf) {
77 std.debug.print("InstallRawStep only works with ELF format.\n", .{});
78 return error.InvalidObjectFormat;
79 }
80
81 const full_src_path = self.artifact.getOutputSource().getPath(b);
82 const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
83 self.output_file.path = full_dest_path;
84
85 try fs.cwd().makePath(b.getInstallPath(self.dest_dir, ""));
86
87 var argv_list = std.ArrayList([]const u8).init(b.allocator);
88 try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
89
90 if (self.options.only_section) |only_section| {
91 try argv_list.appendSlice(&.{ "-j", only_section });
92 }
93 if (self.options.pad_to) |pad_to| {
94 try argv_list.appendSlice(&.{
95 "--pad-to",
96 b.fmt("{d}", .{pad_to}),
97 });
98 }
99 if (self.options.format) |format| switch (format) {
100 .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
101 .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
102 };
103
104 try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
105 _ = try self.builder.execFromStep(argv_list.items, &self.step);
106}
107
108test {
109 std.testing.refAllDecls(InstallRawStep);
110}
lib/std/Build/LogStep.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("../std.zig");
2const log = std.log;
3const Step = std.Build.Step;
4const LogStep = @This();
5
6pub const base_id = .log;
7
8step: Step,
9builder: *std.Build,
10data: []const u8,
11
12pub fn init(builder: *std.Build, data: []const u8) LogStep {
13 return LogStep{
14 .builder = builder,
15 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
16 .data = builder.dupe(data),
17 };
18}
19
20fn make(step: *Step) anyerror!void {
21 const self = @fieldParentPtr(LogStep, "step", step);
22 log.info("{s}", .{self.data});
23}
lib/std/Build/OptionsStep.zig created+371
......@@ -0,0 +1,371 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const fs = std.fs;
4const Step = std.Build.Step;
5const GeneratedFile = std.Build.GeneratedFile;
6const CompileStep = std.Build.CompileStep;
7const FileSource = std.Build.FileSource;
8
9const OptionsStep = @This();
10
11pub const base_id = .options;
12
13step: Step,
14generated_file: GeneratedFile,
15builder: *std.Build,
16
17contents: std.ArrayList(u8),
18artifact_args: std.ArrayList(OptionArtifactArg),
19file_source_args: std.ArrayList(OptionFileSourceArg),
20
21pub fn create(builder: *std.Build) *OptionsStep {
22 const self = builder.allocator.create(OptionsStep) catch @panic("OOM");
23 self.* = .{
24 .builder = builder,
25 .step = Step.init(.options, "options", builder.allocator, make),
26 .generated_file = undefined,
27 .contents = std.ArrayList(u8).init(builder.allocator),
28 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
29 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
30 };
31 self.generated_file = .{ .step = &self.step };
32
33 return self;
34}
35
36pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
37 return addOptionFallible(self, T, name, value) catch @panic("unhandled error");
38}
39
40fn addOptionFallible(self: *OptionsStep, comptime T: type, name: []const u8, value: T) !void {
41 const out = self.contents.writer();
42 switch (T) {
43 []const []const u8 => {
44 try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)});
45 for (value) |slice| {
46 try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)});
47 }
48 try out.writeAll("};\n");
49 return;
50 },
51 [:0]const u8 => {
52 try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
53 return;
54 },
55 []const u8 => {
56 try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
57 return;
58 },
59 ?[:0]const u8 => {
60 try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)});
61 if (value) |payload| {
62 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
63 } else {
64 try out.writeAll("null;\n");
65 }
66 return;
67 },
68 ?[]const u8 => {
69 try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)});
70 if (value) |payload| {
71 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
72 } else {
73 try out.writeAll("null;\n");
74 }
75 return;
76 },
77 std.builtin.Version => {
78 try out.print(
79 \\pub const {}: @import("std").builtin.Version = .{{
80 \\ .major = {d},
81 \\ .minor = {d},
82 \\ .patch = {d},
83 \\}};
84 \\
85 , .{
86 std.zig.fmtId(name),
87
88 value.major,
89 value.minor,
90 value.patch,
91 });
92 return;
93 },
94 std.SemanticVersion => {
95 try out.print(
96 \\pub const {}: @import("std").SemanticVersion = .{{
97 \\ .major = {d},
98 \\ .minor = {d},
99 \\ .patch = {d},
100 \\
101 , .{
102 std.zig.fmtId(name),
103
104 value.major,
105 value.minor,
106 value.patch,
107 });
108 if (value.pre) |some| {
109 try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)});
110 }
111 if (value.build) |some| {
112 try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)});
113 }
114 try out.writeAll("};\n");
115 return;
116 },
117 else => {},
118 }
119 switch (@typeInfo(T)) {
120 .Enum => |enum_info| {
121 try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))});
122 inline for (enum_info.fields) |field| {
123 try out.print(" {},\n", .{std.zig.fmtId(field.name)});
124 }
125 try out.writeAll("};\n");
126 try out.print("pub const {}: {s} = {s}.{s};\n", .{
127 std.zig.fmtId(name),
128 std.zig.fmtId(@typeName(T)),
129 std.zig.fmtId(@typeName(T)),
130 std.zig.fmtId(@tagName(value)),
131 });
132 return;
133 },
134 else => {},
135 }
136 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) });
137 try printLiteral(out, value, 0);
138 try out.writeAll(";\n");
139}
140
141// TODO: non-recursive?
142fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
143 const T = @TypeOf(val);
144 switch (@typeInfo(T)) {
145 .Array => {
146 try out.print("{s} {{\n", .{@typeName(T)});
147 for (val) |item| {
148 try out.writeByteNTimes(' ', indent + 4);
149 try printLiteral(out, item, indent + 4);
150 try out.writeAll(",\n");
151 }
152 try out.writeByteNTimes(' ', indent);
153 try out.writeAll("}");
154 },
155 .Pointer => |p| {
156 if (p.size != .Slice) {
157 @compileError("Non-slice pointers are not yet supported in build options");
158 }
159 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
160 for (val) |item| {
161 try out.writeByteNTimes(' ', indent + 4);
162 try printLiteral(out, item, indent + 4);
163 try out.writeAll(",\n");
164 }
165 try out.writeByteNTimes(' ', indent);
166 try out.writeAll("}");
167 },
168 .Optional => {
169 if (val) |inner| {
170 return printLiteral(out, inner, indent);
171 } else {
172 return out.writeAll("null");
173 }
174 },
175 .Void,
176 .Bool,
177 .Int,
178 .ComptimeInt,
179 .Float,
180 .Null,
181 => try out.print("{any}", .{val}),
182 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
183 }
184}
185
186/// The value is the path in the cache dir.
187/// Adds a dependency automatically.
188pub fn addOptionFileSource(
189 self: *OptionsStep,
190 name: []const u8,
191 source: FileSource,
192) void {
193 self.file_source_args.append(.{
194 .name = name,
195 .source = source.dupe(self.builder),
196 }) catch @panic("OOM");
197 source.addStepDependencies(&self.step);
198}
199
200/// The value is the path in the cache dir.
201/// Adds a dependency automatically.
202pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
203 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM");
204 self.step.dependOn(&artifact.step);
205}
206
207pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg {
208 return .{ .name = package_name, .source = self.getSource() };
209}
210
211pub fn getSource(self: *OptionsStep) FileSource {
212 return .{ .generated = &self.generated_file };
213}
214
215fn make(step: *Step) !void {
216 const self = @fieldParentPtr(OptionsStep, "step", step);
217
218 for (self.artifact_args.items) |item| {
219 self.addOption(
220 []const u8,
221 item.name,
222 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
223 );
224 }
225
226 for (self.file_source_args.items) |item| {
227 self.addOption(
228 []const u8,
229 item.name,
230 item.source.getPath(self.builder),
231 );
232 }
233
234 const options_directory = self.builder.pathFromRoot(
235 try fs.path.join(
236 self.builder.allocator,
237 &[_][]const u8{ self.builder.cache_root, "options" },
238 ),
239 );
240
241 try fs.cwd().makePath(options_directory);
242
243 const options_file = try fs.path.join(
244 self.builder.allocator,
245 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
246 );
247
248 try fs.cwd().writeFile(options_file, self.contents.items);
249
250 self.generated_file.path = options_file;
251}
252
253fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
254 // This implementation is copied from `WriteFileStep.make`
255
256 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
257
258 // Random bytes to make OptionsStep unique. Refresh this with
259 // new random bytes when OptionsStep implementation is modified
260 // in a non-backwards-compatible way.
261 hash.update("yL0Ya4KkmcCjBlP8");
262 hash.update(self.contents.items);
263
264 var digest: [48]u8 = undefined;
265 hash.final(&digest);
266 var hash_basename: [64]u8 = undefined;
267 _ = fs.base64_encoder.encode(&hash_basename, &digest);
268 return hash_basename;
269}
270
271const OptionArtifactArg = struct {
272 name: []const u8,
273 artifact: *CompileStep,
274};
275
276const OptionFileSourceArg = struct {
277 name: []const u8,
278 source: FileSource,
279};
280
281test "OptionsStep" {
282 if (builtin.os.tag == .wasi) return error.SkipZigTest;
283
284 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
285 defer arena.deinit();
286
287 const host = try std.zig.system.NativeTargetInfo.detect(.{});
288
289 var builder = try std.Build.create(
290 arena.allocator(),
291 "test",
292 "test",
293 "test",
294 "test",
295 host,
296 );
297 defer builder.destroy();
298
299 const options = builder.addOptions();
300
301 // TODO this regressed at some point
302 //const KeywordEnum = enum {
303 // @"0.8.1",
304 //};
305
306 const nested_array = [2][2]u16{
307 [2]u16{ 300, 200 },
308 [2]u16{ 300, 200 },
309 };
310 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
311
312 options.addOption(usize, "option1", 1);
313 options.addOption(?usize, "option2", null);
314 options.addOption(?usize, "option3", 3);
315 options.addOption(comptime_int, "option4", 4);
316 options.addOption([]const u8, "string", "zigisthebest");
317 options.addOption(?[]const u8, "optional_string", null);
318 options.addOption([2][2]u16, "nested_array", nested_array);
319 options.addOption([]const []const u16, "nested_slice", nested_slice);
320 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
321 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
322 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
323
324 try std.testing.expectEqualStrings(
325 \\pub const option1: usize = 1;
326 \\pub const option2: ?usize = null;
327 \\pub const option3: ?usize = 3;
328 \\pub const option4: comptime_int = 4;
329 \\pub const string: []const u8 = "zigisthebest";
330 \\pub const optional_string: ?[]const u8 = null;
331 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
332 \\ [2]u16 {
333 \\ 300,
334 \\ 200,
335 \\ },
336 \\ [2]u16 {
337 \\ 300,
338 \\ 200,
339 \\ },
340 \\};
341 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
342 \\ &[_]u16 {
343 \\ 300,
344 \\ 200,
345 \\ },
346 \\ &[_]u16 {
347 \\ 300,
348 \\ 200,
349 \\ },
350 \\};
351 //\\pub const KeywordEnum = enum {
352 //\\ @"0.8.1",
353 //\\};
354 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
355 \\pub const version: @import("std").builtin.Version = .{
356 \\ .major = 0,
357 \\ .minor = 1,
358 \\ .patch = 2,
359 \\};
360 \\pub const semantic_version: @import("std").SemanticVersion = .{
361 \\ .major = 0,
362 \\ .minor = 1,
363 \\ .patch = 2,
364 \\ .pre = "foo",
365 \\ .build = "bar",
366 \\};
367 \\
368 , options.contents.items);
369
370 _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
371}
lib/std/Build/RemoveDirStep.zig created+29
......@@ -0,0 +1,29 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const Step = std.Build.Step;
5const RemoveDirStep = @This();
6
7pub const base_id = .remove_dir;
8
9step: Step,
10builder: *std.Build,
11dir_path: []const u8,
12
13pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {
14 return RemoveDirStep{
15 .builder = builder,
16 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
17 .dir_path = builder.dupePath(dir_path),
18 };
19}
20
21fn make(step: *Step) !void {
22 const self = @fieldParentPtr(RemoveDirStep, "step", step);
23
24 const full_path = self.builder.pathFromRoot(self.dir_path);
25 fs.cwd().deleteTree(full_path) catch |err| {
26 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
27 return err;
28 };
29}
lib/std/Build/RunStep.zig created+376
......@@ -0,0 +1,376 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const Step = std.Build.Step;
4const CompileStep = std.Build.CompileStep;
5const WriteFileStep = std.Build.WriteFileStep;
6const fs = std.fs;
7const mem = std.mem;
8const process = std.process;
9const ArrayList = std.ArrayList;
10const EnvMap = process.EnvMap;
11const Allocator = mem.Allocator;
12const ExecError = std.Build.ExecError;
13
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
15
16const RunStep = @This();
17
18pub const base_id: Step.Id = .run;
19
20step: Step,
21builder: *std.Build,
22
23/// See also addArg and addArgs to modifying this directly
24argv: ArrayList(Arg),
25
26/// Set this to modify the current working directory
27cwd: ?[]const u8,
28
29/// Override this field to modify the environment, or use setEnvironmentVariable
30env_map: ?*EnvMap,
31
32stdout_action: StdIoAction = .inherit,
33stderr_action: StdIoAction = .inherit,
34
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,
36
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
38expected_exit_code: ?u8 = 0,
39
40/// Print the command before running it
41print: bool,
42
43pub const StdIoAction = union(enum) {
44 inherit,
45 ignore,
46 expect_exact: []const u8,
47 expect_matches: []const []const u8,
48};
49
50pub const Arg = union(enum) {
51 artifact: *CompileStep,
52 file_source: std.Build.FileSource,
53 bytes: []u8,
54};
55
56pub fn create(builder: *std.Build, name: []const u8) *RunStep {
57 const self = builder.allocator.create(RunStep) catch @panic("OOM");
58 self.* = RunStep{
59 .builder = builder,
60 .step = Step.init(base_id, name, builder.allocator, make),
61 .argv = ArrayList(Arg).init(builder.allocator),
62 .cwd = null,
63 .env_map = null,
64 .print = builder.verbose,
65 };
66 return self;
67}
68
69pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
70 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
71 self.step.dependOn(&artifact.step);
72}
73
74pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
75 self.argv.append(Arg{
76 .file_source = file_source.dupe(self.builder),
77 }) catch @panic("OOM");
78 file_source.addStepDependencies(&self.step);
79}
80
81pub fn addArg(self: *RunStep, arg: []const u8) void {
82 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");
83}
84
85pub fn addArgs(self: *RunStep, args: []const []const u8) void {
86 for (args) |arg| {
87 self.addArg(arg);
88 }
89}
90
91pub fn clearEnvironment(self: *RunStep) void {
92 const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");
93 new_env_map.* = EnvMap.init(self.builder.allocator);
94 self.env_map = new_env_map;
95}
96
97pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
98 addPathDirInternal(&self.step, self.builder, search_path);
99}
100
101/// For internal use only, users of `RunStep` should use `addPathDir` directly.
102pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void {
103 const env_map = getEnvMapInternal(step, builder.allocator);
104
105 const key = "PATH";
106 var prev_path = env_map.get(key);
107
108 if (prev_path) |pp| {
109 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
110 env_map.put(key, new_path) catch @panic("OOM");
111 } else {
112 env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM");
113 }
114}
115
116pub fn getEnvMap(self: *RunStep) *EnvMap {
117 return getEnvMapInternal(&self.step, self.builder.allocator);
118}
119
120fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
121 const maybe_env_map = switch (step.id) {
122 .run => step.cast(RunStep).?.env_map,
123 .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
124 else => unreachable,
125 };
126 return maybe_env_map orelse {
127 const env_map = allocator.create(EnvMap) catch @panic("OOM");
128 env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");
129 switch (step.id) {
130 .run => step.cast(RunStep).?.env_map = env_map,
131 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
132 else => unreachable,
133 }
134 return env_map;
135 };
136}
137
138pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
139 const env_map = self.getEnvMap();
140 env_map.put(
141 self.builder.dupe(key),
142 self.builder.dupe(value),
143 ) catch @panic("unhandled error");
144}
145
146pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
155 return switch (action) {
156 .ignore => .Ignore,
157 .inherit => .Inherit,
158 .expect_exact, .expect_matches => .Pipe,
159 };
160}
161
162fn make(step: *Step) !void {
163 const self = @fieldParentPtr(RunStep, "step", step);
164
165 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
166 for (self.argv.items) |arg| {
167 switch (arg) {
168 .bytes => |bytes| try argv_list.append(bytes),
169 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
170 .artifact => |artifact| {
171 if (artifact.target.isWindows()) {
172 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
173 self.addPathForDynLibs(artifact);
174 }
175 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
176 try argv_list.append(executable_path);
177 },
178 }
179 }
180
181 try runCommand(
182 argv_list.items,
183 self.builder,
184 self.expected_exit_code,
185 self.stdout_action,
186 self.stderr_action,
187 self.stdin_behavior,
188 self.env_map,
189 self.cwd,
190 self.print,
191 );
192}
193
194pub fn runCommand(
195 argv: []const []const u8,
196 builder: *std.Build,
197 expected_exit_code: ?u8,
198 stdout_action: StdIoAction,
199 stderr_action: StdIoAction,
200 stdin_behavior: std.ChildProcess.StdIo,
201 env_map: ?*EnvMap,
202 maybe_cwd: ?[]const u8,
203 print: bool,
204) !void {
205 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
206
207 if (!std.process.can_spawn) {
208 const cmd = try std.mem.join(builder.allocator, " ", argv);
209 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
210 builder.allocator.free(cmd);
211 return ExecError.ExecNotSupported;
212 }
213
214 var child = std.ChildProcess.init(argv, builder.allocator);
215 child.cwd = cwd;
216 child.env_map = env_map orelse builder.env_map;
217
218 child.stdin_behavior = stdin_behavior;
219 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
220 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
221
222 if (print)
223 printCmd(cwd, argv);
224
225 child.spawn() catch |err| {
226 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
227 return err;
228 };
229
230 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
231
232 var stdout: ?[]const u8 = null;
233 defer if (stdout) |s| builder.allocator.free(s);
234
235 switch (stdout_action) {
236 .expect_exact, .expect_matches => {
237 stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
238 },
239 .inherit, .ignore => {},
240 }
241
242 var stderr: ?[]const u8 = null;
243 defer if (stderr) |s| builder.allocator.free(s);
244
245 switch (stderr_action) {
246 .expect_exact, .expect_matches => {
247 stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
248 },
249 .inherit, .ignore => {},
250 }
251
252 const term = child.wait() catch |err| {
253 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
254 return err;
255 };
256
257 switch (term) {
258 .Exited => |code| blk: {
259 const expected_code = expected_exit_code orelse break :blk;
260
261 if (code != expected_code) {
262 if (builder.prominent_compile_errors) {
263 std.debug.print("Run step exited with error code {} (expected {})\n", .{
264 code,
265 expected_code,
266 });
267 } else {
268 std.debug.print("The following command exited with error code {} (expected {}):\n", .{
269 code,
270 expected_code,
271 });
272 printCmd(cwd, argv);
273 }
274
275 return error.UnexpectedExitCode;
276 }
277 },
278 else => {
279 std.debug.print("The following command terminated unexpectedly:\n", .{});
280 printCmd(cwd, argv);
281 return error.UncleanExit;
282 },
283 }
284
285 switch (stderr_action) {
286 .inherit, .ignore => {},
287 .expect_exact => |expected_bytes| {
288 if (!mem.eql(u8, expected_bytes, stderr.?)) {
289 std.debug.print(
290 \\
291 \\========= Expected this stderr: =========
292 \\{s}
293 \\========= But found: ====================
294 \\{s}
295 \\
296 , .{ expected_bytes, stderr.? });
297 printCmd(cwd, argv);
298 return error.TestFailed;
299 }
300 },
301 .expect_matches => |matches| for (matches) |match| {
302 if (mem.indexOf(u8, stderr.?, match) == null) {
303 std.debug.print(
304 \\
305 \\========= Expected to find in stderr: =========
306 \\{s}
307 \\========= But stderr does not contain it: =====
308 \\{s}
309 \\
310 , .{ match, stderr.? });
311 printCmd(cwd, argv);
312 return error.TestFailed;
313 }
314 },
315 }
316
317 switch (stdout_action) {
318 .inherit, .ignore => {},
319 .expect_exact => |expected_bytes| {
320 if (!mem.eql(u8, expected_bytes, stdout.?)) {
321 std.debug.print(
322 \\
323 \\========= Expected this stdout: =========
324 \\{s}
325 \\========= But found: ====================
326 \\{s}
327 \\
328 , .{ expected_bytes, stdout.? });
329 printCmd(cwd, argv);
330 return error.TestFailed;
331 }
332 },
333 .expect_matches => |matches| for (matches) |match| {
334 if (mem.indexOf(u8, stdout.?, match) == null) {
335 std.debug.print(
336 \\
337 \\========= Expected to find in stdout: =========
338 \\{s}
339 \\========= But stdout does not contain it: =====
340 \\{s}
341 \\
342 , .{ match, stdout.? });
343 printCmd(cwd, argv);
344 return error.TestFailed;
345 }
346 },
347 }
348}
349
350fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
351 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
352 for (argv) |arg| {
353 std.debug.print("{s} ", .{arg});
354 }
355 std.debug.print("\n", .{});
356}
357
358fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
359 addPathForDynLibsInternal(&self.step, self.builder, artifact);
360}
361
362/// This should only be used for internal usage, this is called automatically
363/// for the user.
364pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void {
365 for (artifact.link_objects.items) |link_object| {
366 switch (link_object) {
367 .other_step => |other| {
368 if (other.target.isWindows() and other.isDynamicLibrary()) {
369 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
370 addPathForDynLibsInternal(step, builder, other);
371 }
372 },
373 else => {},
374 }
375 }
376}
lib/std/Build/Step.zig created+97
......@@ -0,0 +1,97 @@
1id: Id,
2name: []const u8,
3makeFn: *const fn (self: *Step) anyerror!void,
4dependencies: std.ArrayList(*Step),
5loop_flag: bool,
6done_flag: bool,
7
8pub const Id = enum {
9 top_level,
10 compile,
11 install_artifact,
12 install_file,
13 install_dir,
14 log,
15 remove_dir,
16 fmt,
17 translate_c,
18 write_file,
19 run,
20 emulatable_run,
21 check_file,
22 check_object,
23 config_header,
24 install_raw,
25 options,
26 custom,
27
28 pub fn Type(comptime id: Id) type {
29 return switch (id) {
30 .top_level => Build.TopLevelStep,
31 .compile => Build.CompileStep,
32 .install_artifact => Build.InstallArtifactStep,
33 .install_file => Build.InstallFileStep,
34 .install_dir => Build.InstallDirStep,
35 .log => Build.LogStep,
36 .remove_dir => Build.RemoveDirStep,
37 .fmt => Build.FmtStep,
38 .translate_c => Build.TranslateCStep,
39 .write_file => Build.WriteFileStep,
40 .run => Build.RunStep,
41 .emulatable_run => Build.EmulatableRunStep,
42 .check_file => Build.CheckFileStep,
43 .check_object => Build.CheckObjectStep,
44 .config_header => Build.ConfigHeaderStep,
45 .install_raw => Build.InstallRawStep,
46 .options => Build.OptionsStep,
47 .custom => @compileError("no type available for custom step"),
48 };
49 }
50};
51
52pub fn init(
53 id: Id,
54 name: []const u8,
55 allocator: Allocator,
56 makeFn: *const fn (self: *Step) anyerror!void,
57) Step {
58 return Step{
59 .id = id,
60 .name = allocator.dupe(u8, name) catch @panic("OOM"),
61 .makeFn = makeFn,
62 .dependencies = std.ArrayList(*Step).init(allocator),
63 .loop_flag = false,
64 .done_flag = false,
65 };
66}
67
68pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
69 return init(id, name, allocator, makeNoOp);
70}
71
72pub fn make(self: *Step) !void {
73 if (self.done_flag) return;
74
75 try self.makeFn(self);
76 self.done_flag = true;
77}
78
79pub fn dependOn(self: *Step, other: *Step) void {
80 self.dependencies.append(other) catch @panic("OOM");
81}
82
83fn makeNoOp(self: *Step) anyerror!void {
84 _ = self;
85}
86
87pub fn cast(step: *Step, comptime T: type) ?*T {
88 if (step.id == T.base_id) {
89 return @fieldParentPtr(T, "step", step);
90 }
91 return null;
92}
93
94const Step = @This();
95const std = @import("../std.zig");
96const Build = std.Build;
97const Allocator = std.mem.Allocator;
lib/std/Build/TranslateCStep.zig created+136
......@@ -0,0 +1,136 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const CheckFileStep = std.Build.CheckFileStep;
5const fs = std.fs;
6const mem = std.mem;
7const CrossTarget = std.zig.CrossTarget;
8
9const TranslateCStep = @This();
10
11pub const base_id = .translate_c;
12
13step: Step,
14builder: *std.Build,
15source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),
18output_dir: ?[]const u8,
19out_basename: []const u8,
20target: CrossTarget,
21optimize: std.builtin.OptimizeMode,
22output_file: std.Build.GeneratedFile,
23
24pub const Options = struct {
25 source_file: std.Build.FileSource,
26 target: CrossTarget,
27 optimize: std.builtin.OptimizeMode,
28};
29
30pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
31 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");
32 const source = options.source_file.dupe(builder);
33 self.* = TranslateCStep{
34 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
35 .builder = builder,
36 .source = source,
37 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
38 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
39 .output_dir = null,
40 .out_basename = undefined,
41 .target = options.target,
42 .optimize = options.optimize,
43 .output_file = std.Build.GeneratedFile{ .step = &self.step },
44 };
45 source.addStepDependencies(&self.step);
46 return self;
47}
48
49pub const AddExecutableOptions = struct {
50 name: ?[]const u8 = null,
51 version: ?std.builtin.Version = null,
52 target: ?CrossTarget = null,
53 optimize: ?std.builtin.Mode = null,
54 linkage: ?CompileStep.Linkage = null,
55};
56
57/// Creates a step to build an executable from the translated source.
58pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
59 return self.builder.addExecutable(.{
60 .root_source_file = .{ .generated = &self.output_file },
61 .name = options.name orelse "translated_c",
62 .version = options.version,
63 .target = options.target orelse self.target,
64 .optimize = options.optimize orelse self.optimize,
65 .linkage = options.linkage,
66 });
67}
68
69pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
70 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");
71}
72
73pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
74 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
75}
76
77/// If the value is omitted, it is set to 1.
78/// `name` and `value` need not live longer than the function call.
79pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
80 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
81 self.c_macros.append(macro) catch @panic("OOM");
82}
83
84/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
85pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
86 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
87}
88
89fn make(step: *Step) !void {
90 const self = @fieldParentPtr(TranslateCStep, "step", step);
91
92 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
93 try argv_list.append(self.builder.zig_exe);
94 try argv_list.append("translate-c");
95 try argv_list.append("-lc");
96
97 try argv_list.append("--enable-cache");
98
99 if (!self.target.isNative()) {
100 try argv_list.append("-target");
101 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
102 }
103
104 switch (self.optimize) {
105 .Debug => {}, // Skip since it's the default.
106 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
107 }
108
109 for (self.include_dirs.items) |include_dir| {
110 try argv_list.append("-I");
111 try argv_list.append(include_dir);
112 }
113
114 for (self.c_macros.items) |c_macro| {
115 try argv_list.append("-D");
116 try argv_list.append(c_macro);
117 }
118
119 try argv_list.append(self.source.getPath(self.builder));
120
121 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
122 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
123
124 self.out_basename = fs.path.basename(output_path);
125 if (self.output_dir) |output_dir| {
126 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
127 try self.builder.updateFile(output_path, full_dest);
128 } else {
129 self.output_dir = fs.path.dirname(output_path).?;
130 }
131
132 self.output_file.path = try fs.path.join(
133 self.builder.allocator,
134 &[_][]const u8{ self.output_dir.?, self.out_basename },
135 );
136}
lib/std/Build/WriteFileStep.zig created+115
......@@ -0,0 +1,115 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const ArrayList = std.ArrayList;
5
6const WriteFileStep = @This();
7
8pub const base_id = .write_file;
9
10step: Step,
11builder: *std.Build,
12output_dir: []const u8,
13files: std.TailQueue(File),
14
15pub const File = struct {
16 source: std.Build.GeneratedFile,
17 basename: []const u8,
18 bytes: []const u8,
19};
20
21pub fn init(builder: *std.Build) WriteFileStep {
22 return WriteFileStep{
23 .builder = builder,
24 .step = Step.init(.write_file, "writefile", builder.allocator, make),
25 .files = .{},
26 .output_dir = undefined,
27 };
28}
29
30pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
31 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error");
32 node.* = .{
33 .data = .{
34 .source = std.Build.GeneratedFile{ .step = &self.step },
35 .basename = self.builder.dupePath(basename),
36 .bytes = self.builder.dupe(bytes),
37 },
38 };
39
40 self.files.append(node);
41}
42
43/// Gets a file source for the given basename. If the file does not exist, returns `null`.
44pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource {
45 var it = step.files.first;
46 while (it) |node| : (it = node.next) {
47 if (std.mem.eql(u8, node.data.basename, basename))
48 return std.Build.FileSource{ .generated = &node.data.source };
49 }
50 return null;
51}
52
53fn make(step: *Step) !void {
54 const self = @fieldParentPtr(WriteFileStep, "step", step);
55
56 // The cache is used here not really as a way to speed things up - because writing
57 // the data to a file would probably be very fast - but as a way to find a canonical
58 // location to put build artifacts.
59
60 // If, for example, a hard-coded path was used as the location to put WriteFileStep
61 // files, then two WriteFileSteps executing in parallel might clobber each other.
62
63 // TODO port the cache system from the compiler to zig std lib. Until then
64 // we directly construct the path, and no "cache hit" detection happens;
65 // the files are always written.
66 // Note there is similar code over in ConfigHeaderStep.
67 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
68 // Random bytes to make WriteFileStep unique. Refresh this with
69 // new random bytes when WriteFileStep implementation is modified
70 // in a non-backwards-compatible way.
71 var hash = Hasher.init("eagVR1dYXoE7ARDP");
72
73 {
74 var it = self.files.first;
75 while (it) |node| : (it = node.next) {
76 hash.update(node.data.basename);
77 hash.update(node.data.bytes);
78 hash.update("|");
79 }
80 }
81 var digest: [16]u8 = undefined;
82 hash.final(&digest);
83 var hash_basename: [digest.len * 2]u8 = undefined;
84 _ = std.fmt.bufPrint(
85 &hash_basename,
86 "{s}",
87 .{std.fmt.fmtSliceHexLower(&digest)},
88 ) catch unreachable;
89
90 self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
91 self.builder.cache_root, "o", &hash_basename,
92 });
93 var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
94 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
95 return err;
96 };
97 defer dir.close();
98 {
99 var it = self.files.first;
100 while (it) |node| : (it = node.next) {
101 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
102 std.debug.print("unable to write {s} into {s}: {s}\n", .{
103 node.data.basename,
104 self.output_dir,
105 @errorName(err),
106 });
107 return err;
108 };
109 node.data.source.path = try fs.path.join(
110 self.builder.allocator,
111 &[_][]const u8{ self.output_dir, node.data.basename },
112 );
113 }
114 }
115}
lib/std/build.zig deleted-1781
......@@ -1,1781 +0,0 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const ThisModule = @This();
21
22pub const CheckFileStep = @import("build/CheckFileStep.zig");
23pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
24pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig");
25pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig");
26pub const FmtStep = @import("build/FmtStep.zig");
27pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig");
28pub const InstallDirStep = @import("build/InstallDirStep.zig");
29pub const InstallFileStep = @import("build/InstallFileStep.zig");
30pub const InstallRawStep = @import("build/InstallRawStep.zig");
31pub const LibExeObjStep = @import("build/LibExeObjStep.zig");
32pub const LogStep = @import("build/LogStep.zig");
33pub const OptionsStep = @import("build/OptionsStep.zig");
34pub const RemoveDirStep = @import("build/RemoveDirStep.zig");
35pub const RunStep = @import("build/RunStep.zig");
36pub const TranslateCStep = @import("build/TranslateCStep.zig");
37pub const WriteFileStep = @import("build/WriteFileStep.zig");
38
39pub const Builder = struct {
40 install_tls: TopLevelStep,
41 uninstall_tls: TopLevelStep,
42 allocator: Allocator,
43 user_input_options: UserInputOptionsMap,
44 available_options_map: AvailableOptionsMap,
45 available_options_list: ArrayList(AvailableOption),
46 verbose: bool,
47 verbose_link: bool,
48 verbose_cc: bool,
49 verbose_air: bool,
50 verbose_llvm_ir: bool,
51 verbose_cimport: bool,
52 verbose_llvm_cpu_features: bool,
53 /// The purpose of executing the command is for a human to read compile errors from the terminal
54 prominent_compile_errors: bool,
55 color: enum { auto, on, off } = .auto,
56 reference_trace: ?u32 = null,
57 invalid_user_input: bool,
58 zig_exe: []const u8,
59 default_step: *Step,
60 env_map: *EnvMap,
61 top_level_steps: ArrayList(*TopLevelStep),
62 install_prefix: []const u8,
63 dest_dir: ?[]const u8,
64 lib_dir: []const u8,
65 exe_dir: []const u8,
66 h_dir: []const u8,
67 install_path: []const u8,
68 sysroot: ?[]const u8 = null,
69 search_prefixes: ArrayList([]const u8),
70 libc_file: ?[]const u8 = null,
71 installed_files: ArrayList(InstalledFile),
72 /// Path to the directory containing build.zig.
73 build_root: []const u8,
74 cache_root: []const u8,
75 global_cache_root: []const u8,
76 release_mode: ?std.builtin.Mode,
77 is_release: bool,
78 /// zig lib dir
79 override_lib_dir: ?[]const u8,
80 vcpkg_root: VcpkgRoot = .unattempted,
81 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
82 args: ?[][]const u8 = null,
83 debug_log_scopes: []const []const u8 = &.{},
84 debug_compile_errors: bool = false,
85
86 /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
87 enable_darling: bool = false,
88 /// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
89 enable_qemu: bool = false,
90 /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
91 enable_rosetta: bool = false,
92 /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
93 enable_wasmtime: bool = false,
94 /// Use system Wine installation to run cross compiled Windows build artifacts.
95 enable_wine: bool = false,
96 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
97 /// this will be the directory $glibc-build-dir/install/glibcs
98 /// Given the example of the aarch64 target, this is the directory
99 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
100 glibc_runtimes_dir: ?[]const u8 = null,
101
102 /// Information about the native target. Computed before build() is invoked.
103 host: NativeTargetInfo,
104
105 dep_prefix: []const u8 = "",
106
107 pub const ExecError = error{
108 ReadFailure,
109 ExitCodeFailure,
110 ProcessTerminated,
111 ExecNotSupported,
112 } || std.ChildProcess.SpawnError;
113
114 pub const PkgConfigError = error{
115 PkgConfigCrashed,
116 PkgConfigFailed,
117 PkgConfigNotInstalled,
118 PkgConfigInvalidOutput,
119 };
120
121 pub const PkgConfigPkg = struct {
122 name: []const u8,
123 desc: []const u8,
124 };
125
126 pub const CStd = enum {
127 C89,
128 C99,
129 C11,
130 };
131
132 const UserInputOptionsMap = StringHashMap(UserInputOption);
133 const AvailableOptionsMap = StringHashMap(AvailableOption);
134
135 const AvailableOption = struct {
136 name: []const u8,
137 type_id: TypeId,
138 description: []const u8,
139 /// If the `type_id` is `enum` this provides the list of enum options
140 enum_options: ?[]const []const u8,
141 };
142
143 const UserInputOption = struct {
144 name: []const u8,
145 value: UserValue,
146 used: bool,
147 };
148
149 const UserValue = union(enum) {
150 flag: void,
151 scalar: []const u8,
152 list: ArrayList([]const u8),
153 };
154
155 const TypeId = enum {
156 bool,
157 int,
158 float,
159 @"enum",
160 string,
161 list,
162 };
163
164 const TopLevelStep = struct {
165 pub const base_id = .top_level;
166
167 step: Step,
168 description: []const u8,
169 };
170
171 pub const DirList = struct {
172 lib_dir: ?[]const u8 = null,
173 exe_dir: ?[]const u8 = null,
174 include_dir: ?[]const u8 = null,
175 };
176
177 pub fn create(
178 allocator: Allocator,
179 zig_exe: []const u8,
180 build_root: []const u8,
181 cache_root: []const u8,
182 global_cache_root: []const u8,
183 ) !*Builder {
184 const env_map = try allocator.create(EnvMap);
185 env_map.* = try process.getEnvMap(allocator);
186
187 const host = try NativeTargetInfo.detect(.{});
188
189 const self = try allocator.create(Builder);
190 self.* = Builder{
191 .zig_exe = zig_exe,
192 .build_root = build_root,
193 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
194 .global_cache_root = global_cache_root,
195 .verbose = false,
196 .verbose_link = false,
197 .verbose_cc = false,
198 .verbose_air = false,
199 .verbose_llvm_ir = false,
200 .verbose_cimport = false,
201 .verbose_llvm_cpu_features = false,
202 .prominent_compile_errors = false,
203 .invalid_user_input = false,
204 .allocator = allocator,
205 .user_input_options = UserInputOptionsMap.init(allocator),
206 .available_options_map = AvailableOptionsMap.init(allocator),
207 .available_options_list = ArrayList(AvailableOption).init(allocator),
208 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
209 .default_step = undefined,
210 .env_map = env_map,
211 .search_prefixes = ArrayList([]const u8).init(allocator),
212 .install_prefix = undefined,
213 .lib_dir = undefined,
214 .exe_dir = undefined,
215 .h_dir = undefined,
216 .dest_dir = env_map.get("DESTDIR"),
217 .installed_files = ArrayList(InstalledFile).init(allocator),
218 .install_tls = TopLevelStep{
219 .step = Step.initNoOp(.top_level, "install", allocator),
220 .description = "Copy build artifacts to prefix path",
221 },
222 .uninstall_tls = TopLevelStep{
223 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
224 .description = "Remove build artifacts from prefix path",
225 },
226 .release_mode = null,
227 .is_release = false,
228 .override_lib_dir = null,
229 .install_path = undefined,
230 .args = null,
231 .host = host,
232 };
233 try self.top_level_steps.append(&self.install_tls);
234 try self.top_level_steps.append(&self.uninstall_tls);
235 self.default_step = &self.install_tls.step;
236 return self;
237 }
238
239 fn createChild(
240 parent: *Builder,
241 dep_name: []const u8,
242 build_root: []const u8,
243 args: anytype,
244 ) !*Builder {
245 const child = try createChildOnly(parent, dep_name, build_root);
246 try applyArgs(child, args);
247 return child;
248 }
249
250 fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
251 const allocator = parent.allocator;
252 const child = try allocator.create(Builder);
253 child.* = .{
254 .allocator = allocator,
255 .install_tls = .{
256 .step = Step.initNoOp(.top_level, "install", allocator),
257 .description = "Copy build artifacts to prefix path",
258 },
259 .uninstall_tls = .{
260 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
261 .description = "Remove build artifacts from prefix path",
262 },
263 .user_input_options = UserInputOptionsMap.init(allocator),
264 .available_options_map = AvailableOptionsMap.init(allocator),
265 .available_options_list = ArrayList(AvailableOption).init(allocator),
266 .verbose = parent.verbose,
267 .verbose_link = parent.verbose_link,
268 .verbose_cc = parent.verbose_cc,
269 .verbose_air = parent.verbose_air,
270 .verbose_llvm_ir = parent.verbose_llvm_ir,
271 .verbose_cimport = parent.verbose_cimport,
272 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
273 .prominent_compile_errors = parent.prominent_compile_errors,
274 .color = parent.color,
275 .reference_trace = parent.reference_trace,
276 .invalid_user_input = false,
277 .zig_exe = parent.zig_exe,
278 .default_step = undefined,
279 .env_map = parent.env_map,
280 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
281 .install_prefix = undefined,
282 .dest_dir = parent.dest_dir,
283 .lib_dir = parent.lib_dir,
284 .exe_dir = parent.exe_dir,
285 .h_dir = parent.h_dir,
286 .install_path = parent.install_path,
287 .sysroot = parent.sysroot,
288 .search_prefixes = ArrayList([]const u8).init(allocator),
289 .libc_file = parent.libc_file,
290 .installed_files = ArrayList(InstalledFile).init(allocator),
291 .build_root = build_root,
292 .cache_root = parent.cache_root,
293 .global_cache_root = parent.global_cache_root,
294 .release_mode = parent.release_mode,
295 .is_release = parent.is_release,
296 .override_lib_dir = parent.override_lib_dir,
297 .debug_log_scopes = parent.debug_log_scopes,
298 .debug_compile_errors = parent.debug_compile_errors,
299 .enable_darling = parent.enable_darling,
300 .enable_qemu = parent.enable_qemu,
301 .enable_rosetta = parent.enable_rosetta,
302 .enable_wasmtime = parent.enable_wasmtime,
303 .enable_wine = parent.enable_wine,
304 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
305 .host = parent.host,
306 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
307 };
308 try child.top_level_steps.append(&child.install_tls);
309 try child.top_level_steps.append(&child.uninstall_tls);
310 child.default_step = &child.install_tls.step;
311 return child;
312 }
313
314 fn applyArgs(b: *Builder, args: anytype) !void {
315 // TODO this function is the way that a build.zig file communicates
316 // options to its dependencies. It is the programmatic way to give
317 // command line arguments to a build.zig script.
318 _ = args;
319 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
320 // Random bytes to make unique. Refresh this with new random bytes when
321 // implementation is modified in a non-backwards-compatible way.
322 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
323 hash.update(b.dep_prefix);
324 // TODO additionally update the hash with `args`.
325
326 var digest: [16]u8 = undefined;
327 hash.final(&digest);
328 var hash_basename: [digest.len * 2]u8 = undefined;
329 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
330 unreachable;
331
332 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
333 b.resolveInstallPrefix(install_prefix, .{});
334 }
335
336 pub fn destroy(self: *Builder) void {
337 self.env_map.deinit();
338 self.top_level_steps.deinit();
339 self.allocator.destroy(self);
340 }
341
342 /// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
343 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void {
344 if (self.dest_dir) |dest_dir| {
345 self.install_prefix = install_prefix orelse "/usr";
346 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
347 } else {
348 self.install_prefix = install_prefix orelse
349 (self.pathJoin(&.{ self.build_root, "zig-out" }));
350 self.install_path = self.install_prefix;
351 }
352
353 var lib_list = [_][]const u8{ self.install_path, "lib" };
354 var exe_list = [_][]const u8{ self.install_path, "bin" };
355 var h_list = [_][]const u8{ self.install_path, "include" };
356
357 if (dir_list.lib_dir) |dir| {
358 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
359 lib_list[1] = dir;
360 }
361
362 if (dir_list.exe_dir) |dir| {
363 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
364 exe_list[1] = dir;
365 }
366
367 if (dir_list.include_dir) |dir| {
368 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
369 h_list[1] = dir;
370 }
371
372 self.lib_dir = self.pathJoin(&lib_list);
373 self.exe_dir = self.pathJoin(&exe_list);
374 self.h_dir = self.pathJoin(&h_list);
375 }
376
377 fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource {
378 return if (path) |p|
379 FileSource{ .path = p }
380 else
381 null;
382 }
383
384 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
385 return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src));
386 }
387
388 pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
389 return LibExeObjStep.createExecutable(builder, name, root_src);
390 }
391
392 pub fn addOptions(self: *Builder) *OptionsStep {
393 return OptionsStep.create(self);
394 }
395
396 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
397 return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
398 }
399
400 pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
401 return LibExeObjStep.createObject(builder, name, root_src);
402 }
403
404 pub fn addSharedLibrary(
405 self: *Builder,
406 name: []const u8,
407 root_src: ?[]const u8,
408 kind: LibExeObjStep.SharedLibKind,
409 ) *LibExeObjStep {
410 return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind);
411 }
412
413 pub fn addSharedLibrarySource(
414 self: *Builder,
415 name: []const u8,
416 root_src: ?FileSource,
417 kind: LibExeObjStep.SharedLibKind,
418 ) *LibExeObjStep {
419 return LibExeObjStep.createSharedLibrary(self, name, root_src, kind);
420 }
421
422 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
423 return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src));
424 }
425
426 pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
427 return LibExeObjStep.createStaticLibrary(self, name, root_src);
428 }
429
430 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {
431 return LibExeObjStep.createTest(self, "test", .{ .path = root_src });
432 }
433
434 pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep {
435 return LibExeObjStep.createTest(self, "test", root_src.dupe(self));
436 }
437
438 pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep {
439 return LibExeObjStep.createTestExe(self, name, .{ .path = root_src });
440 }
441
442 pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
443 return LibExeObjStep.createTestExe(self, name, root_src.dupe(self));
444 }
445
446 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
447 return addAssembleSource(self, name, .{ .path = src });
448 }
449
450 pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep {
451 const obj_step = LibExeObjStep.createObject(self, name, null);
452 obj_step.addAssemblyFileSource(src.dupe(self));
453 return obj_step;
454 }
455
456 /// Initializes a RunStep with argv, which must at least have the path to the
457 /// executable. More command line arguments can be added with `addArg`,
458 /// `addArgs`, and `addArtifactArg`.
459 /// Be careful using this function, as it introduces a system dependency.
460 /// To run an executable built with zig build, see `LibExeObjStep.run`.
461 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
462 assert(argv.len >= 1);
463 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
464 run_step.addArgs(argv);
465 return run_step;
466 }
467
468 pub fn addConfigHeader(
469 b: *Builder,
470 source: FileSource,
471 style: ConfigHeaderStep.Style,
472 values: anytype,
473 ) *ConfigHeaderStep {
474 const config_header_step = ConfigHeaderStep.create(b, source, style);
475 config_header_step.addValues(values);
476 return config_header_step;
477 }
478
479 /// Allocator.dupe without the need to handle out of memory.
480 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
481 return self.allocator.dupe(u8, bytes) catch unreachable;
482 }
483
484 /// Duplicates an array of strings without the need to handle out of memory.
485 pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
486 const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
487 for (strings) |s, i| {
488 array[i] = self.dupe(s);
489 }
490 return array;
491 }
492
493 /// Duplicates a path and converts all slashes to the OS's canonical path separator.
494 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
495 const the_copy = self.dupe(bytes);
496 for (the_copy) |*byte| {
497 switch (byte.*) {
498 '/', '\\' => byte.* = fs.path.sep,
499 else => {},
500 }
501 }
502 return the_copy;
503 }
504
505 /// Duplicates a package recursively.
506 pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
507 var the_copy = Pkg{
508 .name = self.dupe(package.name),
509 .source = package.source.dupe(self),
510 };
511
512 if (package.dependencies) |dependencies| {
513 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
514 the_copy.dependencies = new_dependencies;
515
516 for (dependencies) |dep_package, i| {
517 new_dependencies[i] = self.dupePkg(dep_package);
518 }
519 }
520 return the_copy;
521 }
522
523 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
524 const write_file_step = self.addWriteFiles();
525 write_file_step.add(file_path, data);
526 return write_file_step;
527 }
528
529 pub fn addWriteFiles(self: *Builder) *WriteFileStep {
530 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
531 write_file_step.* = WriteFileStep.init(self);
532 return write_file_step;
533 }
534
535 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
536 const data = self.fmt(format, args);
537 const log_step = self.allocator.create(LogStep) catch unreachable;
538 log_step.* = LogStep.init(self, data);
539 return log_step;
540 }
541
542 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
543 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
544 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
545 return remove_dir_step;
546 }
547
548 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
549 return FmtStep.create(self, paths);
550 }
551
552 pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep {
553 return TranslateCStep.create(self, source.dupe(self));
554 }
555
556 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
557 _ = self;
558 return .{
559 .versioned = .{
560 .major = major,
561 .minor = minor,
562 .patch = patch,
563 },
564 };
565 }
566
567 pub fn make(self: *Builder, step_names: []const []const u8) !void {
568 try self.makePath(self.cache_root);
569
570 var wanted_steps = ArrayList(*Step).init(self.allocator);
571 defer wanted_steps.deinit();
572
573 if (step_names.len == 0) {
574 try wanted_steps.append(self.default_step);
575 } else {
576 for (step_names) |step_name| {
577 const s = try self.getTopLevelStepByName(step_name);
578 try wanted_steps.append(s);
579 }
580 }
581
582 for (wanted_steps.items) |s| {
583 try self.makeOneStep(s);
584 }
585 }
586
587 pub fn getInstallStep(self: *Builder) *Step {
588 return &self.install_tls.step;
589 }
590
591 pub fn getUninstallStep(self: *Builder) *Step {
592 return &self.uninstall_tls.step;
593 }
594
595 fn makeUninstall(uninstall_step: *Step) anyerror!void {
596 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
597 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
598
599 for (self.installed_files.items) |installed_file| {
600 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
601 if (self.verbose) {
602 log.info("rm {s}", .{full_path});
603 }
604 fs.cwd().deleteTree(full_path) catch {};
605 }
606
607 // TODO remove empty directories
608 }
609
610 fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
611 if (s.loop_flag) {
612 log.err("Dependency loop detected:\n {s}", .{s.name});
613 return error.DependencyLoopDetected;
614 }
615 s.loop_flag = true;
616
617 for (s.dependencies.items) |dep| {
618 self.makeOneStep(dep) catch |err| {
619 if (err == error.DependencyLoopDetected) {
620 log.err(" {s}", .{s.name});
621 }
622 return err;
623 };
624 }
625
626 s.loop_flag = false;
627
628 try s.make();
629 }
630
631 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
632 for (self.top_level_steps.items) |top_level_step| {
633 if (mem.eql(u8, top_level_step.step.name, name)) {
634 return &top_level_step.step;
635 }
636 }
637 log.err("Cannot run step '{s}' because it does not exist", .{name});
638 return error.InvalidStepName;
639 }
640
641 pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
642 const name = self.dupe(name_raw);
643 const description = self.dupe(description_raw);
644 const type_id = comptime typeToEnum(T);
645 const enum_options = if (type_id == .@"enum") blk: {
646 const fields = comptime std.meta.fields(T);
647 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
648
649 inline for (fields) |field| {
650 options.appendAssumeCapacity(field.name);
651 }
652
653 break :blk options.toOwnedSlice() catch unreachable;
654 } else null;
655 const available_option = AvailableOption{
656 .name = name,
657 .type_id = type_id,
658 .description = description,
659 .enum_options = enum_options,
660 };
661 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
662 panic("Option '{s}' declared twice", .{name});
663 }
664 self.available_options_list.append(available_option) catch unreachable;
665
666 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
667 option_ptr.used = true;
668 switch (type_id) {
669 .bool => switch (option_ptr.value) {
670 .flag => return true,
671 .scalar => |s| {
672 if (mem.eql(u8, s, "true")) {
673 return true;
674 } else if (mem.eql(u8, s, "false")) {
675 return false;
676 } else {
677 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
678 self.markInvalidUserInput();
679 return null;
680 }
681 },
682 .list => {
683 log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name});
684 self.markInvalidUserInput();
685 return null;
686 },
687 },
688 .int => switch (option_ptr.value) {
689 .flag => {
690 log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name});
691 self.markInvalidUserInput();
692 return null;
693 },
694 .scalar => |s| {
695 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
696 error.Overflow => {
697 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
698 self.markInvalidUserInput();
699 return null;
700 },
701 else => {
702 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
703 self.markInvalidUserInput();
704 return null;
705 },
706 };
707 return n;
708 },
709 .list => {
710 log.err("Expected -D{s} to be an integer, but received a list.\n", .{name});
711 self.markInvalidUserInput();
712 return null;
713 },
714 },
715 .float => switch (option_ptr.value) {
716 .flag => {
717 log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name});
718 self.markInvalidUserInput();
719 return null;
720 },
721 .scalar => |s| {
722 const n = std.fmt.parseFloat(T, s) catch {
723 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
724 self.markInvalidUserInput();
725 return null;
726 };
727 return n;
728 },
729 .list => {
730 log.err("Expected -D{s} to be a float, but received a list.\n", .{name});
731 self.markInvalidUserInput();
732 return null;
733 },
734 },
735 .@"enum" => switch (option_ptr.value) {
736 .flag => {
737 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
738 self.markInvalidUserInput();
739 return null;
740 },
741 .scalar => |s| {
742 if (std.meta.stringToEnum(T, s)) |enum_lit| {
743 return enum_lit;
744 } else {
745 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
746 self.markInvalidUserInput();
747 return null;
748 }
749 },
750 .list => {
751 log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
752 self.markInvalidUserInput();
753 return null;
754 },
755 },
756 .string => switch (option_ptr.value) {
757 .flag => {
758 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
759 self.markInvalidUserInput();
760 return null;
761 },
762 .list => {
763 log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
764 self.markInvalidUserInput();
765 return null;
766 },
767 .scalar => |s| return s,
768 },
769 .list => switch (option_ptr.value) {
770 .flag => {
771 log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name});
772 self.markInvalidUserInput();
773 return null;
774 },
775 .scalar => |s| {
776 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
777 },
778 .list => |lst| return lst.items,
779 },
780 }
781 }
782
783 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
784 const step_info = self.allocator.create(TopLevelStep) catch unreachable;
785 step_info.* = TopLevelStep{
786 .step = Step.initNoOp(.top_level, name, self.allocator),
787 .description = self.dupe(description),
788 };
789 self.top_level_steps.append(step_info) catch unreachable;
790 return &step_info.step;
791 }
792
793 /// This provides the -Drelease option to the build user and does not give them the choice.
794 pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void {
795 if (self.release_mode != null) {
796 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
797 }
798 const description = self.fmt("Create a release build ({s})", .{@tagName(mode)});
799 self.is_release = self.option(bool, "release", description) orelse false;
800 self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug;
801 }
802
803 /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user
804 /// the choice of what kind of release.
805 pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode {
806 if (self.release_mode) |mode| return mode;
807
808 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
809 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
810 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
811
812 const mode = if (release_safe and !release_fast and !release_small)
813 std.builtin.Mode.ReleaseSafe
814 else if (release_fast and !release_safe and !release_small)
815 std.builtin.Mode.ReleaseFast
816 else if (release_small and !release_fast and !release_safe)
817 std.builtin.Mode.ReleaseSmall
818 else if (!release_fast and !release_safe and !release_small)
819 std.builtin.Mode.Debug
820 else x: {
821 log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{});
822 self.markInvalidUserInput();
823 break :x std.builtin.Mode.Debug;
824 };
825 self.is_release = mode != .Debug;
826 self.release_mode = mode;
827 return mode;
828 }
829
830 pub const StandardTargetOptionsArgs = struct {
831 whitelist: ?[]const CrossTarget = null,
832
833 default_target: CrossTarget = CrossTarget{},
834 };
835
836 /// Exposes standard `zig build` options for choosing a target.
837 pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
838 const maybe_triple = self.option(
839 []const u8,
840 "target",
841 "The CPU architecture, OS, and ABI to build for",
842 );
843 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
844
845 if (maybe_triple == null and mcpu == null) {
846 return args.default_target;
847 }
848
849 const triple = maybe_triple orelse "native";
850
851 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
852 const selected_target = CrossTarget.parse(.{
853 .arch_os_abi = triple,
854 .cpu_features = mcpu,
855 .diagnostics = &diags,
856 }) catch |err| switch (err) {
857 error.UnknownCpuModel => {
858 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
859 diags.cpu_name.?,
860 @tagName(diags.arch.?),
861 });
862 for (diags.arch.?.allCpuModels()) |cpu| {
863 log.err(" {s}", .{cpu.name});
864 }
865 self.markInvalidUserInput();
866 return args.default_target;
867 },
868 error.UnknownCpuFeature => {
869 log.err(
870 \\Unknown CPU feature: '{s}'
871 \\Available CPU features for architecture '{s}':
872 \\
873 , .{
874 diags.unknown_feature_name.?,
875 @tagName(diags.arch.?),
876 });
877 for (diags.arch.?.allFeaturesList()) |feature| {
878 log.err(" {s}: {s}", .{ feature.name, feature.description });
879 }
880 self.markInvalidUserInput();
881 return args.default_target;
882 },
883 error.UnknownOperatingSystem => {
884 log.err(
885 \\Unknown OS: '{s}'
886 \\Available operating systems:
887 \\
888 , .{diags.os_name.?});
889 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
890 log.err(" {s}", .{field.name});
891 }
892 self.markInvalidUserInput();
893 return args.default_target;
894 },
895 else => |e| {
896 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
897 self.markInvalidUserInput();
898 return args.default_target;
899 },
900 };
901
902 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
903
904 if (args.whitelist) |list| whitelist_check: {
905 // Make sure it's a match of one of the list.
906 var mismatch_triple = true;
907 var mismatch_cpu_features = true;
908 var whitelist_item = CrossTarget{};
909 for (list) |t| {
910 mismatch_cpu_features = true;
911 mismatch_triple = true;
912
913 const t_triple = t.zigTriple(self.allocator) catch unreachable;
914 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
915 mismatch_triple = false;
916 whitelist_item = t;
917 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
918 mismatch_cpu_features = false;
919 break :whitelist_check;
920 } else {
921 break;
922 }
923 }
924 }
925 if (mismatch_triple) {
926 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
927 selected_canonicalized_triple,
928 });
929 for (list) |t| {
930 const t_triple = t.zigTriple(self.allocator) catch unreachable;
931 log.err(" {s}", .{t_triple});
932 }
933 } else {
934 assert(mismatch_cpu_features);
935 const whitelist_cpu = whitelist_item.getCpu();
936 const selected_cpu = selected_target.getCpu();
937 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
938 selected_cpu.model.name,
939 });
940 log.err(" Supported feature Set: ", .{});
941 const all_features = whitelist_cpu.arch.allFeaturesList();
942 var populated_cpu_features = whitelist_cpu.model.features;
943 populated_cpu_features.populateDependencies(all_features);
944 for (all_features) |feature, i_usize| {
945 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
946 const in_cpu_set = populated_cpu_features.isEnabled(i);
947 if (in_cpu_set) {
948 log.err("{s} ", .{feature.name});
949 }
950 }
951 log.err(" Remove: ", .{});
952 for (all_features) |feature, i_usize| {
953 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
954 const in_cpu_set = populated_cpu_features.isEnabled(i);
955 const in_actual_set = selected_cpu.features.isEnabled(i);
956 if (in_actual_set and !in_cpu_set) {
957 log.err("{s} ", .{feature.name});
958 }
959 }
960 }
961 self.markInvalidUserInput();
962 return args.default_target;
963 }
964
965 return selected_target;
966 }
967
968 pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
969 const name = self.dupe(name_raw);
970 const value = self.dupe(value_raw);
971 const gop = try self.user_input_options.getOrPut(name);
972 if (!gop.found_existing) {
973 gop.value_ptr.* = UserInputOption{
974 .name = name,
975 .value = .{ .scalar = value },
976 .used = false,
977 };
978 return false;
979 }
980
981 // option already exists
982 switch (gop.value_ptr.value) {
983 .scalar => |s| {
984 // turn it into a list
985 var list = ArrayList([]const u8).init(self.allocator);
986 list.append(s) catch unreachable;
987 list.append(value) catch unreachable;
988 self.user_input_options.put(name, .{
989 .name = name,
990 .value = .{ .list = list },
991 .used = false,
992 }) catch unreachable;
993 },
994 .list => |*list| {
995 // append to the list
996 list.append(value) catch unreachable;
997 self.user_input_options.put(name, .{
998 .name = name,
999 .value = .{ .list = list.* },
1000 .used = false,
1001 }) catch unreachable;
1002 },
1003 .flag => {
1004 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1005 return true;
1006 },
1007 }
1008 return false;
1009 }
1010
1011 pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
1012 const name = self.dupe(name_raw);
1013 const gop = try self.user_input_options.getOrPut(name);
1014 if (!gop.found_existing) {
1015 gop.value_ptr.* = .{
1016 .name = name,
1017 .value = .{ .flag = {} },
1018 .used = false,
1019 };
1020 return false;
1021 }
1022
1023 // option already exists
1024 switch (gop.value_ptr.value) {
1025 .scalar => |s| {
1026 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1027 return true;
1028 },
1029 .list => {
1030 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1031 return true;
1032 },
1033 .flag => {},
1034 }
1035 return false;
1036 }
1037
1038 fn typeToEnum(comptime T: type) TypeId {
1039 return switch (@typeInfo(T)) {
1040 .Int => .int,
1041 .Float => .float,
1042 .Bool => .bool,
1043 .Enum => .@"enum",
1044 else => switch (T) {
1045 []const u8 => .string,
1046 []const []const u8 => .list,
1047 else => @compileError("Unsupported type: " ++ @typeName(T)),
1048 },
1049 };
1050 }
1051
1052 fn markInvalidUserInput(self: *Builder) void {
1053 self.invalid_user_input = true;
1054 }
1055
1056 pub fn validateUserInputDidItFail(self: *Builder) bool {
1057 // make sure all args are used
1058 var it = self.user_input_options.iterator();
1059 while (it.next()) |entry| {
1060 if (!entry.value_ptr.used) {
1061 log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*});
1062 self.markInvalidUserInput();
1063 }
1064 }
1065
1066 return self.invalid_user_input;
1067 }
1068
1069 pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
1070 return self.spawnChildEnvMap(null, self.env_map, argv);
1071 }
1072
1073 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1074 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1075 for (argv) |arg| {
1076 std.debug.print("{s} ", .{arg});
1077 }
1078 std.debug.print("\n", .{});
1079 }
1080
1081 pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1082 if (self.verbose) {
1083 printCmd(cwd, argv);
1084 }
1085
1086 if (!std.process.can_spawn)
1087 return error.ExecNotSupported;
1088
1089 var child = std.ChildProcess.init(argv, self.allocator);
1090 child.cwd = cwd;
1091 child.env_map = env_map;
1092
1093 const term = child.spawnAndWait() catch |err| {
1094 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1095 return err;
1096 };
1097
1098 switch (term) {
1099 .Exited => |code| {
1100 if (code != 0) {
1101 log.err("The following command exited with error code {}:", .{code});
1102 printCmd(cwd, argv);
1103 return error.UncleanExit;
1104 }
1105 },
1106 else => {
1107 log.err("The following command terminated unexpectedly:", .{});
1108 printCmd(cwd, argv);
1109
1110 return error.UncleanExit;
1111 },
1112 }
1113 }
1114
1115 pub fn makePath(self: *Builder, path: []const u8) !void {
1116 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1117 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1118 return err;
1119 };
1120 }
1121
1122 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
1123 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1124 }
1125
1126 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
1127 return InstallArtifactStep.create(self, artifact);
1128 }
1129
1130 ///`dest_rel_path` is relative to prefix path
1131 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1132 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1133 }
1134
1135 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
1136 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1137 }
1138
1139 ///`dest_rel_path` is relative to bin path
1140 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1141 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1142 }
1143
1144 ///`dest_rel_path` is relative to lib path
1145 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1146 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1147 }
1148
1149 /// Output format (BIN vs Intel HEX) determined by filename
1150 pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1151 const raw = self.addInstallRaw(artifact, dest_filename, options);
1152 self.getInstallStep().dependOn(&raw.step);
1153 return raw;
1154 }
1155
1156 ///`dest_rel_path` is relative to install prefix path
1157 pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1158 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1159 }
1160
1161 ///`dest_rel_path` is relative to bin path
1162 pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1163 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1164 }
1165
1166 ///`dest_rel_path` is relative to lib path
1167 pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1168 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1169 }
1170
1171 pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1172 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1173 }
1174
1175 pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1176 return InstallRawStep.create(self, artifact, dest_filename, options);
1177 }
1178
1179 pub fn addInstallFileWithDir(
1180 self: *Builder,
1181 source: FileSource,
1182 install_dir: InstallDir,
1183 dest_rel_path: []const u8,
1184 ) *InstallFileStep {
1185 if (dest_rel_path.len == 0) {
1186 panic("dest_rel_path must be non-empty", .{});
1187 }
1188 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
1189 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1190 return install_step;
1191 }
1192
1193 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
1194 const install_step = self.allocator.create(InstallDirStep) catch unreachable;
1195 install_step.* = InstallDirStep.init(self, options);
1196 return install_step;
1197 }
1198
1199 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
1200 const file = InstalledFile{
1201 .dir = dir,
1202 .path = dest_rel_path,
1203 };
1204 self.installed_files.append(file.dupe(self)) catch unreachable;
1205 }
1206
1207 pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
1208 if (self.verbose) {
1209 log.info("cp {s} {s} ", .{ source_path, dest_path });
1210 }
1211 const cwd = fs.cwd();
1212 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1213 if (self.verbose) switch (prev_status) {
1214 .stale => log.info("# installed", .{}),
1215 .fresh => log.info("# up-to-date", .{}),
1216 };
1217 }
1218
1219 pub fn truncateFile(self: *Builder, dest_path: []const u8) !void {
1220 if (self.verbose) {
1221 log.info("truncate {s}", .{dest_path});
1222 }
1223 const cwd = fs.cwd();
1224 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1225 error.FileNotFound => blk: {
1226 if (fs.path.dirname(dest_path)) |dirname| {
1227 try cwd.makePath(dirname);
1228 }
1229 break :blk try cwd.createFile(dest_path, .{});
1230 },
1231 else => |e| return e,
1232 };
1233 src_file.close();
1234 }
1235
1236 pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
1237 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
1238 }
1239
1240 /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable`
1241 pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 {
1242 return fs.path.join(self.allocator, paths) catch unreachable;
1243 }
1244
1245 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
1246 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
1247 }
1248
1249 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1250 // TODO report error for ambiguous situations
1251 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1252 for (self.search_prefixes.items) |search_prefix| {
1253 for (names) |name| {
1254 if (fs.path.isAbsolute(name)) {
1255 return name;
1256 }
1257 const full_path = self.pathJoin(&.{
1258 search_prefix,
1259 "bin",
1260 self.fmt("{s}{s}", .{ name, exe_extension }),
1261 });
1262 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1263 }
1264 }
1265 if (self.env_map.get("PATH")) |PATH| {
1266 for (names) |name| {
1267 if (fs.path.isAbsolute(name)) {
1268 return name;
1269 }
1270 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1271 while (it.next()) |path| {
1272 const full_path = self.pathJoin(&.{
1273 path,
1274 self.fmt("{s}{s}", .{ name, exe_extension }),
1275 });
1276 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1277 }
1278 }
1279 }
1280 for (names) |name| {
1281 if (fs.path.isAbsolute(name)) {
1282 return name;
1283 }
1284 for (paths) |path| {
1285 const full_path = self.pathJoin(&.{
1286 path,
1287 self.fmt("{s}{s}", .{ name, exe_extension }),
1288 });
1289 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1290 }
1291 }
1292 return error.FileNotFound;
1293 }
1294
1295 pub fn execAllowFail(
1296 self: *Builder,
1297 argv: []const []const u8,
1298 out_code: *u8,
1299 stderr_behavior: std.ChildProcess.StdIo,
1300 ) ExecError![]u8 {
1301 assert(argv.len != 0);
1302
1303 if (!std.process.can_spawn)
1304 return error.ExecNotSupported;
1305
1306 const max_output_size = 400 * 1024;
1307 var child = std.ChildProcess.init(argv, self.allocator);
1308 child.stdin_behavior = .Ignore;
1309 child.stdout_behavior = .Pipe;
1310 child.stderr_behavior = stderr_behavior;
1311 child.env_map = self.env_map;
1312
1313 try child.spawn();
1314
1315 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1316 return error.ReadFailure;
1317 };
1318 errdefer self.allocator.free(stdout);
1319
1320 const term = try child.wait();
1321 switch (term) {
1322 .Exited => |code| {
1323 if (code != 0) {
1324 out_code.* = @truncate(u8, code);
1325 return error.ExitCodeFailure;
1326 }
1327 return stdout;
1328 },
1329 .Signal, .Stopped, .Unknown => |code| {
1330 out_code.* = @truncate(u8, code);
1331 return error.ProcessTerminated;
1332 },
1333 }
1334 }
1335
1336 pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1337 assert(argv.len != 0);
1338
1339 if (self.verbose) {
1340 printCmd(null, argv);
1341 }
1342
1343 if (!std.process.can_spawn) {
1344 if (src_step) |s| log.err("{s}...", .{s.name});
1345 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1346 printCmd(null, argv);
1347 std.os.abort();
1348 }
1349
1350 var code: u8 = undefined;
1351 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1352 error.ExecNotSupported => {
1353 if (src_step) |s| log.err("{s}...", .{s.name});
1354 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1355 printCmd(null, argv);
1356 std.os.abort();
1357 },
1358 error.FileNotFound => {
1359 if (src_step) |s| log.err("{s}...", .{s.name});
1360 log.err("Unable to spawn the following command: file not found", .{});
1361 printCmd(null, argv);
1362 std.os.exit(@truncate(u8, code));
1363 },
1364 error.ExitCodeFailure => {
1365 if (src_step) |s| log.err("{s}...", .{s.name});
1366 if (self.prominent_compile_errors) {
1367 log.err("The step exited with error code {d}", .{code});
1368 } else {
1369 log.err("The following command exited with error code {d}:", .{code});
1370 printCmd(null, argv);
1371 }
1372
1373 std.os.exit(@truncate(u8, code));
1374 },
1375 error.ProcessTerminated => {
1376 if (src_step) |s| log.err("{s}...", .{s.name});
1377 log.err("The following command terminated unexpectedly:", .{});
1378 printCmd(null, argv);
1379 std.os.exit(@truncate(u8, code));
1380 },
1381 else => |e| return e,
1382 };
1383 }
1384
1385 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
1386 return self.execFromStep(argv, null);
1387 }
1388
1389 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
1390 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
1391 }
1392
1393 pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1394 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1395 const base_dir = switch (dir) {
1396 .prefix => self.install_path,
1397 .bin => self.exe_dir,
1398 .lib => self.lib_dir,
1399 .header => self.h_dir,
1400 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1401 };
1402 return fs.path.resolve(
1403 self.allocator,
1404 &[_][]const u8{ base_dir, dest_rel_path },
1405 ) catch unreachable;
1406 }
1407
1408 pub const Dependency = struct {
1409 builder: *Builder,
1410
1411 pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1412 var found: ?*LibExeObjStep = null;
1413 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1414 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1415 if (mem.eql(u8, inst.artifact.name, name)) {
1416 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1417 found = inst.artifact;
1418 }
1419 }
1420 return found orelse {
1421 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1422 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1423 log.info("available artifact: '{s}'", .{inst.artifact.name});
1424 }
1425 panic("unable to find artifact '{s}'", .{name});
1426 };
1427 }
1428 };
1429
1430 pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
1431 const build_runner = @import("root");
1432 const deps = build_runner.dependencies;
1433
1434 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1435 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1436 mem.endsWith(u8, decl.name, name) and
1437 decl.name.len == b.dep_prefix.len + name.len)
1438 {
1439 const build_zig = @field(deps.imports, decl.name);
1440 const build_root = @field(deps.build_root, decl.name);
1441 return dependencyInner(b, name, build_root, build_zig, args);
1442 }
1443 }
1444
1445 const full_path = b.pathFromRoot("build.zig.ini");
1446 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1447 std.process.exit(1);
1448 }
1449
1450 fn dependencyInner(
1451 b: *Builder,
1452 name: []const u8,
1453 build_root: []const u8,
1454 comptime build_zig: type,
1455 args: anytype,
1456 ) *Dependency {
1457 const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1458 sub_builder.runBuild(build_zig) catch unreachable;
1459 const dep = b.allocator.create(Dependency) catch unreachable;
1460 dep.* = .{ .builder = sub_builder };
1461 return dep;
1462 }
1463
1464 pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
1465 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1466 .Void => build_zig.build(b),
1467 .ErrorUnion => try build_zig.build(b),
1468 else => @compileError("expected return type of build to be 'void' or '!void'"),
1469 }
1470 }
1471};
1472
1473test "builder.findProgram compiles" {
1474 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1475
1476 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1477 defer arena.deinit();
1478
1479 const builder = try Builder.create(
1480 arena.allocator(),
1481 "zig",
1482 "zig-cache",
1483 "zig-cache",
1484 "zig-cache",
1485 );
1486 defer builder.destroy();
1487 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1488}
1489
1490pub const Pkg = struct {
1491 name: []const u8,
1492 source: FileSource,
1493 dependencies: ?[]const Pkg = null,
1494};
1495
1496/// A file that is generated by a build step.
1497/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1498pub const GeneratedFile = struct {
1499 /// The step that generates the file
1500 step: *Step,
1501
1502 /// The path to the generated file. Must be either absolute or relative to the build root.
1503 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1504 path: ?[]const u8 = null,
1505
1506 pub fn getPath(self: GeneratedFile) []const u8 {
1507 return self.path orelse std.debug.panic(
1508 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1509 .{self.step.name},
1510 );
1511 }
1512};
1513
1514/// A file source is a reference to an existing or future file.
1515///
1516pub const FileSource = union(enum) {
1517 /// A plain file path, relative to build root or absolute.
1518 path: []const u8,
1519
1520 /// A file that is generated by an interface. Those files usually are
1521 /// not available until built by a build step.
1522 generated: *const GeneratedFile,
1523
1524 /// Returns a new file source that will have a relative path to the build root guaranteed.
1525 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1526 pub fn relative(path: []const u8) FileSource {
1527 std.debug.assert(!std.fs.path.isAbsolute(path));
1528 return FileSource{ .path = path };
1529 }
1530
1531 /// Returns a string that can be shown to represent the file source.
1532 /// Either returns the path or `"generated"`.
1533 pub fn getDisplayName(self: FileSource) []const u8 {
1534 return switch (self) {
1535 .path => self.path,
1536 .generated => "generated",
1537 };
1538 }
1539
1540 /// Adds dependencies this file source implies to the given step.
1541 pub fn addStepDependencies(self: FileSource, step: *Step) void {
1542 switch (self) {
1543 .path => {},
1544 .generated => |gen| step.dependOn(gen.step),
1545 }
1546 }
1547
1548 /// Should only be called during make(), returns a path relative to the build root or absolute.
1549 pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
1550 const path = switch (self) {
1551 .path => |p| builder.pathFromRoot(p),
1552 .generated => |gen| gen.getPath(),
1553 };
1554 return path;
1555 }
1556
1557 /// Duplicates the file source for a given builder.
1558 pub fn dupe(self: FileSource, b: *Builder) FileSource {
1559 return switch (self) {
1560 .path => |p| .{ .path = b.dupePath(p) },
1561 .generated => |gen| .{ .generated = gen },
1562 };
1563 }
1564};
1565
1566/// Allocates a new string for assigning a value to a named macro.
1567/// If the value is omitted, it is set to 1.
1568/// `name` and `value` need not live longer than the function call.
1569pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1570 var macro = allocator.alloc(
1571 u8,
1572 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1573 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1574 mem.copy(u8, macro, name);
1575 if (value) |value_slice| {
1576 macro[name.len] = '=';
1577 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1578 }
1579 return macro;
1580}
1581
1582/// deprecated: use `InstallDirStep.Options`
1583pub const InstallDirectoryOptions = InstallDirStep.Options;
1584
1585pub const Step = struct {
1586 id: Id,
1587 name: []const u8,
1588 makeFn: MakeFn,
1589 dependencies: ArrayList(*Step),
1590 loop_flag: bool,
1591 done_flag: bool,
1592
1593 const MakeFn = *const fn (self: *Step) anyerror!void;
1594
1595 pub const Id = enum {
1596 top_level,
1597 lib_exe_obj,
1598 install_artifact,
1599 install_file,
1600 install_dir,
1601 log,
1602 remove_dir,
1603 fmt,
1604 translate_c,
1605 write_file,
1606 run,
1607 emulatable_run,
1608 check_file,
1609 check_object,
1610 config_header,
1611 install_raw,
1612 options,
1613 custom,
1614
1615 pub fn Type(comptime id: Id) type {
1616 return switch (id) {
1617 .top_level => Builder.TopLevelStep,
1618 .lib_exe_obj => LibExeObjStep,
1619 .install_artifact => InstallArtifactStep,
1620 .install_file => InstallFileStep,
1621 .install_dir => InstallDirStep,
1622 .log => LogStep,
1623 .remove_dir => RemoveDirStep,
1624 .fmt => FmtStep,
1625 .translate_c => TranslateCStep,
1626 .write_file => WriteFileStep,
1627 .run => RunStep,
1628 .emulatable_run => EmulatableRunStep,
1629 .check_file => CheckFileStep,
1630 .check_object => CheckObjectStep,
1631 .config_header => ConfigHeaderStep,
1632 .install_raw => InstallRawStep,
1633 .options => OptionsStep,
1634 .custom => @compileError("no type available for custom step"),
1635 };
1636 }
1637 };
1638
1639 pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step {
1640 return Step{
1641 .id = id,
1642 .name = allocator.dupe(u8, name) catch unreachable,
1643 .makeFn = makeFn,
1644 .dependencies = ArrayList(*Step).init(allocator),
1645 .loop_flag = false,
1646 .done_flag = false,
1647 };
1648 }
1649 pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
1650 return init(id, name, allocator, makeNoOp);
1651 }
1652
1653 pub fn make(self: *Step) !void {
1654 if (self.done_flag) return;
1655
1656 try self.makeFn(self);
1657 self.done_flag = true;
1658 }
1659
1660 pub fn dependOn(self: *Step, other: *Step) void {
1661 self.dependencies.append(other) catch unreachable;
1662 }
1663
1664 fn makeNoOp(self: *Step) anyerror!void {
1665 _ = self;
1666 }
1667
1668 pub fn cast(step: *Step, comptime T: type) ?*T {
1669 if (step.id == T.base_id) {
1670 return @fieldParentPtr(T, "step", step);
1671 }
1672 return null;
1673 }
1674};
1675
1676pub const VcpkgRoot = union(VcpkgRootStatus) {
1677 unattempted: void,
1678 not_found: void,
1679 found: []const u8,
1680};
1681
1682pub const VcpkgRootStatus = enum {
1683 unattempted,
1684 not_found,
1685 found,
1686};
1687
1688pub const InstallDir = union(enum) {
1689 prefix: void,
1690 lib: void,
1691 bin: void,
1692 header: void,
1693 /// A path relative to the prefix
1694 custom: []const u8,
1695
1696 /// Duplicates the install directory including the path if set to custom.
1697 pub fn dupe(self: InstallDir, builder: *Builder) InstallDir {
1698 if (self == .custom) {
1699 // Written with this temporary to avoid RLS problems
1700 const duped_path = builder.dupe(self.custom);
1701 return .{ .custom = duped_path };
1702 } else {
1703 return self;
1704 }
1705 }
1706};
1707
1708pub const InstalledFile = struct {
1709 dir: InstallDir,
1710 path: []const u8,
1711
1712 /// Duplicates the installed file path and directory.
1713 pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
1714 return .{
1715 .dir = self.dir.dupe(builder),
1716 .path = builder.dupe(self.path),
1717 };
1718 }
1719};
1720
1721test "dupePkg()" {
1722 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1723
1724 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1725 defer arena.deinit();
1726 var builder = try Builder.create(
1727 arena.allocator(),
1728 "test",
1729 "test",
1730 "test",
1731 "test",
1732 );
1733 defer builder.destroy();
1734
1735 var pkg_dep = Pkg{
1736 .name = "pkg_dep",
1737 .source = .{ .path = "/not/a/pkg_dep.zig" },
1738 };
1739 var pkg_top = Pkg{
1740 .name = "pkg_top",
1741 .source = .{ .path = "/not/a/pkg_top.zig" },
1742 .dependencies = &[_]Pkg{pkg_dep},
1743 };
1744 const dupe = builder.dupePkg(pkg_top);
1745
1746 const original_deps = pkg_top.dependencies.?;
1747 const dupe_deps = dupe.dependencies.?;
1748
1749 // probably the same top level package details
1750 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1751
1752 // probably the same dependencies
1753 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1754 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1755
1756 // could segfault otherwise if pointers in duplicated package's fields are
1757 // the same as those in stack allocated package's fields
1758 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1759 try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
1760 try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr);
1761 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1762 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1763}
1764
1765test {
1766 _ = CheckFileStep;
1767 _ = CheckObjectStep;
1768 _ = EmulatableRunStep;
1769 _ = FmtStep;
1770 _ = InstallArtifactStep;
1771 _ = InstallDirStep;
1772 _ = InstallFileStep;
1773 _ = InstallRawStep;
1774 _ = LibExeObjStep;
1775 _ = LogStep;
1776 _ = OptionsStep;
1777 _ = RemoveDirStep;
1778 _ = RunStep;
1779 _ = TranslateCStep;
1780 _ = WriteFileStep;
1781}
lib/std/build/CheckFileStep.zig deleted-53
......@@ -1,53 +0,0 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const mem = std.mem;
7
8const CheckFileStep = @This();
9
10pub const base_id = .check_file;
11
12step: Step,
13builder: *Builder,
14expected_matches: []const []const u8,
15source: build.FileSource,
16max_bytes: usize = 20 * 1024 * 1024,
17
18pub fn create(
19 builder: *Builder,
20 source: build.FileSource,
21 expected_matches: []const []const u8,
22) *CheckFileStep {
23 const self = builder.allocator.create(CheckFileStep) catch unreachable;
24 self.* = CheckFileStep{
25 .builder = builder,
26 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
27 .source = source.dupe(builder),
28 .expected_matches = builder.dupeStrings(expected_matches),
29 };
30 self.source.addStepDependencies(&self.step);
31 return self;
32}
33
34fn make(step: *Step) !void {
35 const self = @fieldParentPtr(CheckFileStep, "step", step);
36
37 const src_path = self.source.getPath(self.builder);
38 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
39
40 for (self.expected_matches) |expected_match| {
41 if (mem.indexOf(u8, contents, expected_match) == null) {
42 std.debug.print(
43 \\
44 \\========= Expected to find: ===================
45 \\{s}
46 \\========= But file does not contain it: =======
47 \\{s}
48 \\
49 , .{ expected_match, contents });
50 return error.TestFailed;
51 }
52 }
53}
lib/std/build/CheckObjectStep.zig deleted-1026
......@@ -1,1026 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const build = std.build;
4const fs = std.fs;
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const testing = std.testing;
9
10const CheckObjectStep = @This();
11
12const Allocator = mem.Allocator;
13const Builder = build.Builder;
14const Step = build.Step;
15const EmulatableRunStep = build.EmulatableRunStep;
16
17pub const base_id = .check_object;
18
19step: Step,
20builder: *Builder,
21source: build.FileSource,
22max_bytes: usize = 20 * 1024 * 1024,
23checks: std.ArrayList(Check),
24dump_symtab: bool = false,
25obj_format: std.Target.ObjectFormat,
26
27pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
28 const gpa = builder.allocator;
29 const self = gpa.create(CheckObjectStep) catch unreachable;
30 self.* = .{
31 .builder = builder,
32 .step = Step.init(.check_file, "CheckObject", gpa, make),
33 .source = source.dupe(builder),
34 .checks = std.ArrayList(Check).init(gpa),
35 .obj_format = obj_format,
36 };
37 self.source.addStepDependencies(&self.step);
38 return self;
39}
40
41/// Runs and (optionally) compares the output of a binary.
42/// Asserts `self` was generated from an executable step.
43pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
44 const dependencies_len = self.step.dependencies.items.len;
45 assert(dependencies_len > 0);
46 const exe_step = self.step.dependencies.items[dependencies_len - 1];
47 const exe = exe_step.cast(std.build.LibExeObjStep).?;
48 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
49 emulatable_step.step.dependOn(&self.step);
50 return emulatable_step;
51}
52
53/// There two types of actions currently suported:
54/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
55/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
56/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
57/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
58/// it should be plenty useful in its current form.
59/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
60/// using the MatchAction. It currently only supports an addition. The operation is required
61/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
62/// to avoid any parsing really).
63/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
64/// they could then be added with this simple program `vmaddr entryoff +`.
65const Action = struct {
66 tag: enum { match, not_present, compute_cmp },
67 phrase: []const u8,
68 expected: ?ComputeCompareExpected = null,
69
70 /// Will return true if the `phrase` was found in the `haystack`.
71 /// Some examples include:
72 ///
73 /// LC 0 => will match in its entirety
74 /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
75 /// and save under `vmaddr` global name (see `global_vars` param)
76 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
77 /// in that order with other letters in between
78 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
79 assert(act.tag == .match or act.tag == .not_present);
80
81 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
82 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
83 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
84
85 while (needle_it.next()) |needle_tok| {
86 const hay_tok = hay_it.next() orelse return false;
87
88 if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
89 // We have fuzzy matchers within the search pattern, so we match substrings.
90 var start = index;
91 var n_tok = needle_tok;
92 var h_tok = hay_tok;
93 while (true) {
94 n_tok = n_tok[start + 3 ..];
95 const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
96 n_tok[0..sub_end]
97 else
98 n_tok;
99 if (mem.indexOf(u8, h_tok, inner) == null) return false;
100 start = mem.indexOf(u8, n_tok, "{*}") orelse break;
101 }
102 } else if (mem.startsWith(u8, needle_tok, "{")) {
103 const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
104 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
105
106 const name = needle_tok[1..closing_brace];
107 if (name.len == 0) return error.MissingBraceValue;
108 const value = try std.fmt.parseInt(u64, hay_tok, 16);
109 candidate_var = .{
110 .name = name,
111 .value = value,
112 };
113 } else {
114 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
115 }
116 }
117
118 if (candidate_var) |v| {
119 try global_vars.putNoClobber(v.name, v.value);
120 }
121
122 return true;
123 }
124
125 /// Will return true if the `phrase` is correctly parsed into an RPN program and
126 /// its reduced, computed value compares using `op` with the expected value, either
127 /// a literal or another extracted variable.
128 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
129 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
130 var values = std.ArrayList(u64).init(gpa);
131
132 var it = mem.tokenize(u8, act.phrase, " ");
133 while (it.next()) |next| {
134 if (mem.eql(u8, next, "+")) {
135 try op_stack.append(.add);
136 } else if (mem.eql(u8, next, "-")) {
137 try op_stack.append(.sub);
138 } else if (mem.eql(u8, next, "%")) {
139 try op_stack.append(.mod);
140 } else if (mem.eql(u8, next, "*")) {
141 try op_stack.append(.mul);
142 } else {
143 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
144 break :blk global_vars.get(next) orelse {
145 std.debug.print(
146 \\
147 \\========= Variable was not extracted: ===========
148 \\{s}
149 \\
150 , .{next});
151 return error.UnknownVariable;
152 };
153 };
154 try values.append(val);
155 }
156 }
157
158 var op_i: usize = 1;
159 var reduced: u64 = values.items[0];
160 for (op_stack.items) |op| {
161 const other = values.items[op_i];
162 switch (op) {
163 .add => {
164 reduced += other;
165 },
166 .sub => {
167 reduced -= other;
168 },
169 .mod => {
170 reduced %= other;
171 },
172 .mul => {
173 reduced *= other;
174 },
175 }
176 op_i += 1;
177 }
178
179 const exp_value = switch (act.expected.?.value) {
180 .variable => |name| global_vars.get(name) orelse {
181 std.debug.print(
182 \\
183 \\========= Variable was not extracted: ===========
184 \\{s}
185 \\
186 , .{name});
187 return error.UnknownVariable;
188 },
189 .literal => |x| x,
190 };
191 return math.compare(reduced, act.expected.?.op, exp_value);
192 }
193};
194
195const ComputeCompareExpected = struct {
196 op: math.CompareOperator,
197 value: union(enum) {
198 variable: []const u8,
199 literal: u64,
200 },
201
202 pub fn format(
203 value: @This(),
204 comptime fmt: []const u8,
205 options: std.fmt.FormatOptions,
206 writer: anytype,
207 ) !void {
208 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
209 _ = options;
210 try writer.print("{s} ", .{@tagName(value.op)});
211 switch (value.value) {
212 .variable => |name| try writer.writeAll(name),
213 .literal => |x| try writer.print("{x}", .{x}),
214 }
215 }
216};
217
218const Check = struct {
219 builder: *Builder,
220 actions: std.ArrayList(Action),
221
222 fn create(b: *Builder) Check {
223 return .{
224 .builder = b,
225 .actions = std.ArrayList(Action).init(b.allocator),
226 };
227 }
228
229 fn match(self: *Check, phrase: []const u8) void {
230 self.actions.append(.{
231 .tag = .match,
232 .phrase = self.builder.dupe(phrase),
233 }) catch unreachable;
234 }
235
236 fn notPresent(self: *Check, phrase: []const u8) void {
237 self.actions.append(.{
238 .tag = .not_present,
239 .phrase = self.builder.dupe(phrase),
240 }) catch unreachable;
241 }
242
243 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
244 self.actions.append(.{
245 .tag = .compute_cmp,
246 .phrase = self.builder.dupe(phrase),
247 .expected = expected,
248 }) catch unreachable;
249 }
250};
251
252/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
253pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
254 var new_check = Check.create(self.builder);
255 new_check.match(phrase);
256 self.checks.append(new_check) catch unreachable;
257}
258
259/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
260/// Asserts at least one check already exists.
261pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
262 assert(self.checks.items.len > 0);
263 const last = &self.checks.items[self.checks.items.len - 1];
264 last.match(phrase);
265}
266
267/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
268/// however ensures there is no matching phrase in the output.
269/// Asserts at least one check already exists.
270pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
271 assert(self.checks.items.len > 0);
272 const last = &self.checks.items[self.checks.items.len - 1];
273 last.notPresent(phrase);
274}
275
276/// Creates a new check checking specifically symbol table parsed and dumped from the object
277/// file.
278/// Issuing this check will force parsing and dumping of the symbol table.
279pub fn checkInSymtab(self: *CheckObjectStep) void {
280 self.dump_symtab = true;
281 const symtab_label = switch (self.obj_format) {
282 .macho => MachODumper.symtab_label,
283 else => @panic("TODO other parsers"),
284 };
285 self.checkStart(symtab_label);
286}
287
288/// Creates a new standalone, singular check which allows running simple binary operations
289/// on the extracted variables. It will then compare the reduced program with the value of
290/// the expected variable.
291pub fn checkComputeCompare(
292 self: *CheckObjectStep,
293 program: []const u8,
294 expected: ComputeCompareExpected,
295) void {
296 var new_check = Check.create(self.builder);
297 new_check.computeCmp(program, expected);
298 self.checks.append(new_check) catch unreachable;
299}
300
301fn make(step: *Step) !void {
302 const self = @fieldParentPtr(CheckObjectStep, "step", step);
303
304 const gpa = self.builder.allocator;
305 const src_path = self.source.getPath(self.builder);
306 const contents = try fs.cwd().readFileAllocOptions(
307 gpa,
308 src_path,
309 self.max_bytes,
310 null,
311 @alignOf(u64),
312 null,
313 );
314
315 const output = switch (self.obj_format) {
316 .macho => try MachODumper.parseAndDump(contents, .{
317 .gpa = gpa,
318 .dump_symtab = self.dump_symtab,
319 }),
320 .elf => @panic("TODO elf parser"),
321 .coff => @panic("TODO coff parser"),
322 .wasm => try WasmDumper.parseAndDump(contents, .{
323 .gpa = gpa,
324 .dump_symtab = self.dump_symtab,
325 }),
326 else => unreachable,
327 };
328
329 var vars = std.StringHashMap(u64).init(gpa);
330
331 for (self.checks.items) |chk| {
332 var it = mem.tokenize(u8, output, "\r\n");
333 for (chk.actions.items) |act| {
334 switch (act.tag) {
335 .match => {
336 while (it.next()) |line| {
337 if (try act.match(line, &vars)) break;
338 } else {
339 std.debug.print(
340 \\
341 \\========= Expected to find: ==========================
342 \\{s}
343 \\========= But parsed file does not contain it: =======
344 \\{s}
345 \\
346 , .{ act.phrase, output });
347 return error.TestFailed;
348 }
349 },
350 .not_present => {
351 while (it.next()) |line| {
352 if (try act.match(line, &vars)) {
353 std.debug.print(
354 \\
355 \\========= Expected not to find: ===================
356 \\{s}
357 \\========= But parsed file does contain it: ========
358 \\{s}
359 \\
360 , .{ act.phrase, output });
361 return error.TestFailed;
362 }
363 }
364 },
365 .compute_cmp => {
366 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
367 error.UnknownVariable => {
368 std.debug.print(
369 \\========= From parsed file: =====================
370 \\{s}
371 \\
372 , .{output});
373 return error.TestFailed;
374 },
375 else => |e| return e,
376 };
377 if (!res) {
378 std.debug.print(
379 \\
380 \\========= Comparison failed for action: ===========
381 \\{s} {}
382 \\========= From parsed file: =======================
383 \\{s}
384 \\
385 , .{ act.phrase, act.expected.?, output });
386 return error.TestFailed;
387 }
388 },
389 }
390 }
391 }
392}
393
394const Opts = struct {
395 gpa: ?Allocator = null,
396 dump_symtab: bool = false,
397};
398
399const MachODumper = struct {
400 const LoadCommandIterator = macho.LoadCommandIterator;
401 const symtab_label = "symtab";
402
403 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
404 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
405 var stream = std.io.fixedBufferStream(bytes);
406 const reader = stream.reader();
407
408 const hdr = try reader.readStruct(macho.mach_header_64);
409 if (hdr.magic != macho.MH_MAGIC_64) {
410 return error.InvalidMagicNumber;
411 }
412
413 var output = std.ArrayList(u8).init(gpa);
414 const writer = output.writer();
415
416 var symtab: []const macho.nlist_64 = undefined;
417 var strtab: []const u8 = undefined;
418 var sections = std.ArrayList(macho.section_64).init(gpa);
419 var imports = std.ArrayList([]const u8).init(gpa);
420
421 var it = LoadCommandIterator{
422 .ncmds = hdr.ncmds,
423 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
424 };
425 var i: usize = 0;
426 while (it.next()) |cmd| {
427 switch (cmd.cmd()) {
428 .SEGMENT_64 => {
429 const seg = cmd.cast(macho.segment_command_64).?;
430 try sections.ensureUnusedCapacity(seg.nsects);
431 for (cmd.getSections()) |sect| {
432 sections.appendAssumeCapacity(sect);
433 }
434 },
435 .SYMTAB => if (opts.dump_symtab) {
436 const lc = cmd.cast(macho.symtab_command).?;
437 symtab = @ptrCast(
438 [*]const macho.nlist_64,
439 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
440 )[0..lc.nsyms];
441 strtab = bytes[lc.stroff..][0..lc.strsize];
442 },
443 .LOAD_DYLIB,
444 .LOAD_WEAK_DYLIB,
445 .REEXPORT_DYLIB,
446 => {
447 try imports.append(cmd.getDylibPathName());
448 },
449 else => {},
450 }
451
452 try dumpLoadCommand(cmd, i, writer);
453 try writer.writeByte('\n');
454
455 i += 1;
456 }
457
458 if (opts.dump_symtab) {
459 try writer.print("{s}\n", .{symtab_label});
460 for (symtab) |sym| {
461 if (sym.stab()) continue;
462 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
463 if (sym.sect()) {
464 const sect = sections.items[sym.n_sect - 1];
465 try writer.print("{x} ({s},{s})", .{
466 sym.n_value,
467 sect.segName(),
468 sect.sectName(),
469 });
470 if (sym.ext()) {
471 try writer.writeAll(" external");
472 }
473 try writer.print(" {s}\n", .{sym_name});
474 } else if (sym.undf()) {
475 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
476 const import_name = blk: {
477 if (ordinal <= 0) {
478 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
479 break :blk "self import";
480 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
481 break :blk "main executable";
482 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
483 break :blk "flat lookup";
484 unreachable;
485 }
486 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
487 const basename = fs.path.basename(full_path);
488 assert(basename.len > 0);
489 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
490 break :blk basename[0..ext];
491 };
492 try writer.writeAll("(undefined)");
493 if (sym.weakRef()) {
494 try writer.writeAll(" weak");
495 }
496 if (sym.ext()) {
497 try writer.writeAll(" external");
498 }
499 try writer.print(" {s} (from {s})\n", .{
500 sym_name,
501 import_name,
502 });
503 } else unreachable;
504 }
505 }
506
507 return output.toOwnedSlice();
508 }
509
510 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
511 // print header first
512 try writer.print(
513 \\LC {d}
514 \\cmd {s}
515 \\cmdsize {d}
516 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
517
518 switch (lc.cmd()) {
519 .SEGMENT_64 => {
520 const seg = lc.cast(macho.segment_command_64).?;
521 try writer.writeByte('\n');
522 try writer.print(
523 \\segname {s}
524 \\vmaddr {x}
525 \\vmsize {x}
526 \\fileoff {x}
527 \\filesz {x}
528 , .{
529 seg.segName(),
530 seg.vmaddr,
531 seg.vmsize,
532 seg.fileoff,
533 seg.filesize,
534 });
535
536 for (lc.getSections()) |sect| {
537 try writer.writeByte('\n');
538 try writer.print(
539 \\sectname {s}
540 \\addr {x}
541 \\size {x}
542 \\offset {x}
543 \\align {x}
544 , .{
545 sect.sectName(),
546 sect.addr,
547 sect.size,
548 sect.offset,
549 sect.@"align",
550 });
551 }
552 },
553
554 .ID_DYLIB,
555 .LOAD_DYLIB,
556 .LOAD_WEAK_DYLIB,
557 .REEXPORT_DYLIB,
558 => {
559 const dylib = lc.cast(macho.dylib_command).?;
560 try writer.writeByte('\n');
561 try writer.print(
562 \\name {s}
563 \\timestamp {d}
564 \\current version {x}
565 \\compatibility version {x}
566 , .{
567 lc.getDylibPathName(),
568 dylib.dylib.timestamp,
569 dylib.dylib.current_version,
570 dylib.dylib.compatibility_version,
571 });
572 },
573
574 .MAIN => {
575 const main = lc.cast(macho.entry_point_command).?;
576 try writer.writeByte('\n');
577 try writer.print(
578 \\entryoff {x}
579 \\stacksize {x}
580 , .{ main.entryoff, main.stacksize });
581 },
582
583 .RPATH => {
584 try writer.writeByte('\n');
585 try writer.print(
586 \\path {s}
587 , .{
588 lc.getRpathPathName(),
589 });
590 },
591
592 .UUID => {
593 const uuid = lc.cast(macho.uuid_command).?;
594 try writer.writeByte('\n');
595 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
596 },
597
598 .DATA_IN_CODE,
599 .FUNCTION_STARTS,
600 .CODE_SIGNATURE,
601 => {
602 const llc = lc.cast(macho.linkedit_data_command).?;
603 try writer.writeByte('\n');
604 try writer.print(
605 \\dataoff {x}
606 \\datasize {x}
607 , .{ llc.dataoff, llc.datasize });
608 },
609
610 .DYLD_INFO_ONLY => {
611 const dlc = lc.cast(macho.dyld_info_command).?;
612 try writer.writeByte('\n');
613 try writer.print(
614 \\rebaseoff {x}
615 \\rebasesize {x}
616 \\bindoff {x}
617 \\bindsize {x}
618 \\weakbindoff {x}
619 \\weakbindsize {x}
620 \\lazybindoff {x}
621 \\lazybindsize {x}
622 \\exportoff {x}
623 \\exportsize {x}
624 , .{
625 dlc.rebase_off,
626 dlc.rebase_size,
627 dlc.bind_off,
628 dlc.bind_size,
629 dlc.weak_bind_off,
630 dlc.weak_bind_size,
631 dlc.lazy_bind_off,
632 dlc.lazy_bind_size,
633 dlc.export_off,
634 dlc.export_size,
635 });
636 },
637
638 .SYMTAB => {
639 const slc = lc.cast(macho.symtab_command).?;
640 try writer.writeByte('\n');
641 try writer.print(
642 \\symoff {x}
643 \\nsyms {x}
644 \\stroff {x}
645 \\strsize {x}
646 , .{
647 slc.symoff,
648 slc.nsyms,
649 slc.stroff,
650 slc.strsize,
651 });
652 },
653
654 .DYSYMTAB => {
655 const dlc = lc.cast(macho.dysymtab_command).?;
656 try writer.writeByte('\n');
657 try writer.print(
658 \\ilocalsym {x}
659 \\nlocalsym {x}
660 \\iextdefsym {x}
661 \\nextdefsym {x}
662 \\iundefsym {x}
663 \\nundefsym {x}
664 \\indirectsymoff {x}
665 \\nindirectsyms {x}
666 , .{
667 dlc.ilocalsym,
668 dlc.nlocalsym,
669 dlc.iextdefsym,
670 dlc.nextdefsym,
671 dlc.iundefsym,
672 dlc.nundefsym,
673 dlc.indirectsymoff,
674 dlc.nindirectsyms,
675 });
676 },
677
678 else => {},
679 }
680 }
681};
682
683const WasmDumper = struct {
684 const symtab_label = "symbols";
685
686 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
687 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
688 if (opts.dump_symtab) {
689 @panic("TODO: Implement symbol table parsing and dumping");
690 }
691
692 var fbs = std.io.fixedBufferStream(bytes);
693 const reader = fbs.reader();
694
695 const buf = try reader.readBytesNoEof(8);
696 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
697 return error.InvalidMagicByte;
698 }
699 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
700 return error.UnsupportedWasmVersion;
701 }
702
703 var output = std.ArrayList(u8).init(gpa);
704 errdefer output.deinit();
705 const writer = output.writer();
706
707 while (reader.readByte()) |current_byte| {
708 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
709 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
710 return err;
711 };
712
713 const section_length = try std.leb.readULEB128(u32, reader);
714 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
715 fbs.pos += section_length;
716 } else |_| {} // reached end of stream
717
718 return output.toOwnedSlice();
719 }
720
721 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
722 var fbs = std.io.fixedBufferStream(data);
723 const reader = fbs.reader();
724
725 try writer.print(
726 \\Section {s}
727 \\size {d}
728 , .{ @tagName(section), data.len });
729
730 switch (section) {
731 .type,
732 .import,
733 .function,
734 .table,
735 .memory,
736 .global,
737 .@"export",
738 .element,
739 .code,
740 .data,
741 => {
742 const entries = try std.leb.readULEB128(u32, reader);
743 try writer.print("\nentries {d}\n", .{entries});
744 try dumpSection(section, data[fbs.pos..], entries, writer);
745 },
746 .custom => {
747 const name_length = try std.leb.readULEB128(u32, reader);
748 const name = data[fbs.pos..][0..name_length];
749 fbs.pos += name_length;
750 try writer.print("\nname {s}\n", .{name});
751
752 if (mem.eql(u8, name, "name")) {
753 try parseDumpNames(reader, writer, data);
754 } else if (mem.eql(u8, name, "producers")) {
755 try parseDumpProducers(reader, writer, data);
756 } else if (mem.eql(u8, name, "target_features")) {
757 try parseDumpFeatures(reader, writer, data);
758 }
759 // TODO: Implement parsing and dumping other custom sections (such as relocations)
760 },
761 .start => {
762 const start = try std.leb.readULEB128(u32, reader);
763 try writer.print("\nstart {d}\n", .{start});
764 },
765 else => {}, // skip unknown sections
766 }
767 }
768
769 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
770 var fbs = std.io.fixedBufferStream(data);
771 const reader = fbs.reader();
772
773 switch (section) {
774 .type => {
775 var i: u32 = 0;
776 while (i < entries) : (i += 1) {
777 const func_type = try reader.readByte();
778 if (func_type != std.wasm.function_type) {
779 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
780 return error.UnexpectedByte;
781 }
782 const params = try std.leb.readULEB128(u32, reader);
783 try writer.print("params {d}\n", .{params});
784 var index: u32 = 0;
785 while (index < params) : (index += 1) {
786 try parseDumpType(std.wasm.Valtype, reader, writer);
787 } else index = 0;
788 const returns = try std.leb.readULEB128(u32, reader);
789 try writer.print("returns {d}\n", .{returns});
790 while (index < returns) : (index += 1) {
791 try parseDumpType(std.wasm.Valtype, reader, writer);
792 }
793 }
794 },
795 .import => {
796 var i: u32 = 0;
797 while (i < entries) : (i += 1) {
798 const module_name_len = try std.leb.readULEB128(u32, reader);
799 const module_name = data[fbs.pos..][0..module_name_len];
800 fbs.pos += module_name_len;
801 const name_len = try std.leb.readULEB128(u32, reader);
802 const name = data[fbs.pos..][0..name_len];
803 fbs.pos += name_len;
804
805 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
806 std.debug.print("Invalid import kind\n", .{});
807 return err;
808 };
809
810 try writer.print(
811 \\module {s}
812 \\name {s}
813 \\kind {s}
814 , .{ module_name, name, @tagName(kind) });
815 try writer.writeByte('\n');
816 switch (kind) {
817 .function => {
818 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
819 },
820 .memory => {
821 try parseDumpLimits(reader, writer);
822 },
823 .global => {
824 try parseDumpType(std.wasm.Valtype, reader, writer);
825 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
826 },
827 .table => {
828 try parseDumpType(std.wasm.RefType, reader, writer);
829 try parseDumpLimits(reader, writer);
830 },
831 }
832 }
833 },
834 .function => {
835 var i: u32 = 0;
836 while (i < entries) : (i += 1) {
837 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
838 }
839 },
840 .table => {
841 var i: u32 = 0;
842 while (i < entries) : (i += 1) {
843 try parseDumpType(std.wasm.RefType, reader, writer);
844 try parseDumpLimits(reader, writer);
845 }
846 },
847 .memory => {
848 var i: u32 = 0;
849 while (i < entries) : (i += 1) {
850 try parseDumpLimits(reader, writer);
851 }
852 },
853 .global => {
854 var i: u32 = 0;
855 while (i < entries) : (i += 1) {
856 try parseDumpType(std.wasm.Valtype, reader, writer);
857 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
858 try parseDumpInit(reader, writer);
859 }
860 },
861 .@"export" => {
862 var i: u32 = 0;
863 while (i < entries) : (i += 1) {
864 const name_len = try std.leb.readULEB128(u32, reader);
865 const name = data[fbs.pos..][0..name_len];
866 fbs.pos += name_len;
867 const kind_byte = try std.leb.readULEB128(u8, reader);
868 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
869 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
870 return err;
871 };
872 const index = try std.leb.readULEB128(u32, reader);
873 try writer.print(
874 \\name {s}
875 \\kind {s}
876 \\index {d}
877 , .{ name, @tagName(kind), index });
878 try writer.writeByte('\n');
879 }
880 },
881 .element => {
882 var i: u32 = 0;
883 while (i < entries) : (i += 1) {
884 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
885 try parseDumpInit(reader, writer);
886
887 const function_indexes = try std.leb.readULEB128(u32, reader);
888 var function_index: u32 = 0;
889 try writer.print("indexes {d}\n", .{function_indexes});
890 while (function_index < function_indexes) : (function_index += 1) {
891 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
892 }
893 }
894 },
895 .code => {}, // code section is considered opaque to linker
896 .data => {
897 var i: u32 = 0;
898 while (i < entries) : (i += 1) {
899 const index = try std.leb.readULEB128(u32, reader);
900 try writer.print("memory index 0x{x}\n", .{index});
901 try parseDumpInit(reader, writer);
902 const size = try std.leb.readULEB128(u32, reader);
903 try writer.print("size {d}\n", .{size});
904 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
905 }
906 },
907 else => unreachable,
908 }
909 }
910
911 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
912 const type_byte = try reader.readByte();
913 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
914 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
915 return err;
916 };
917 try writer.print("type {s}\n", .{@tagName(valtype)});
918 }
919
920 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
921 const flags = try std.leb.readULEB128(u8, reader);
922 const min = try std.leb.readULEB128(u32, reader);
923
924 try writer.print("min {x}\n", .{min});
925 if (flags != 0) {
926 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
927 }
928 }
929
930 fn parseDumpInit(reader: anytype, writer: anytype) !void {
931 const byte = try std.leb.readULEB128(u8, reader);
932 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
933 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
934 return err;
935 };
936 switch (opcode) {
937 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
938 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
939 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
940 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
941 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
942 else => unreachable,
943 }
944 const end_opcode = try std.leb.readULEB128(u8, reader);
945 if (end_opcode != std.wasm.opcode(.end)) {
946 std.debug.print("expected 'end' opcode in init expression\n", .{});
947 return error.MissingEndOpcode;
948 }
949 }
950
951 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
952 while (reader.context.pos < data.len) {
953 try parseDumpType(std.wasm.NameSubsection, reader, writer);
954 const size = try std.leb.readULEB128(u32, reader);
955 const entries = try std.leb.readULEB128(u32, reader);
956 try writer.print(
957 \\size {d}
958 \\names {d}
959 , .{ size, entries });
960 try writer.writeByte('\n');
961 var i: u32 = 0;
962 while (i < entries) : (i += 1) {
963 const index = try std.leb.readULEB128(u32, reader);
964 const name_len = try std.leb.readULEB128(u32, reader);
965 const pos = reader.context.pos;
966 const name = data[pos..][0..name_len];
967 reader.context.pos += name_len;
968
969 try writer.print(
970 \\index {d}
971 \\name {s}
972 , .{ index, name });
973 try writer.writeByte('\n');
974 }
975 }
976 }
977
978 fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
979 const field_count = try std.leb.readULEB128(u32, reader);
980 try writer.print("fields {d}\n", .{field_count});
981 var current_field: u32 = 0;
982 while (current_field < field_count) : (current_field += 1) {
983 const field_name_length = try std.leb.readULEB128(u32, reader);
984 const field_name = data[reader.context.pos..][0..field_name_length];
985 reader.context.pos += field_name_length;
986
987 const value_count = try std.leb.readULEB128(u32, reader);
988 try writer.print(
989 \\field_name {s}
990 \\values {d}
991 , .{ field_name, value_count });
992 try writer.writeByte('\n');
993 var current_value: u32 = 0;
994 while (current_value < value_count) : (current_value += 1) {
995 const value_length = try std.leb.readULEB128(u32, reader);
996 const value = data[reader.context.pos..][0..value_length];
997 reader.context.pos += value_length;
998
999 const version_length = try std.leb.readULEB128(u32, reader);
1000 const version = data[reader.context.pos..][0..version_length];
1001 reader.context.pos += version_length;
1002
1003 try writer.print(
1004 \\value_name {s}
1005 \\version {s}
1006 , .{ value, version });
1007 try writer.writeByte('\n');
1008 }
1009 }
1010 }
1011
1012 fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
1013 const feature_count = try std.leb.readULEB128(u32, reader);
1014 try writer.print("features {d}\n", .{feature_count});
1015
1016 var index: u32 = 0;
1017 while (index < feature_count) : (index += 1) {
1018 const prefix_byte = try std.leb.readULEB128(u8, reader);
1019 const name_length = try std.leb.readULEB128(u32, reader);
1020 const feature_name = data[reader.context.pos..][0..name_length];
1021 reader.context.pos += name_length;
1022
1023 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
1024 }
1025 }
1026};
lib/std/build/ConfigHeaderStep.zig deleted-288
......@@ -1,288 +0,0 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.build.Step;
4const Builder = std.build.Builder;
5
6pub const base_id: Step.Id = .config_header;
7
8pub const Style = enum {
9 /// The configure format supported by autotools. It uses `#undef foo` to
10 /// mark lines that can be substituted with different values.
11 autoconf,
12 /// The configure format supported by CMake. It uses `@@FOO@@` and
13 /// `#cmakedefine` for template substitution.
14 cmake,
15};
16
17pub const Value = union(enum) {
18 undef,
19 defined,
20 boolean: bool,
21 int: i64,
22 ident: []const u8,
23 string: []const u8,
24};
25
26step: Step,
27builder: *Builder,
28source: std.build.FileSource,
29style: Style,
30values: std.StringHashMap(Value),
31max_bytes: usize = 2 * 1024 * 1024,
32output_dir: []const u8,
33output_basename: []const u8,
34
35pub fn create(builder: *Builder, source: std.build.FileSource, style: Style) *ConfigHeaderStep {
36 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
37 const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
38 self.* = .{
39 .builder = builder,
40 .step = Step.init(base_id, name, builder.allocator, make),
41 .source = source,
42 .style = style,
43 .values = std.StringHashMap(Value).init(builder.allocator),
44 .output_dir = undefined,
45 .output_basename = "config.h",
46 };
47 switch (source) {
48 .path => |p| {
49 const basename = std.fs.path.basename(p);
50 if (std.mem.endsWith(u8, basename, ".h.in")) {
51 self.output_basename = basename[0 .. basename.len - 3];
52 }
53 },
54 else => {},
55 }
56 return self;
57}
58
59pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
60 return addValuesInner(self, values) catch @panic("OOM");
61}
62
63fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
64 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
65 switch (@typeInfo(field.type)) {
66 .Null => {
67 try self.values.put(field.name, .undef);
68 },
69 .Void => {
70 try self.values.put(field.name, .defined);
71 },
72 .Bool => {
73 try self.values.put(field.name, .{ .boolean = @field(values, field.name) });
74 },
75 .ComptimeInt => {
76 try self.values.put(field.name, .{ .int = @field(values, field.name) });
77 },
78 .EnumLiteral => {
79 try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) });
80 },
81 .Pointer => |ptr| {
82 switch (@typeInfo(ptr.child)) {
83 .Array => |array| {
84 if (ptr.size == .One and array.child == u8) {
85 try self.values.put(field.name, .{ .string = @field(values, field.name) });
86 continue;
87 }
88 },
89 else => {},
90 }
91
92 @compileError("unsupported ConfigHeaderStep value type: " ++
93 @typeName(field.type));
94 },
95 else => @compileError("unsupported ConfigHeaderStep value type: " ++
96 @typeName(field.type)),
97 }
98 }
99}
100
101fn make(step: *Step) !void {
102 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
103 const gpa = self.builder.allocator;
104 const src_path = self.source.getPath(self.builder);
105 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
106
107 // The cache is used here not really as a way to speed things up - because writing
108 // the data to a file would probably be very fast - but as a way to find a canonical
109 // location to put build artifacts.
110
111 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
112 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
113
114 // TODO port the cache system from the compiler to zig std lib. Until then
115 // we construct the path directly, and no "cache hit" detection happens;
116 // the files are always written.
117 // Note there is very similar code over in WriteFileStep
118 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
119 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
120 // random bytes when ConfigHeaderStep implementation is modified in a
121 // non-backwards-compatible way.
122 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
123 hash.update(self.source.getDisplayName());
124 hash.update(contents);
125
126 var digest: [16]u8 = undefined;
127 hash.final(&digest);
128 var hash_basename: [digest.len * 2]u8 = undefined;
129 _ = std.fmt.bufPrint(
130 &hash_basename,
131 "{s}",
132 .{std.fmt.fmtSliceHexLower(&digest)},
133 ) catch unreachable;
134
135 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
136 self.builder.cache_root, "o", &hash_basename,
137 });
138 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
139 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
140 return err;
141 };
142 defer dir.close();
143
144 var values_copy = try self.values.clone();
145 defer values_copy.deinit();
146
147 var output = std.ArrayList(u8).init(gpa);
148 defer output.deinit();
149 try output.ensureTotalCapacity(contents.len);
150
151 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
152
153 switch (self.style) {
154 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
155 .cmake => try render_cmake(contents, &output, &values_copy, src_path),
156 }
157
158 try dir.writeFile(self.output_basename, output.items);
159}
160
161fn render_autoconf(
162 contents: []const u8,
163 output: *std.ArrayList(u8),
164 values_copy: *std.StringHashMap(Value),
165 src_path: []const u8,
166) !void {
167 var any_errors = false;
168 var line_index: u32 = 0;
169 var line_it = std.mem.split(u8, contents, "\n");
170 while (line_it.next()) |line| : (line_index += 1) {
171 if (!std.mem.startsWith(u8, line, "#")) {
172 try output.appendSlice(line);
173 try output.appendSlice("\n");
174 continue;
175 }
176 var it = std.mem.tokenize(u8, line[1..], " \t\r");
177 const undef = it.next().?;
178 if (!std.mem.eql(u8, undef, "undef")) {
179 try output.appendSlice(line);
180 try output.appendSlice("\n");
181 continue;
182 }
183 const name = it.rest();
184 const kv = values_copy.fetchRemove(name) orelse {
185 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
186 src_path, line_index + 1, name,
187 });
188 any_errors = true;
189 continue;
190 };
191 try renderValue(output, name, kv.value);
192 }
193
194 {
195 var it = values_copy.iterator();
196 while (it.next()) |entry| {
197 const name = entry.key_ptr.*;
198 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
199 }
200 }
201
202 if (any_errors) {
203 return error.HeaderConfigFailed;
204 }
205}
206
207fn render_cmake(
208 contents: []const u8,
209 output: *std.ArrayList(u8),
210 values_copy: *std.StringHashMap(Value),
211 src_path: []const u8,
212) !void {
213 var any_errors = false;
214 var line_index: u32 = 0;
215 var line_it = std.mem.split(u8, contents, "\n");
216 while (line_it.next()) |line| : (line_index += 1) {
217 if (!std.mem.startsWith(u8, line, "#")) {
218 try output.appendSlice(line);
219 try output.appendSlice("\n");
220 continue;
221 }
222 var it = std.mem.tokenize(u8, line[1..], " \t\r");
223 const cmakedefine = it.next().?;
224 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
225 try output.appendSlice(line);
226 try output.appendSlice("\n");
227 continue;
228 }
229 const name = it.next() orelse {
230 std.debug.print("{s}:{d}: error: missing define name\n", .{
231 src_path, line_index + 1,
232 });
233 any_errors = true;
234 continue;
235 };
236 const kv = values_copy.fetchRemove(name) orelse {
237 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
238 src_path, line_index + 1, name,
239 });
240 any_errors = true;
241 continue;
242 };
243 try renderValue(output, name, kv.value);
244 }
245
246 {
247 var it = values_copy.iterator();
248 while (it.next()) |entry| {
249 const name = entry.key_ptr.*;
250 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
251 }
252 }
253
254 if (any_errors) {
255 return error.HeaderConfigFailed;
256 }
257}
258
259fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
260 switch (value) {
261 .undef => {
262 try output.appendSlice("/* #undef ");
263 try output.appendSlice(name);
264 try output.appendSlice(" */\n");
265 },
266 .defined => {
267 try output.appendSlice("#define ");
268 try output.appendSlice(name);
269 try output.appendSlice("\n");
270 },
271 .boolean => |b| {
272 try output.appendSlice("#define ");
273 try output.appendSlice(name);
274 try output.appendSlice(" ");
275 try output.appendSlice(if (b) "true\n" else "false\n");
276 },
277 .int => |i| {
278 try output.writer().print("#define {s} {d}\n", .{ name, i });
279 },
280 .ident => |ident| {
281 try output.writer().print("#define {s} {s}\n", .{ name, ident });
282 },
283 .string => |string| {
284 // TODO: use C-specific escaping instead of zig string literals
285 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
286 },
287 }
288}
lib/std/build/EmulatableRunStep.zig deleted-215
......@@ -1,215 +0,0 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const build = std.build;
9const Step = std.build.Step;
10const Builder = std.build.Builder;
11const LibExeObjStep = std.build.LibExeObjStep;
12const RunStep = std.build.RunStep;
13
14const fs = std.fs;
15const process = std.process;
16const EnvMap = process.EnvMap;
17
18const EmulatableRunStep = @This();
19
20pub const base_id = .emulatable_run;
21
22const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
23
24step: Step,
25builder: *Builder,
26
27/// The artifact (executable) to be run by this step
28exe: *LibExeObjStep,
29
30/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
31expected_exit_code: ?u8 = 0,
32
33/// Override this field to modify the environment
34env_map: ?*EnvMap,
35
36/// Set this to modify the current working directory
37cwd: ?[]const u8,
38
39stdout_action: RunStep.StdIoAction = .inherit,
40stderr_action: RunStep.StdIoAction = .inherit,
41
42/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
43/// or through emulation.
44hide_foreign_binaries_warning: bool,
45
46/// Creates a step that will execute the given artifact. This step will allow running the
47/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
48/// When set to false, and the binary is foreign, running the executable is skipped.
49/// Asserts given artifact is an executable.
50pub fn create(builder: *Builder, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep {
51 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
52 const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
53
54 const option_name = "hide-foreign-warnings";
55 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
56 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
57 } else false;
58
59 self.* = .{
60 .builder = builder,
61 .step = Step.init(.emulatable_run, name, builder.allocator, make),
62 .exe = artifact,
63 .env_map = null,
64 .cwd = null,
65 .hide_foreign_binaries_warning = hide_warnings,
66 };
67 self.step.dependOn(&artifact.step);
68
69 return self;
70}
71
72fn make(step: *Step) !void {
73 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
74 const host_info = self.builder.host;
75
76 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
77 defer argv_list.deinit();
78
79 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
80 switch (host_info.getExternalExecutor(self.exe.target_info, .{
81 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
82 .link_libc = self.exe.is_linking_libc,
83 })) {
84 .native => {},
85 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
86 .wine => |bin_name| if (self.builder.enable_wine) {
87 try argv_list.append(bin_name);
88 } else return,
89 .qemu => |bin_name| if (self.builder.enable_qemu) {
90 const glibc_dir_arg = if (need_cross_glibc)
91 self.builder.glibc_runtimes_dir orelse return
92 else
93 null;
94 try argv_list.append(bin_name);
95 if (glibc_dir_arg) |dir| {
96 // TODO look into making this a call to `linuxTriple`. This
97 // needs the directory to be called "i686" rather than
98 // "x86" which is why we do it manually here.
99 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
100 const cpu_arch = self.exe.target.getCpuArch();
101 const os_tag = self.exe.target.getOsTag();
102 const abi = self.exe.target.getAbi();
103 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
104 "i686"
105 else
106 @tagName(cpu_arch);
107 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
108 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
109 });
110
111 try argv_list.append("-L");
112 try argv_list.append(full_dir);
113 }
114 } else return warnAboutForeignBinaries(self),
115 .darling => |bin_name| if (self.builder.enable_darling) {
116 try argv_list.append(bin_name);
117 } else return warnAboutForeignBinaries(self),
118 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
119 try argv_list.append(bin_name);
120 try argv_list.append("--dir=.");
121 } else return warnAboutForeignBinaries(self),
122 else => return warnAboutForeignBinaries(self),
123 }
124
125 if (self.exe.target.isWindows()) {
126 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
127 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
128 }
129
130 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
131 try argv_list.append(executable_path);
132
133 try RunStep.runCommand(
134 argv_list.items,
135 self.builder,
136 self.expected_exit_code,
137 self.stdout_action,
138 self.stderr_action,
139 .Inherit,
140 self.env_map,
141 self.cwd,
142 false,
143 );
144}
145
146pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
155 if (step.hide_foreign_binaries_warning) return;
156 const builder = step.builder;
157 const artifact = step.exe;
158
159 const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
160 const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
161 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
162 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
163 switch (builder.host.getExternalExecutor(target_info, .{
164 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
165 .link_libc = artifact.is_linking_libc,
166 })) {
167 .native => unreachable,
168 .bad_dl => |foreign_dl| {
169 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
170 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
171 host_dl, foreign_dl, host_dl,
172 });
173 },
174 .bad_os_or_cpu => {
175 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
176 host_name, foreign_name,
177 });
178 },
179 .darling => if (!builder.enable_darling) {
180 std.debug.print(
181 "the host system ({s}) does not appear to be capable of executing binaries " ++
182 "from the target ({s}). Consider enabling darling.\n",
183 .{ host_name, foreign_name },
184 );
185 },
186 .rosetta => if (!builder.enable_rosetta) {
187 std.debug.print(
188 "the host system ({s}) does not appear to be capable of executing binaries " ++
189 "from the target ({s}). Consider enabling rosetta.\n",
190 .{ host_name, foreign_name },
191 );
192 },
193 .wine => if (!builder.enable_wine) {
194 std.debug.print(
195 "the host system ({s}) does not appear to be capable of executing binaries " ++
196 "from the target ({s}). Consider enabling wine.\n",
197 .{ host_name, foreign_name },
198 );
199 },
200 .qemu => if (!builder.enable_qemu) {
201 std.debug.print(
202 "the host system ({s}) does not appear to be capable of executing binaries " ++
203 "from the target ({s}). Consider enabling qemu.\n",
204 .{ host_name, foreign_name },
205 );
206 },
207 .wasmtime => {
208 std.debug.print(
209 "the host system ({s}) does not appear to be capable of executing binaries " ++
210 "from the target ({s}). Consider enabling wasmtime.\n",
211 .{ host_name, foreign_name },
212 );
213 },
214 }
215}
lib/std/build/FmtStep.zig deleted-37
......@@ -1,37 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const BufMap = std.BufMap;
6const mem = std.mem;
7
8const FmtStep = @This();
9
10pub const base_id = .fmt;
11
12step: Step,
13builder: *Builder,
14argv: [][]const u8,
15
16pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep {
17 const self = builder.allocator.create(FmtStep) catch unreachable;
18 const name = "zig fmt";
19 self.* = FmtStep{
20 .step = Step.init(.fmt, name, builder.allocator, make),
21 .builder = builder,
22 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable,
23 };
24
25 self.argv[0] = builder.zig_exe;
26 self.argv[1] = "fmt";
27 for (paths) |path, i| {
28 self.argv[2 + i] = builder.pathFromRoot(path);
29 }
30 return self;
31}
32
33fn make(step: *Step) !void {
34 const self = @fieldParentPtr(FmtStep, "step", step);
35
36 return self.builder.spawnChild(self.argv);
37}
lib/std/build/InstallArtifactStep.zig deleted-88
......@@ -1,88 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = std.build.LibExeObjStep;
6const InstallDir = std.build.InstallDir;
7
8pub const base_id = .install_artifact;
9
10step: Step,
11builder: *Builder,
12artifact: *LibExeObjStep,
13dest_dir: InstallDir,
14pdb_dir: ?InstallDir,
15h_dir: ?InstallDir,
16
17const Self = @This();
18
19pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self {
20 if (artifact.install_step) |s| return s;
21
22 const self = builder.allocator.create(Self) catch unreachable;
23 self.* = Self{
24 .builder = builder,
25 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
26 .artifact = artifact,
27 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
28 .obj => @panic("Cannot install a .obj build artifact."),
29 .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
30 .exe, .test_exe => InstallDir{ .bin = {} },
31 .lib => InstallDir{ .lib = {} },
32 },
33 .pdb_dir = if (artifact.producesPdbFile()) blk: {
34 if (artifact.kind == .exe or artifact.kind == .test_exe) {
35 break :blk InstallDir{ .bin = {} };
36 } else {
37 break :blk InstallDir{ .lib = {} };
38 }
39 } else null,
40 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
41 };
42 self.step.dependOn(&artifact.step);
43 artifact.install_step = self;
44
45 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
46 if (self.artifact.isDynamicLibrary()) {
47 if (artifact.major_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
49 }
50 if (artifact.name_only_filename) |name| {
51 builder.pushInstalledFile(.lib, name);
52 }
53 if (self.artifact.target.isWindows()) {
54 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
55 }
56 }
57 if (self.pdb_dir) |pdb_dir| {
58 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
59 }
60 if (self.h_dir) |h_dir| {
61 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
62 }
63 return self;
64}
65
66fn make(step: *Step) !void {
67 const self = @fieldParentPtr(Self, "step", step);
68 const builder = self.builder;
69
70 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
71 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
72 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
73 try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
74 }
75 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
76 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
77 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
78 }
79 if (self.pdb_dir) |pdb_dir| {
80 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
81 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
82 }
83 if (self.h_dir) |h_dir| {
84 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
85 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
86 }
87 self.artifact.installed_path = full_dest_path;
88}
lib/std/build/InstallDirStep.zig deleted-95
......@@ -1,95 +0,0 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const InstallDir = std.build.InstallDir;
8const InstallDirStep = @This();
9const log = std.log;
10
11step: Step,
12builder: *Builder,
13options: Options,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16override_source_builder: ?*Builder = null,
17
18pub const base_id = .install_dir;
19
20pub const Options = struct {
21 source_dir: []const u8,
22 install_dir: InstallDir,
23 install_subdir: []const u8,
24 /// File paths which end in any of these suffixes will be excluded
25 /// from being installed.
26 exclude_extensions: []const []const u8 = &.{},
27 /// File paths which end in any of these suffixes will result in
28 /// empty files being installed. This is mainly intended for large
29 /// test.zig files in order to prevent needless installation bloat.
30 /// However if the files were not present at all, then
31 /// `@import("test.zig")` would be a compile error.
32 blank_extensions: []const []const u8 = &.{},
33
34 fn dupe(self: Options, b: *Builder) Options {
35 return .{
36 .source_dir = b.dupe(self.source_dir),
37 .install_dir = self.install_dir.dupe(b),
38 .install_subdir = b.dupe(self.install_subdir),
39 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
40 .blank_extensions = b.dupeStrings(self.blank_extensions),
41 };
42 }
43};
44
45pub fn init(
46 builder: *Builder,
47 options: Options,
48) InstallDirStep {
49 builder.pushInstalledFile(options.install_dir, options.install_subdir);
50 return InstallDirStep{
51 .builder = builder,
52 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
53 .options = options.dupe(builder),
54 };
55}
56
57fn make(step: *Step) !void {
58 const self = @fieldParentPtr(InstallDirStep, "step", step);
59 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
60 const src_builder = self.override_source_builder orelse self.builder;
61 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
62 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
63 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
64 full_src_dir, @errorName(err),
65 });
66 return error.StepFailed;
67 };
68 defer src_dir.close();
69 var it = try src_dir.walk(self.builder.allocator);
70 next_entry: while (try it.next()) |entry| {
71 for (self.options.exclude_extensions) |ext| {
72 if (mem.endsWith(u8, entry.path, ext)) {
73 continue :next_entry;
74 }
75 }
76
77 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
78 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
79
80 switch (entry.kind) {
81 .Directory => try fs.cwd().makePath(dest_path),
82 .File => {
83 for (self.options.blank_extensions) |ext| {
84 if (mem.endsWith(u8, entry.path, ext)) {
85 try self.builder.truncateFile(dest_path);
86 continue :next_entry;
87 }
88 }
89
90 try self.builder.updateFile(full_path, dest_path);
91 },
92 else => continue,
93 }
94 }
95}
lib/std/build/InstallFileStep.zig deleted-42
......@@ -1,42 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const FileSource = std.build.FileSource;
6const InstallDir = std.build.InstallDir;
7const InstallFileStep = @This();
8
9pub const base_id = .install_file;
10
11step: Step,
12builder: *Builder,
13source: FileSource,
14dir: InstallDir,
15dest_rel_path: []const u8,
16/// This is used by the build system when a file being installed comes from one
17/// package but is being installed by another.
18override_source_builder: ?*Builder = null,
19
20pub fn init(
21 builder: *Builder,
22 source: FileSource,
23 dir: InstallDir,
24 dest_rel_path: []const u8,
25) InstallFileStep {
26 builder.pushInstalledFile(dir, dest_rel_path);
27 return InstallFileStep{
28 .builder = builder,
29 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
30 .source = source.dupe(builder),
31 .dir = dir.dupe(builder),
32 .dest_rel_path = builder.dupePath(dest_rel_path),
33 };
34}
35
36fn make(step: *Step) !void {
37 const self = @fieldParentPtr(InstallFileStep, "step", step);
38 const src_builder = self.override_source_builder orelse self.builder;
39 const full_src_path = self.source.getPath(src_builder);
40 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
41 try self.builder.updateFile(full_src_path, full_dest_path);
42}
lib/std/build/InstallRawStep.zig deleted-106
......@@ -1,106 +0,0 @@
1//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
2//! subcommand rather than containing an implementation directly.
3
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const Builder = std.build.Builder;
11const File = std.fs.File;
12const InstallDir = std.build.InstallDir;
13const LibExeObjStep = std.build.LibExeObjStep;
14const Step = std.build.Step;
15const elf = std.elf;
16const fs = std.fs;
17const io = std.io;
18const sort = std.sort;
19
20pub const base_id = .install_raw;
21
22pub const RawFormat = enum {
23 bin,
24 hex,
25};
26
27step: Step,
28builder: *Builder,
29artifact: *LibExeObjStep,
30dest_dir: InstallDir,
31dest_filename: []const u8,
32options: CreateOptions,
33output_file: std.build.GeneratedFile,
34
35pub const CreateOptions = struct {
36 format: ?RawFormat = null,
37 dest_dir: ?InstallDir = null,
38 only_section: ?[]const u8 = null,
39 pad_to: ?u64 = null,
40};
41
42pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep {
43 const self = builder.allocator.create(InstallRawStep) catch unreachable;
44 self.* = InstallRawStep{
45 .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
46 .builder = builder,
47 .artifact = artifact,
48 .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
49 .obj => unreachable,
50 .@"test" => unreachable,
51 .exe, .test_exe => .bin,
52 .lib => unreachable,
53 },
54 .dest_filename = dest_filename,
55 .options = options,
56 .output_file = std.build.GeneratedFile{ .step = &self.step },
57 };
58 self.step.dependOn(&artifact.step);
59
60 builder.pushInstalledFile(self.dest_dir, dest_filename);
61 return self;
62}
63
64pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource {
65 return std.build.FileSource{ .generated = &self.output_file };
66}
67
68fn make(step: *Step) !void {
69 const self = @fieldParentPtr(InstallRawStep, "step", step);
70 const b = self.builder;
71
72 if (self.artifact.target.getObjectFormat() != .elf) {
73 std.debug.print("InstallRawStep only works with ELF format.\n", .{});
74 return error.InvalidObjectFormat;
75 }
76
77 const full_src_path = self.artifact.getOutputSource().getPath(b);
78 const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
79 self.output_file.path = full_dest_path;
80
81 fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable;
82
83 var argv_list = std.ArrayList([]const u8).init(b.allocator);
84 try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
85
86 if (self.options.only_section) |only_section| {
87 try argv_list.appendSlice(&.{ "-j", only_section });
88 }
89 if (self.options.pad_to) |pad_to| {
90 try argv_list.appendSlice(&.{
91 "--pad-to",
92 b.fmt("{d}", .{pad_to}),
93 });
94 }
95 if (self.options.format) |format| switch (format) {
96 .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
97 .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
98 };
99
100 try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
101 _ = try self.builder.execFromStep(argv_list.items, &self.step);
102}
103
104test {
105 std.testing.refAllDecls(InstallRawStep);
106}
lib/std/build/LibExeObjStep.zig deleted-2111
......@@ -1,2111 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const build = @import("../build.zig");
13const Step = build.Step;
14const Builder = build.Builder;
15const CrossTarget = std.zig.CrossTarget;
16const NativeTargetInfo = std.zig.system.NativeTargetInfo;
17const FileSource = std.build.FileSource;
18const PkgConfigPkg = Builder.PkgConfigPkg;
19const PkgConfigError = Builder.PkgConfigError;
20const ExecError = Builder.ExecError;
21const Pkg = std.build.Pkg;
22const VcpkgRoot = std.build.VcpkgRoot;
23const InstallDir = std.build.InstallDir;
24const InstallArtifactStep = std.build.InstallArtifactStep;
25const GeneratedFile = std.build.GeneratedFile;
26const InstallRawStep = std.build.InstallRawStep;
27const EmulatableRunStep = std.build.EmulatableRunStep;
28const CheckObjectStep = std.build.CheckObjectStep;
29const RunStep = std.build.RunStep;
30const OptionsStep = std.build.OptionsStep;
31const ConfigHeaderStep = std.build.ConfigHeaderStep;
32const LibExeObjStep = @This();
33
34pub const base_id = .lib_exe_obj;
35
36step: Step,
37builder: *Builder,
38name: []const u8,
39target: CrossTarget = CrossTarget{},
40target_info: NativeTargetInfo,
41linker_script: ?FileSource = null,
42version_script: ?[]const u8 = null,
43out_filename: []const u8,
44linkage: ?Linkage = null,
45version: ?std.builtin.Version,
46build_mode: std.builtin.Mode,
47kind: Kind,
48major_only_filename: ?[]const u8,
49name_only_filename: ?[]const u8,
50strip: ?bool,
51unwind_tables: ?bool,
52// keep in sync with src/link.zig:CompressDebugSections
53compress_debug_sections: enum { none, zlib } = .none,
54lib_paths: ArrayList([]const u8),
55rpaths: ArrayList([]const u8),
56framework_dirs: ArrayList([]const u8),
57frameworks: StringHashMap(FrameworkLinkInfo),
58verbose_link: bool,
59verbose_cc: bool,
60emit_analysis: EmitOption = .default,
61emit_asm: EmitOption = .default,
62emit_bin: EmitOption = .default,
63emit_docs: EmitOption = .default,
64emit_implib: EmitOption = .default,
65emit_llvm_bc: EmitOption = .default,
66emit_llvm_ir: EmitOption = .default,
67// Lots of things depend on emit_h having a consistent path,
68// so it is not an EmitOption for now.
69emit_h: bool = false,
70bundle_compiler_rt: ?bool = null,
71single_threaded: ?bool = null,
72stack_protector: ?bool = null,
73disable_stack_probing: bool,
74disable_sanitize_c: bool,
75sanitize_thread: bool,
76rdynamic: bool,
77import_memory: bool = false,
78/// For WebAssembly targets, this will allow for undefined symbols to
79/// be imported from the host environment.
80import_symbols: bool = false,
81import_table: bool = false,
82export_table: bool = false,
83initial_memory: ?u64 = null,
84max_memory: ?u64 = null,
85shared_memory: bool = false,
86global_base: ?u64 = null,
87c_std: Builder.CStd,
88override_lib_dir: ?[]const u8,
89main_pkg_path: ?[]const u8,
90exec_cmd_args: ?[]const ?[]const u8,
91name_prefix: []const u8,
92filter: ?[]const u8,
93test_evented_io: bool = false,
94test_runner: ?[]const u8,
95code_model: std.builtin.CodeModel = .default,
96wasi_exec_model: ?std.builtin.WasiExecModel = null,
97/// Symbols to be exported when compiling to wasm
98export_symbol_names: []const []const u8 = &.{},
99
100root_src: ?FileSource,
101out_h_filename: []const u8,
102out_lib_filename: []const u8,
103out_pdb_filename: []const u8,
104packages: ArrayList(Pkg),
105
106object_src: []const u8,
107
108link_objects: ArrayList(LinkObject),
109include_dirs: ArrayList(IncludeDir),
110c_macros: ArrayList([]const u8),
111installed_headers: ArrayList(*std.build.Step),
112output_dir: ?[]const u8,
113is_linking_libc: bool = false,
114is_linking_libcpp: bool = false,
115vcpkg_bin_path: ?[]const u8 = null,
116
117/// This may be set in order to override the default install directory
118override_dest_dir: ?InstallDir,
119installed_path: ?[]const u8,
120install_step: ?*InstallArtifactStep,
121
122/// Base address for an executable image.
123image_base: ?u64 = null,
124
125libc_file: ?FileSource = null,
126
127valgrind_support: ?bool = null,
128each_lib_rpath: ?bool = null,
129/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
130/// which can be used to coordinate a stripped binary with its debug symbols.
131/// As an example, the bloaty project refuses to work unless its inputs have
132/// build ids, in order to prevent accidental mismatches.
133/// The default is to not include this section because it slows down linking.
134build_id: ?bool = null,
135
136/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
137/// file.
138link_eh_frame_hdr: bool = false,
139link_emit_relocs: bool = false,
140
141/// Place every function in its own section so that unused ones may be
142/// safely garbage-collected during the linking phase.
143link_function_sections: bool = false,
144
145/// Remove functions and data that are unreachable by the entry point or
146/// exported symbols.
147link_gc_sections: ?bool = null,
148
149linker_allow_shlib_undefined: ?bool = null,
150
151/// Permit read-only relocations in read-only segments. Disallowed by default.
152link_z_notext: bool = false,
153
154/// Force all relocations to be read-only after processing.
155link_z_relro: bool = true,
156
157/// Allow relocations to be lazily processed after load.
158link_z_lazy: bool = false,
159
160/// Common page size
161link_z_common_page_size: ?u64 = null,
162
163/// Maximum page size
164link_z_max_page_size: ?u64 = null,
165
166/// (Darwin) Install name for the dylib
167install_name: ?[]const u8 = null,
168
169/// (Darwin) Path to entitlements file
170entitlements: ?[]const u8 = null,
171
172/// (Darwin) Size of the pagezero segment.
173pagezero_size: ?u64 = null,
174
175/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
176/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
177/// option.
178/// By default, if no option is specified, the linker assumes `paths_first` as the default
179/// search strategy.
180search_strategy: ?enum { paths_first, dylibs_first } = null,
181
182/// (Darwin) Set size of the padding between the end of load commands
183/// and start of `__TEXT,__text` section.
184headerpad_size: ?u32 = null,
185
186/// (Darwin) Automatically Set size of the padding between the end of load commands
187/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
188headerpad_max_install_names: bool = false,
189
190/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
191dead_strip_dylibs: bool = false,
192
193/// Position Independent Code
194force_pic: ?bool = null,
195
196/// Position Independent Executable
197pie: ?bool = null,
198
199red_zone: ?bool = null,
200
201omit_frame_pointer: ?bool = null,
202dll_export_fns: ?bool = null,
203
204subsystem: ?std.Target.SubSystem = null,
205
206entry_symbol_name: ?[]const u8 = null,
207
208/// Overrides the default stack size
209stack_size: ?u64 = null,
210
211want_lto: ?bool = null,
212use_llvm: ?bool = null,
213use_lld: ?bool = null,
214
215output_path_source: GeneratedFile,
216output_lib_path_source: GeneratedFile,
217output_h_path_source: GeneratedFile,
218output_pdb_path_source: GeneratedFile,
219
220pub const CSourceFiles = struct {
221 files: []const []const u8,
222 flags: []const []const u8,
223};
224
225pub const CSourceFile = struct {
226 source: FileSource,
227 args: []const []const u8,
228
229 pub fn dupe(self: CSourceFile, b: *Builder) CSourceFile {
230 return .{
231 .source = self.source.dupe(b),
232 .args = b.dupeStrings(self.args),
233 };
234 }
235};
236
237pub const LinkObject = union(enum) {
238 static_path: FileSource,
239 other_step: *LibExeObjStep,
240 system_lib: SystemLib,
241 assembly_file: FileSource,
242 c_source_file: *CSourceFile,
243 c_source_files: *CSourceFiles,
244};
245
246pub const SystemLib = struct {
247 name: []const u8,
248 needed: bool,
249 weak: bool,
250 use_pkg_config: enum {
251 /// Don't use pkg-config, just pass -lfoo where foo is name.
252 no,
253 /// Try to get information on how to link the library from pkg-config.
254 /// If that fails, fall back to passing -lfoo where foo is name.
255 yes,
256 /// Try to get information on how to link the library from pkg-config.
257 /// If that fails, error out.
258 force,
259 },
260};
261
262const FrameworkLinkInfo = struct {
263 needed: bool = false,
264 weak: bool = false,
265};
266
267pub const IncludeDir = union(enum) {
268 raw_path: []const u8,
269 raw_path_system: []const u8,
270 other_step: *LibExeObjStep,
271 config_header_step: *ConfigHeaderStep,
272};
273
274pub const Kind = enum {
275 exe,
276 lib,
277 obj,
278 @"test",
279 test_exe,
280};
281
282pub const SharedLibKind = union(enum) {
283 versioned: std.builtin.Version,
284 unversioned: void,
285};
286
287pub const Linkage = enum { dynamic, static };
288
289pub const EmitOption = union(enum) {
290 default: void,
291 no_emit: void,
292 emit: void,
293 emit_to: []const u8,
294
295 fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 {
296 return switch (self) {
297 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
298 .default => null,
299 .emit => b.fmt("-f{s}", .{arg_name}),
300 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
301 };
302 }
303};
304
305pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep {
306 return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) {
307 .versioned => |ver| ver,
308 .unversioned => null,
309 });
310}
311
312pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
313 return initExtraArgs(builder, name, root_src, .lib, .static, null);
314}
315
316pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
317 return initExtraArgs(builder, name, root_src, .obj, null, null);
318}
319
320pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
321 return initExtraArgs(builder, name, root_src, .exe, null, null);
322}
323
324pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
325 return initExtraArgs(builder, name, root_src, .@"test", null, null);
326}
327
328pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
329 return initExtraArgs(builder, name, root_src, .test_exe, null, null);
330}
331
332fn initExtraArgs(
333 builder: *Builder,
334 name_raw: []const u8,
335 root_src_raw: ?FileSource,
336 kind: Kind,
337 linkage: ?Linkage,
338 ver: ?std.builtin.Version,
339) *LibExeObjStep {
340 const name = builder.dupe(name_raw);
341 const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null;
342 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
343 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
344 }
345
346 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
347 self.* = LibExeObjStep{
348 .strip = null,
349 .unwind_tables = null,
350 .builder = builder,
351 .verbose_link = false,
352 .verbose_cc = false,
353 .build_mode = std.builtin.Mode.Debug,
354 .linkage = linkage,
355 .kind = kind,
356 .root_src = root_src,
357 .name = name,
358 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
359 .step = Step.init(base_id, name, builder.allocator, make),
360 .version = ver,
361 .out_filename = undefined,
362 .out_h_filename = builder.fmt("{s}.h", .{name}),
363 .out_lib_filename = undefined,
364 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
365 .major_only_filename = null,
366 .name_only_filename = null,
367 .packages = ArrayList(Pkg).init(builder.allocator),
368 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
369 .link_objects = ArrayList(LinkObject).init(builder.allocator),
370 .c_macros = ArrayList([]const u8).init(builder.allocator),
371 .lib_paths = ArrayList([]const u8).init(builder.allocator),
372 .rpaths = ArrayList([]const u8).init(builder.allocator),
373 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
374 .installed_headers = ArrayList(*std.build.Step).init(builder.allocator),
375 .object_src = undefined,
376 .c_std = Builder.CStd.C99,
377 .override_lib_dir = null,
378 .main_pkg_path = null,
379 .exec_cmd_args = null,
380 .name_prefix = "",
381 .filter = null,
382 .test_runner = null,
383 .disable_stack_probing = false,
384 .disable_sanitize_c = false,
385 .sanitize_thread = false,
386 .rdynamic = false,
387 .output_dir = null,
388 .override_dest_dir = null,
389 .installed_path = null,
390 .install_step = null,
391
392 .output_path_source = GeneratedFile{ .step = &self.step },
393 .output_lib_path_source = GeneratedFile{ .step = &self.step },
394 .output_h_path_source = GeneratedFile{ .step = &self.step },
395 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
396
397 .target_info = undefined, // populated in computeOutFileNames
398 };
399 self.computeOutFileNames();
400 if (root_src) |rs| rs.addStepDependencies(&self.step);
401 return self;
402}
403
404fn computeOutFileNames(self: *LibExeObjStep) void {
405 self.target_info = NativeTargetInfo.detect(self.target) catch
406 unreachable;
407
408 const target = self.target_info.target;
409
410 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
411 .root_name = self.name,
412 .target = target,
413 .output_mode = switch (self.kind) {
414 .lib => .Lib,
415 .obj => .Obj,
416 .exe, .@"test", .test_exe => .Exe,
417 },
418 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
419 .dynamic => .Dynamic,
420 .static => .Static,
421 }) else null,
422 .version = self.version,
423 }) catch unreachable;
424
425 if (self.kind == .lib) {
426 if (self.linkage != null and self.linkage.? == .static) {
427 self.out_lib_filename = self.out_filename;
428 } else if (self.version) |version| {
429 if (target.isDarwin()) {
430 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
431 self.name,
432 version.major,
433 });
434 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
435 self.out_lib_filename = self.out_filename;
436 } else if (target.os.tag == .windows) {
437 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
438 } else {
439 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
440 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
441 self.out_lib_filename = self.out_filename;
442 }
443 } else {
444 if (target.isDarwin()) {
445 self.out_lib_filename = self.out_filename;
446 } else if (target.os.tag == .windows) {
447 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
448 } else {
449 self.out_lib_filename = self.out_filename;
450 }
451 }
452 if (self.output_dir != null) {
453 self.output_lib_path_source.path = self.builder.pathJoin(
454 &.{ self.output_dir.?, self.out_lib_filename },
455 );
456 }
457 }
458}
459
460pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
461 self.target = target;
462 self.computeOutFileNames();
463}
464
465pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
466 self.output_dir = self.builder.dupePath(dir);
467}
468
469pub fn install(self: *LibExeObjStep) void {
470 self.builder.installArtifact(self);
471}
472
473pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
474 return self.builder.installRaw(self, dest_filename, options);
475}
476
477pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
478 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
479 a.builder.getInstallStep().dependOn(&install_file.step);
480 a.installed_headers.append(&install_file.step) catch unreachable;
481}
482
483pub fn installHeadersDirectory(
484 a: *LibExeObjStep,
485 src_dir_path: []const u8,
486 dest_rel_path: []const u8,
487) void {
488 return installHeadersDirectoryOptions(a, .{
489 .source_dir = src_dir_path,
490 .install_dir = .header,
491 .install_subdir = dest_rel_path,
492 });
493}
494
495pub fn installHeadersDirectoryOptions(
496 a: *LibExeObjStep,
497 options: std.build.InstallDirStep.Options,
498) void {
499 const install_dir = a.builder.addInstallDirectory(options);
500 a.builder.getInstallStep().dependOn(&install_dir.step);
501 a.installed_headers.append(&install_dir.step) catch unreachable;
502}
503
504pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
505 assert(l.kind == .lib);
506 const install_step = a.builder.getInstallStep();
507 // Copy each element from installed_headers, modifying the builder
508 // to be the new parent's builder.
509 for (l.installed_headers.items) |step| {
510 const step_copy = switch (step.id) {
511 inline .install_file, .install_dir => |id| blk: {
512 const T = id.Type();
513 const ptr = a.builder.allocator.create(T) catch unreachable;
514 ptr.* = step.cast(T).?.*;
515 ptr.override_source_builder = ptr.builder;
516 ptr.builder = a.builder;
517 break :blk &ptr.step;
518 },
519 else => unreachable,
520 };
521 a.installed_headers.append(step_copy) catch unreachable;
522 install_step.dependOn(step_copy);
523 }
524 a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
525}
526
527/// Creates a `RunStep` with an executable built with `addExecutable`.
528/// Add command line arguments with `addArg`.
529pub fn run(exe: *LibExeObjStep) *RunStep {
530 assert(exe.kind == .exe or exe.kind == .test_exe);
531
532 // It doesn't have to be native. We catch that if you actually try to run it.
533 // Consider that this is declarative; the run step may not be run unless a user
534 // option is supplied.
535 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
536 run_step.addArtifactArg(exe);
537
538 if (exe.kind == .test_exe) {
539 run_step.addArg(exe.builder.zig_exe);
540 }
541
542 if (exe.vcpkg_bin_path) |path| {
543 run_step.addPathDir(path);
544 }
545
546 return run_step;
547}
548
549/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
550/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
551/// When a binary cannot be ran through emulation or the option is disabled, a warning
552/// will be printed and the binary will *NOT* be ran.
553pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
554 assert(exe.kind == .exe or exe.kind == .test_exe);
555
556 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
557 if (exe.vcpkg_bin_path) |path| {
558 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
559 }
560 return run_step;
561}
562
563pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
564 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
565}
566
567pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
568 self.linker_script = source.dupe(self.builder);
569 source.addStepDependencies(&self.step);
570}
571
572pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
573 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
574}
575
576pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
577 self.frameworks.put(self.builder.dupe(framework_name), .{
578 .needed = true,
579 }) catch unreachable;
580}
581
582pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
583 self.frameworks.put(self.builder.dupe(framework_name), .{
584 .weak = true,
585 }) catch unreachable;
586}
587
588/// Returns whether the library, executable, or object depends on a particular system library.
589pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
590 if (isLibCLibrary(name)) {
591 return self.is_linking_libc;
592 }
593 if (isLibCppLibrary(name)) {
594 return self.is_linking_libcpp;
595 }
596 for (self.link_objects.items) |link_object| {
597 switch (link_object) {
598 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
599 else => continue,
600 }
601 }
602 return false;
603}
604
605pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
606 assert(lib.kind == .lib);
607 self.linkLibraryOrObject(lib);
608}
609
610pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
611 return self.kind == .lib and self.linkage == Linkage.dynamic;
612}
613
614pub fn isStaticLibrary(self: *LibExeObjStep) bool {
615 return self.kind == .lib and self.linkage != Linkage.dynamic;
616}
617
618pub fn producesPdbFile(self: *LibExeObjStep) bool {
619 if (!self.target.isWindows() and !self.target.isUefi()) return false;
620 if (self.target.getObjectFormat() == .c) return false;
621 if (self.strip == true) return false;
622 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
623}
624
625pub fn linkLibC(self: *LibExeObjStep) void {
626 self.is_linking_libc = true;
627}
628
629pub fn linkLibCpp(self: *LibExeObjStep) void {
630 self.is_linking_libcpp = true;
631}
632
633/// If the value is omitted, it is set to 1.
634/// `name` and `value` need not live longer than the function call.
635pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
636 const macro = std.build.constructCMacro(self.builder.allocator, name, value);
637 self.c_macros.append(macro) catch unreachable;
638}
639
640/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
641pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
642 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
643}
644
645/// This one has no integration with anything, it just puts -lname on the command line.
646/// Prefer to use `linkSystemLibrary` instead.
647pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
648 self.link_objects.append(.{
649 .system_lib = .{
650 .name = self.builder.dupe(name),
651 .needed = false,
652 .weak = false,
653 .use_pkg_config = .no,
654 },
655 }) catch unreachable;
656}
657
658/// This one has no integration with anything, it just puts -needed-lname on the command line.
659/// Prefer to use `linkSystemLibraryNeeded` instead.
660pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
661 self.link_objects.append(.{
662 .system_lib = .{
663 .name = self.builder.dupe(name),
664 .needed = true,
665 .weak = false,
666 .use_pkg_config = .no,
667 },
668 }) catch unreachable;
669}
670
671/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
672/// command line. Prefer to use `linkSystemLibraryWeak` instead.
673pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
674 self.link_objects.append(.{
675 .system_lib = .{
676 .name = self.builder.dupe(name),
677 .needed = false,
678 .weak = true,
679 .use_pkg_config = .no,
680 },
681 }) catch unreachable;
682}
683
684/// This links against a system library, exclusively using pkg-config to find the library.
685/// Prefer to use `linkSystemLibrary` instead.
686pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
687 self.link_objects.append(.{
688 .system_lib = .{
689 .name = self.builder.dupe(lib_name),
690 .needed = false,
691 .weak = false,
692 .use_pkg_config = .force,
693 },
694 }) catch unreachable;
695}
696
697/// This links against a system library, exclusively using pkg-config to find the library.
698/// Prefer to use `linkSystemLibraryNeeded` instead.
699pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
700 self.link_objects.append(.{
701 .system_lib = .{
702 .name = self.builder.dupe(lib_name),
703 .needed = true,
704 .weak = false,
705 .use_pkg_config = .force,
706 },
707 }) catch unreachable;
708}
709
710/// Run pkg-config for the given library name and parse the output, returning the arguments
711/// that should be passed to zig to link the given library.
712pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
713 const pkg_name = match: {
714 // First we have to map the library name to pkg config name. Unfortunately,
715 // there are several examples where this is not straightforward:
716 // -lSDL2 -> pkg-config sdl2
717 // -lgdk-3 -> pkg-config gdk-3.0
718 // -latk-1.0 -> pkg-config atk
719 const pkgs = try getPkgConfigList(self.builder);
720
721 // Exact match means instant winner.
722 for (pkgs) |pkg| {
723 if (mem.eql(u8, pkg.name, lib_name)) {
724 break :match pkg.name;
725 }
726 }
727
728 // Next we'll try ignoring case.
729 for (pkgs) |pkg| {
730 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
731 break :match pkg.name;
732 }
733 }
734
735 // Now try appending ".0".
736 for (pkgs) |pkg| {
737 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
738 if (pos != 0) continue;
739 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
740 break :match pkg.name;
741 }
742 }
743 }
744
745 // Trimming "-1.0".
746 if (mem.endsWith(u8, lib_name, "-1.0")) {
747 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
748 for (pkgs) |pkg| {
749 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
750 break :match pkg.name;
751 }
752 }
753 }
754
755 return error.PackageNotFound;
756 };
757
758 var code: u8 = undefined;
759 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
760 "pkg-config",
761 pkg_name,
762 "--cflags",
763 "--libs",
764 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
765 error.ProcessTerminated => return error.PkgConfigCrashed,
766 error.ExecNotSupported => return error.PkgConfigFailed,
767 error.ExitCodeFailure => return error.PkgConfigFailed,
768 error.FileNotFound => return error.PkgConfigNotInstalled,
769 error.ChildExecFailed => return error.PkgConfigFailed,
770 else => return err,
771 };
772
773 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
774 defer zig_args.deinit();
775
776 var it = mem.tokenize(u8, stdout, " \r\n\t");
777 while (it.next()) |tok| {
778 if (mem.eql(u8, tok, "-I")) {
779 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
780 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
781 } else if (mem.startsWith(u8, tok, "-I")) {
782 try zig_args.append(tok);
783 } else if (mem.eql(u8, tok, "-L")) {
784 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
785 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
786 } else if (mem.startsWith(u8, tok, "-L")) {
787 try zig_args.append(tok);
788 } else if (mem.eql(u8, tok, "-l")) {
789 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
790 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
791 } else if (mem.startsWith(u8, tok, "-l")) {
792 try zig_args.append(tok);
793 } else if (mem.eql(u8, tok, "-D")) {
794 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
795 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
796 } else if (mem.startsWith(u8, tok, "-D")) {
797 try zig_args.append(tok);
798 } else if (self.builder.verbose) {
799 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
800 }
801 }
802
803 return zig_args.toOwnedSlice();
804}
805
806pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
807 self.linkSystemLibraryInner(name, .{});
808}
809
810pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
811 self.linkSystemLibraryInner(name, .{ .needed = true });
812}
813
814pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
815 self.linkSystemLibraryInner(name, .{ .weak = true });
816}
817
818fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
819 needed: bool = false,
820 weak: bool = false,
821}) void {
822 if (isLibCLibrary(name)) {
823 self.linkLibC();
824 return;
825 }
826 if (isLibCppLibrary(name)) {
827 self.linkLibCpp();
828 return;
829 }
830
831 self.link_objects.append(.{
832 .system_lib = .{
833 .name = self.builder.dupe(name),
834 .needed = opts.needed,
835 .weak = opts.weak,
836 .use_pkg_config = .yes,
837 },
838 }) catch unreachable;
839}
840
841pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
842 assert(self.kind == .@"test" or self.kind == .test_exe);
843 self.name_prefix = self.builder.dupe(text);
844}
845
846pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
847 assert(self.kind == .@"test" or self.kind == .test_exe);
848 self.filter = if (text) |t| self.builder.dupe(t) else null;
849}
850
851pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
852 assert(self.kind == .@"test" or self.kind == .test_exe);
853 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
854}
855
856/// Handy when you have many C/C++ source files and want them all to have the same flags.
857pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
858 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
859
860 const files_copy = self.builder.dupeStrings(files);
861 const flags_copy = self.builder.dupeStrings(flags);
862
863 c_source_files.* = .{
864 .files = files_copy,
865 .flags = flags_copy,
866 };
867 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
868}
869
870pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
871 self.addCSourceFileSource(.{
872 .args = flags,
873 .source = .{ .path = file },
874 });
875}
876
877pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
878 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
879 c_source_file.* = source.dupe(self.builder);
880 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
881 source.source.addStepDependencies(&self.step);
882}
883
884pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
885 self.verbose_link = value;
886}
887
888pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
889 self.verbose_cc = value;
890}
891
892pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void {
893 self.build_mode = mode;
894}
895
896pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
897 self.override_lib_dir = self.builder.dupePath(dir_path);
898}
899
900pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
901 self.main_pkg_path = self.builder.dupePath(dir_path);
902}
903
904pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
905 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
906}
907
908/// Returns the generated executable, library or object file.
909/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
910pub fn getOutputSource(self: *LibExeObjStep) FileSource {
911 return FileSource{ .generated = &self.output_path_source };
912}
913
914/// Returns the generated import library. This function can only be called for libraries.
915pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
916 assert(self.kind == .lib);
917 return FileSource{ .generated = &self.output_lib_path_source };
918}
919
920/// Returns the generated header file.
921/// This function can only be called for libraries or object files which have `emit_h` set.
922pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
923 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
924 assert(self.emit_h);
925 return FileSource{ .generated = &self.output_h_path_source };
926}
927
928/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
929pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
930 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
931 assert(self.target.isWindows() or self.target.isUefi());
932 return FileSource{ .generated = &self.output_pdb_path_source };
933}
934
935pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
936 self.link_objects.append(.{
937 .assembly_file = .{ .path = self.builder.dupe(path) },
938 }) catch unreachable;
939}
940
941pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
942 const source_duped = source.dupe(self.builder);
943 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
944 source_duped.addStepDependencies(&self.step);
945}
946
947pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
948 self.addObjectFileSource(.{ .path = source_file });
949}
950
951pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
952 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
953 source.addStepDependencies(&self.step);
954}
955
956pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
957 assert(obj.kind == .obj);
958 self.linkLibraryOrObject(obj);
959}
960
961pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
962pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
963pub const addLibPath = @compileError("deprecated, use addLibraryPath");
964pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
965
966pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
967 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
968}
969
970pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
971 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
972}
973
974pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
975 self.step.dependOn(&config_header.step);
976 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
977}
978
979pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
980 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
981}
982
983pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
984 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
985}
986
987pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
988 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
989}
990
991pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
992 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
993 self.addRecursiveBuildDeps(package);
994}
995
996pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
997 self.addPackage(options.getPackage(package_name));
998}
999
1000fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
1001 package.source.addStepDependencies(&self.step);
1002 if (package.dependencies) |deps| {
1003 for (deps) |dep| {
1004 self.addRecursiveBuildDeps(dep);
1005 }
1006 }
1007}
1008
1009pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
1010 self.addPackage(Pkg{
1011 .name = self.builder.dupe(name),
1012 .source = .{ .path = self.builder.dupe(pkg_index_path) },
1013 });
1014}
1015
1016/// If Vcpkg was found on the system, it will be added to include and lib
1017/// paths for the specified target.
1018pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
1019 // Ideally in the Unattempted case we would call the function recursively
1020 // after findVcpkgRoot and have only one switch statement, but the compiler
1021 // cannot resolve the error set.
1022 switch (self.builder.vcpkg_root) {
1023 .unattempted => {
1024 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1025 VcpkgRoot{ .found = root }
1026 else
1027 .not_found;
1028 },
1029 .not_found => return error.VcpkgNotFound,
1030 .found => {},
1031 }
1032
1033 switch (self.builder.vcpkg_root) {
1034 .unattempted => unreachable,
1035 .not_found => return error.VcpkgNotFound,
1036 .found => |root| {
1037 const allocator = self.builder.allocator;
1038 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1039 defer self.builder.allocator.free(triplet);
1040
1041 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1042 errdefer allocator.free(include_path);
1043 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1044
1045 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1046 try self.lib_paths.append(lib_path);
1047
1048 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1049 },
1050 }
1051}
1052
1053pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
1054 assert(self.kind == .@"test");
1055 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
1056 for (args) |arg, i| {
1057 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1058 }
1059 self.exec_cmd_args = duped_args;
1060}
1061
1062fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
1063 self.step.dependOn(&other.step);
1064 self.link_objects.append(.{ .other_step = other }) catch unreachable;
1065 self.include_dirs.append(.{ .other_step = other }) catch unreachable;
1066}
1067
1068fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1069 const builder = self.builder;
1070
1071 try zig_args.append("--pkg-begin");
1072 try zig_args.append(pkg.name);
1073 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1074
1075 if (pkg.dependencies) |dependencies| {
1076 for (dependencies) |sub_pkg| {
1077 try self.makePackageCmd(sub_pkg, zig_args);
1078 }
1079 }
1080
1081 try zig_args.append("--pkg-end");
1082}
1083
1084fn make(step: *Step) !void {
1085 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1086 const builder = self.builder;
1087
1088 if (self.root_src == null and self.link_objects.items.len == 0) {
1089 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1090 return error.NeedAnObject;
1091 }
1092
1093 var zig_args = ArrayList([]const u8).init(builder.allocator);
1094 defer zig_args.deinit();
1095
1096 zig_args.append(builder.zig_exe) catch unreachable;
1097
1098 const cmd = switch (self.kind) {
1099 .lib => "build-lib",
1100 .exe => "build-exe",
1101 .obj => "build-obj",
1102 .@"test" => "test",
1103 .test_exe => "test",
1104 };
1105 zig_args.append(cmd) catch unreachable;
1106
1107 if (builder.color != .auto) {
1108 try zig_args.append("--color");
1109 try zig_args.append(@tagName(builder.color));
1110 }
1111
1112 if (builder.reference_trace) |some| {
1113 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1114 }
1115
1116 try addFlag(&zig_args, "LLVM", self.use_llvm);
1117 try addFlag(&zig_args, "LLD", self.use_lld);
1118
1119 if (self.target.ofmt) |ofmt| {
1120 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1121 }
1122
1123 if (self.entry_symbol_name) |entry| {
1124 try zig_args.append("--entry");
1125 try zig_args.append(entry);
1126 }
1127
1128 if (self.stack_size) |stack_size| {
1129 try zig_args.append("--stack");
1130 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1131 }
1132
1133 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1134
1135 // We will add link objects from transitive dependencies, but we want to keep
1136 // all link objects in the same order provided.
1137 // This array is used to keep self.link_objects immutable.
1138 var transitive_deps: TransitiveDeps = .{
1139 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1140 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1141 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1142 .is_linking_libcpp = self.is_linking_libcpp,
1143 .is_linking_libc = self.is_linking_libc,
1144 .frameworks = &self.frameworks,
1145 };
1146
1147 try transitive_deps.seen_steps.put(&self.step, {});
1148 try transitive_deps.add(self.link_objects.items);
1149
1150 var prev_has_extra_flags = false;
1151
1152 for (transitive_deps.link_objects.items) |link_object| {
1153 switch (link_object) {
1154 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1155
1156 .other_step => |other| switch (other.kind) {
1157 .exe => @panic("Cannot link with an executable build artifact"),
1158 .test_exe => @panic("Cannot link with an executable build artifact"),
1159 .@"test" => @panic("Cannot link with a test"),
1160 .obj => {
1161 try zig_args.append(other.getOutputSource().getPath(builder));
1162 },
1163 .lib => l: {
1164 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1165 // Avoid putting a static library inside a static library.
1166 break :l;
1167 }
1168
1169 const full_path_lib = other.getOutputLibSource().getPath(builder);
1170 try zig_args.append(full_path_lib);
1171
1172 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1173 if (fs.path.dirname(full_path_lib)) |dirname| {
1174 try zig_args.append("-rpath");
1175 try zig_args.append(dirname);
1176 }
1177 }
1178 },
1179 },
1180
1181 .system_lib => |system_lib| {
1182 const prefix: []const u8 = prefix: {
1183 if (system_lib.needed) break :prefix "-needed-l";
1184 if (system_lib.weak) {
1185 if (self.target.isDarwin()) break :prefix "-weak-l";
1186 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1187 }
1188 break :prefix "-l";
1189 };
1190 switch (system_lib.use_pkg_config) {
1191 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1192 .yes, .force => {
1193 if (self.runPkgConfig(system_lib.name)) |args| {
1194 try zig_args.appendSlice(args);
1195 } else |err| switch (err) {
1196 error.PkgConfigInvalidOutput,
1197 error.PkgConfigCrashed,
1198 error.PkgConfigFailed,
1199 error.PkgConfigNotInstalled,
1200 error.PackageNotFound,
1201 => switch (system_lib.use_pkg_config) {
1202 .yes => {
1203 // pkg-config failed, so fall back to linking the library
1204 // by name directly.
1205 try zig_args.append(builder.fmt("{s}{s}", .{
1206 prefix,
1207 system_lib.name,
1208 }));
1209 },
1210 .force => {
1211 panic("pkg-config failed for library {s}", .{system_lib.name});
1212 },
1213 .no => unreachable,
1214 },
1215
1216 else => |e| return e,
1217 }
1218 },
1219 }
1220 },
1221
1222 .assembly_file => |asm_file| {
1223 if (prev_has_extra_flags) {
1224 try zig_args.append("-extra-cflags");
1225 try zig_args.append("--");
1226 prev_has_extra_flags = false;
1227 }
1228 try zig_args.append(asm_file.getPath(builder));
1229 },
1230
1231 .c_source_file => |c_source_file| {
1232 if (c_source_file.args.len == 0) {
1233 if (prev_has_extra_flags) {
1234 try zig_args.append("-cflags");
1235 try zig_args.append("--");
1236 prev_has_extra_flags = false;
1237 }
1238 } else {
1239 try zig_args.append("-cflags");
1240 for (c_source_file.args) |arg| {
1241 try zig_args.append(arg);
1242 }
1243 try zig_args.append("--");
1244 }
1245 try zig_args.append(c_source_file.source.getPath(builder));
1246 },
1247
1248 .c_source_files => |c_source_files| {
1249 if (c_source_files.flags.len == 0) {
1250 if (prev_has_extra_flags) {
1251 try zig_args.append("-cflags");
1252 try zig_args.append("--");
1253 prev_has_extra_flags = false;
1254 }
1255 } else {
1256 try zig_args.append("-cflags");
1257 for (c_source_files.flags) |flag| {
1258 try zig_args.append(flag);
1259 }
1260 try zig_args.append("--");
1261 }
1262 for (c_source_files.files) |file| {
1263 try zig_args.append(builder.pathFromRoot(file));
1264 }
1265 },
1266 }
1267 }
1268
1269 if (transitive_deps.is_linking_libcpp) {
1270 try zig_args.append("-lc++");
1271 }
1272
1273 if (transitive_deps.is_linking_libc) {
1274 try zig_args.append("-lc");
1275 }
1276
1277 if (self.image_base) |image_base| {
1278 try zig_args.append("--image-base");
1279 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1280 }
1281
1282 if (self.filter) |filter| {
1283 try zig_args.append("--test-filter");
1284 try zig_args.append(filter);
1285 }
1286
1287 if (self.test_evented_io) {
1288 try zig_args.append("--test-evented-io");
1289 }
1290
1291 if (self.name_prefix.len != 0) {
1292 try zig_args.append("--test-name-prefix");
1293 try zig_args.append(self.name_prefix);
1294 }
1295
1296 if (self.test_runner) |test_runner| {
1297 try zig_args.append("--test-runner");
1298 try zig_args.append(builder.pathFromRoot(test_runner));
1299 }
1300
1301 for (builder.debug_log_scopes) |log_scope| {
1302 try zig_args.append("--debug-log");
1303 try zig_args.append(log_scope);
1304 }
1305
1306 if (builder.debug_compile_errors) {
1307 try zig_args.append("--debug-compile-errors");
1308 }
1309
1310 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
1311 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
1312 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
1313 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
1314 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
1315 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
1316
1317 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1318 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1319 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1320 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1321 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1322 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1323 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1324
1325 if (self.emit_h) try zig_args.append("-femit-h");
1326
1327 try addFlag(&zig_args, "strip", self.strip);
1328 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1329
1330 switch (self.compress_debug_sections) {
1331 .none => {},
1332 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1333 }
1334
1335 if (self.link_eh_frame_hdr) {
1336 try zig_args.append("--eh-frame-hdr");
1337 }
1338 if (self.link_emit_relocs) {
1339 try zig_args.append("--emit-relocs");
1340 }
1341 if (self.link_function_sections) {
1342 try zig_args.append("-ffunction-sections");
1343 }
1344 if (self.link_gc_sections) |x| {
1345 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1346 }
1347 if (self.linker_allow_shlib_undefined) |x| {
1348 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1349 }
1350 if (self.link_z_notext) {
1351 try zig_args.append("-z");
1352 try zig_args.append("notext");
1353 }
1354 if (!self.link_z_relro) {
1355 try zig_args.append("-z");
1356 try zig_args.append("norelro");
1357 }
1358 if (self.link_z_lazy) {
1359 try zig_args.append("-z");
1360 try zig_args.append("lazy");
1361 }
1362 if (self.link_z_common_page_size) |size| {
1363 try zig_args.append("-z");
1364 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1365 }
1366 if (self.link_z_max_page_size) |size| {
1367 try zig_args.append("-z");
1368 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1369 }
1370
1371 if (self.libc_file) |libc_file| {
1372 try zig_args.append("--libc");
1373 try zig_args.append(libc_file.getPath(builder));
1374 } else if (builder.libc_file) |libc_file| {
1375 try zig_args.append("--libc");
1376 try zig_args.append(libc_file);
1377 }
1378
1379 switch (self.build_mode) {
1380 .Debug => {}, // Skip since it's the default.
1381 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
1382 }
1383
1384 try zig_args.append("--cache-dir");
1385 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1386
1387 try zig_args.append("--global-cache-dir");
1388 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1389
1390 zig_args.append("--name") catch unreachable;
1391 zig_args.append(self.name) catch unreachable;
1392
1393 if (self.linkage) |some| switch (some) {
1394 .dynamic => try zig_args.append("-dynamic"),
1395 .static => try zig_args.append("-static"),
1396 };
1397 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1398 if (self.version) |version| {
1399 zig_args.append("--version") catch unreachable;
1400 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
1401 }
1402
1403 if (self.target.isDarwin()) {
1404 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1405 self.target.libPrefix(),
1406 self.name,
1407 self.target.dynamicLibSuffix(),
1408 });
1409 try zig_args.append("-install_name");
1410 try zig_args.append(install_name);
1411 }
1412 }
1413
1414 if (self.entitlements) |entitlements| {
1415 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1416 }
1417 if (self.pagezero_size) |pagezero_size| {
1418 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1419 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1420 }
1421 if (self.search_strategy) |strat| switch (strat) {
1422 .paths_first => try zig_args.append("-search_paths_first"),
1423 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1424 };
1425 if (self.headerpad_size) |headerpad_size| {
1426 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1427 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1428 }
1429 if (self.headerpad_max_install_names) {
1430 try zig_args.append("-headerpad_max_install_names");
1431 }
1432 if (self.dead_strip_dylibs) {
1433 try zig_args.append("-dead_strip_dylibs");
1434 }
1435
1436 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1437 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1438 if (self.disable_stack_probing) {
1439 try zig_args.append("-fno-stack-check");
1440 }
1441 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1442 if (self.red_zone) |red_zone| {
1443 if (red_zone) {
1444 try zig_args.append("-mred-zone");
1445 } else {
1446 try zig_args.append("-mno-red-zone");
1447 }
1448 }
1449 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1450 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1451
1452 if (self.disable_sanitize_c) {
1453 try zig_args.append("-fno-sanitize-c");
1454 }
1455 if (self.sanitize_thread) {
1456 try zig_args.append("-fsanitize-thread");
1457 }
1458 if (self.rdynamic) {
1459 try zig_args.append("-rdynamic");
1460 }
1461 if (self.import_memory) {
1462 try zig_args.append("--import-memory");
1463 }
1464 if (self.import_symbols) {
1465 try zig_args.append("--import-symbols");
1466 }
1467 if (self.import_table) {
1468 try zig_args.append("--import-table");
1469 }
1470 if (self.export_table) {
1471 try zig_args.append("--export-table");
1472 }
1473 if (self.initial_memory) |initial_memory| {
1474 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1475 }
1476 if (self.max_memory) |max_memory| {
1477 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1478 }
1479 if (self.shared_memory) {
1480 try zig_args.append("--shared-memory");
1481 }
1482 if (self.global_base) |global_base| {
1483 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1484 }
1485
1486 if (self.code_model != .default) {
1487 try zig_args.append("-mcmodel");
1488 try zig_args.append(@tagName(self.code_model));
1489 }
1490 if (self.wasi_exec_model) |model| {
1491 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1492 }
1493 for (self.export_symbol_names) |symbol_name| {
1494 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1495 }
1496
1497 if (!self.target.isNative()) {
1498 try zig_args.append("-target");
1499 try zig_args.append(try self.target.zigTriple(builder.allocator));
1500
1501 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1502 const cross = self.target.toTarget();
1503 const all_features = cross.cpu.arch.allFeaturesList();
1504 var populated_cpu_features = cross.cpu.model.features;
1505 populated_cpu_features.populateDependencies(all_features);
1506
1507 if (populated_cpu_features.eql(cross.cpu.features)) {
1508 // The CPU name alone is sufficient.
1509 try zig_args.append("-mcpu");
1510 try zig_args.append(cross.cpu.model.name);
1511 } else {
1512 var mcpu_buffer = ArrayList(u8).init(builder.allocator);
1513
1514 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
1515
1516 for (all_features) |feature, i_usize| {
1517 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1518 const in_cpu_set = populated_cpu_features.isEnabled(i);
1519 const in_actual_set = cross.cpu.features.isEnabled(i);
1520 if (in_cpu_set and !in_actual_set) {
1521 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1522 } else if (!in_cpu_set and in_actual_set) {
1523 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1524 }
1525 }
1526
1527 try zig_args.append(try mcpu_buffer.toOwnedSlice());
1528 }
1529
1530 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1531 try zig_args.append("--dynamic-linker");
1532 try zig_args.append(dynamic_linker);
1533 }
1534 }
1535
1536 if (self.linker_script) |linker_script| {
1537 try zig_args.append("--script");
1538 try zig_args.append(linker_script.getPath(builder));
1539 }
1540
1541 if (self.version_script) |version_script| {
1542 try zig_args.append("--version-script");
1543 try zig_args.append(builder.pathFromRoot(version_script));
1544 }
1545
1546 if (self.kind == .@"test") {
1547 if (self.exec_cmd_args) |exec_cmd_args| {
1548 for (exec_cmd_args) |cmd_arg| {
1549 if (cmd_arg) |arg| {
1550 try zig_args.append("--test-cmd");
1551 try zig_args.append(arg);
1552 } else {
1553 try zig_args.append("--test-cmd-bin");
1554 }
1555 }
1556 } else {
1557 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1558
1559 switch (builder.host.getExternalExecutor(self.target_info, .{
1560 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1561 .link_libc = transitive_deps.is_linking_libc,
1562 })) {
1563 .native => {},
1564 .bad_dl, .bad_os_or_cpu => {
1565 try zig_args.append("--test-no-exec");
1566 },
1567 .rosetta => if (builder.enable_rosetta) {
1568 try zig_args.append("--test-cmd-bin");
1569 } else {
1570 try zig_args.append("--test-no-exec");
1571 },
1572 .qemu => |bin_name| ok: {
1573 if (builder.enable_qemu) qemu: {
1574 const glibc_dir_arg = if (need_cross_glibc)
1575 builder.glibc_runtimes_dir orelse break :qemu
1576 else
1577 null;
1578 try zig_args.append("--test-cmd");
1579 try zig_args.append(bin_name);
1580 if (glibc_dir_arg) |dir| {
1581 // TODO look into making this a call to `linuxTriple`. This
1582 // needs the directory to be called "i686" rather than
1583 // "x86" which is why we do it manually here.
1584 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1585 const cpu_arch = self.target.getCpuArch();
1586 const os_tag = self.target.getOsTag();
1587 const abi = self.target.getAbi();
1588 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1589 "i686"
1590 else
1591 @tagName(cpu_arch);
1592 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1593 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1594 });
1595
1596 try zig_args.append("--test-cmd");
1597 try zig_args.append("-L");
1598 try zig_args.append("--test-cmd");
1599 try zig_args.append(full_dir);
1600 }
1601 try zig_args.append("--test-cmd-bin");
1602 break :ok;
1603 }
1604 try zig_args.append("--test-no-exec");
1605 },
1606 .wine => |bin_name| if (builder.enable_wine) {
1607 try zig_args.append("--test-cmd");
1608 try zig_args.append(bin_name);
1609 try zig_args.append("--test-cmd-bin");
1610 } else {
1611 try zig_args.append("--test-no-exec");
1612 },
1613 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1614 try zig_args.append("--test-cmd");
1615 try zig_args.append(bin_name);
1616 try zig_args.append("--test-cmd");
1617 try zig_args.append("--dir=.");
1618 try zig_args.append("--test-cmd-bin");
1619 } else {
1620 try zig_args.append("--test-no-exec");
1621 },
1622 .darling => |bin_name| if (builder.enable_darling) {
1623 try zig_args.append("--test-cmd");
1624 try zig_args.append(bin_name);
1625 try zig_args.append("--test-cmd-bin");
1626 } else {
1627 try zig_args.append("--test-no-exec");
1628 },
1629 }
1630 }
1631 } else if (self.kind == .test_exe) {
1632 try zig_args.append("--test-no-exec");
1633 }
1634
1635 for (self.packages.items) |pkg| {
1636 try self.makePackageCmd(pkg, &zig_args);
1637 }
1638
1639 for (self.include_dirs.items) |include_dir| {
1640 switch (include_dir) {
1641 .raw_path => |include_path| {
1642 try zig_args.append("-I");
1643 try zig_args.append(builder.pathFromRoot(include_path));
1644 },
1645 .raw_path_system => |include_path| {
1646 if (builder.sysroot != null) {
1647 try zig_args.append("-iwithsysroot");
1648 } else {
1649 try zig_args.append("-isystem");
1650 }
1651
1652 const resolved_include_path = builder.pathFromRoot(include_path);
1653
1654 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1655 // We need to check for disk designator and strip it out from dir path so
1656 // that zig/clang can concat resolved_include_path with sysroot.
1657 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1658
1659 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1660 break :blk resolved_include_path[where + disk_designator.len ..];
1661 }
1662
1663 break :blk resolved_include_path;
1664 } else resolved_include_path;
1665
1666 try zig_args.append(common_include_path);
1667 },
1668 .other_step => |other| {
1669 if (other.emit_h) {
1670 const h_path = other.getOutputHSource().getPath(builder);
1671 try zig_args.append("-isystem");
1672 try zig_args.append(fs.path.dirname(h_path).?);
1673 }
1674 if (other.installed_headers.items.len > 0) {
1675 for (other.installed_headers.items) |install_step| {
1676 try install_step.make();
1677 }
1678 try zig_args.append("-I");
1679 try zig_args.append(builder.pathJoin(&.{
1680 other.builder.install_prefix, "include",
1681 }));
1682 }
1683 },
1684 .config_header_step => |config_header| {
1685 try zig_args.append("-I");
1686 try zig_args.append(config_header.output_dir);
1687 },
1688 }
1689 }
1690
1691 for (self.lib_paths.items) |lib_path| {
1692 try zig_args.append("-L");
1693 try zig_args.append(lib_path);
1694 }
1695
1696 for (self.rpaths.items) |rpath| {
1697 try zig_args.append("-rpath");
1698 try zig_args.append(rpath);
1699 }
1700
1701 for (self.c_macros.items) |c_macro| {
1702 try zig_args.append("-D");
1703 try zig_args.append(c_macro);
1704 }
1705
1706 if (self.target.isDarwin()) {
1707 for (self.framework_dirs.items) |dir| {
1708 if (builder.sysroot != null) {
1709 try zig_args.append("-iframeworkwithsysroot");
1710 } else {
1711 try zig_args.append("-iframework");
1712 }
1713 try zig_args.append(dir);
1714 try zig_args.append("-F");
1715 try zig_args.append(dir);
1716 }
1717
1718 var it = self.frameworks.iterator();
1719 while (it.next()) |entry| {
1720 const name = entry.key_ptr.*;
1721 const info = entry.value_ptr.*;
1722 if (info.needed) {
1723 zig_args.append("-needed_framework") catch unreachable;
1724 } else if (info.weak) {
1725 zig_args.append("-weak_framework") catch unreachable;
1726 } else {
1727 zig_args.append("-framework") catch unreachable;
1728 }
1729 zig_args.append(name) catch unreachable;
1730 }
1731 } else {
1732 if (self.framework_dirs.items.len > 0) {
1733 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1734 }
1735
1736 if (self.frameworks.count() > 0) {
1737 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1738 }
1739 }
1740
1741 if (builder.sysroot) |sysroot| {
1742 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1743 }
1744
1745 for (builder.search_prefixes.items) |search_prefix| {
1746 try zig_args.append("-L");
1747 try zig_args.append(builder.pathJoin(&.{
1748 search_prefix, "lib",
1749 }));
1750 try zig_args.append("-I");
1751 try zig_args.append(builder.pathJoin(&.{
1752 search_prefix, "include",
1753 }));
1754 }
1755
1756 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1757 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1758 try addFlag(&zig_args, "build-id", self.build_id);
1759
1760 if (self.override_lib_dir) |dir| {
1761 try zig_args.append("--zig-lib-dir");
1762 try zig_args.append(builder.pathFromRoot(dir));
1763 } else if (builder.override_lib_dir) |dir| {
1764 try zig_args.append("--zig-lib-dir");
1765 try zig_args.append(builder.pathFromRoot(dir));
1766 }
1767
1768 if (self.main_pkg_path) |dir| {
1769 try zig_args.append("--main-pkg-path");
1770 try zig_args.append(builder.pathFromRoot(dir));
1771 }
1772
1773 try addFlag(&zig_args, "PIC", self.force_pic);
1774 try addFlag(&zig_args, "PIE", self.pie);
1775 try addFlag(&zig_args, "lto", self.want_lto);
1776
1777 if (self.subsystem) |subsystem| {
1778 try zig_args.append("--subsystem");
1779 try zig_args.append(switch (subsystem) {
1780 .Console => "console",
1781 .Windows => "windows",
1782 .Posix => "posix",
1783 .Native => "native",
1784 .EfiApplication => "efi_application",
1785 .EfiBootServiceDriver => "efi_boot_service_driver",
1786 .EfiRom => "efi_rom",
1787 .EfiRuntimeDriver => "efi_runtime_driver",
1788 });
1789 }
1790
1791 try zig_args.append("--enable-cache");
1792
1793 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1794 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1795 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1796 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1797 var args_length: usize = 0;
1798 for (zig_args.items) |arg| {
1799 args_length += arg.len + 1; // +1 to account for null terminator
1800 }
1801 if (args_length >= 30 * 1024) {
1802 const args_dir = try fs.path.join(
1803 builder.allocator,
1804 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1805 );
1806 try std.fs.cwd().makePath(args_dir);
1807
1808 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1809 defer args_arena.deinit();
1810
1811 const args_to_escape = zig_args.items[2..];
1812 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1813
1814 arg_blk: for (args_to_escape) |arg| {
1815 for (arg) |c, arg_idx| {
1816 if (c == '\\' or c == '"') {
1817 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1818 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1819 const writer = escaped.writer();
1820 writer.writeAll(arg[0..arg_idx]) catch unreachable;
1821 for (arg[arg_idx..]) |to_escape| {
1822 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1823 try writer.writeByte(to_escape);
1824 }
1825 escaped_args.appendAssumeCapacity(escaped.items);
1826 continue :arg_blk;
1827 }
1828 }
1829 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1830 }
1831
1832 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1833 // other zig build commands running in parallel.
1834 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1835 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1836
1837 var args_hash: [Sha256.digest_length]u8 = undefined;
1838 Sha256.hash(args, &args_hash, .{});
1839 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1840 _ = try std.fmt.bufPrint(
1841 &args_hex_hash,
1842 "{s}",
1843 .{std.fmt.fmtSliceHexLower(&args_hash)},
1844 );
1845
1846 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1847 try std.fs.cwd().writeFile(args_file, args);
1848
1849 zig_args.shrinkRetainingCapacity(2);
1850 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1851 }
1852
1853 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1854 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1855
1856 if (self.output_dir) |output_dir| {
1857 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1858 defer src_dir.close();
1859
1860 // Create the output directory if it doesn't exist.
1861 try std.fs.cwd().makePath(output_dir);
1862
1863 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1864 defer dest_dir.close();
1865
1866 var it = src_dir.iterate();
1867 while (try it.next()) |entry| {
1868 // The compiler can put these files into the same directory, but we don't
1869 // want to copy them over.
1870 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1871 mem.eql(u8, entry.name, "libs.txt") or
1872 mem.eql(u8, entry.name, "builtin.zig") or
1873 mem.eql(u8, entry.name, "zld.id") or
1874 mem.eql(u8, entry.name, "lld.id")) continue;
1875
1876 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1877 }
1878 } else {
1879 self.output_dir = build_output_dir;
1880 }
1881
1882 // This will ensure all output filenames will now have the output_dir available!
1883 self.computeOutFileNames();
1884
1885 // Update generated files
1886 if (self.output_dir != null) {
1887 self.output_path_source.path = builder.pathJoin(
1888 &.{ self.output_dir.?, self.out_filename },
1889 );
1890
1891 if (self.emit_h) {
1892 self.output_h_path_source.path = builder.pathJoin(
1893 &.{ self.output_dir.?, self.out_h_filename },
1894 );
1895 }
1896
1897 if (self.target.isWindows() or self.target.isUefi()) {
1898 self.output_pdb_path_source.path = builder.pathJoin(
1899 &.{ self.output_dir.?, self.out_pdb_filename },
1900 );
1901 }
1902 }
1903
1904 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1905 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1906 }
1907}
1908
1909fn isLibCLibrary(name: []const u8) bool {
1910 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1911 for (libc_libraries) |libc_lib_name| {
1912 if (mem.eql(u8, name, libc_lib_name))
1913 return true;
1914 }
1915 return false;
1916}
1917
1918fn isLibCppLibrary(name: []const u8) bool {
1919 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1920 for (libcpp_libraries) |libcpp_lib_name| {
1921 if (mem.eql(u8, name, libcpp_lib_name))
1922 return true;
1923 }
1924 return false;
1925}
1926
1927/// Returned slice must be freed by the caller.
1928fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1929 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1930 defer allocator.free(appdata_path);
1931
1932 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1933 defer allocator.free(path_file);
1934
1935 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1936 defer file.close();
1937
1938 const size = @intCast(usize, try file.getEndPos());
1939 const vcpkg_path = try allocator.alloc(u8, size);
1940 const size_read = try file.read(vcpkg_path);
1941 std.debug.assert(size == size_read);
1942
1943 return vcpkg_path;
1944}
1945
1946pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
1947 const out_dir = fs.path.dirname(output_path) orelse ".";
1948 const out_basename = fs.path.basename(output_path);
1949 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1950 const major_only_path = fs.path.join(
1951 allocator,
1952 &[_][]const u8{ out_dir, filename_major_only },
1953 ) catch unreachable;
1954 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1955 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1956 return err;
1957 };
1958 // sym link for libfoo.so to libfoo.so.1
1959 const name_only_path = fs.path.join(
1960 allocator,
1961 &[_][]const u8{ out_dir, filename_name_only },
1962 ) catch unreachable;
1963 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1964 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1965 return err;
1966 };
1967}
1968
1969fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1970 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1971 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1972 errdefer list.deinit();
1973 var line_it = mem.tokenize(u8, stdout, "\r\n");
1974 while (line_it.next()) |line| {
1975 if (mem.trim(u8, line, " \t").len == 0) continue;
1976 var tok_it = mem.tokenize(u8, line, " \t");
1977 try list.append(PkgConfigPkg{
1978 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1979 .desc = tok_it.rest(),
1980 });
1981 }
1982 return list.toOwnedSlice();
1983}
1984
1985fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
1986 if (self.pkg_config_pkg_list) |res| {
1987 return res;
1988 }
1989 var code: u8 = undefined;
1990 if (execPkgConfigList(self, &code)) |list| {
1991 self.pkg_config_pkg_list = list;
1992 return list;
1993 } else |err| {
1994 const result = switch (err) {
1995 error.ProcessTerminated => error.PkgConfigCrashed,
1996 error.ExecNotSupported => error.PkgConfigFailed,
1997 error.ExitCodeFailure => error.PkgConfigFailed,
1998 error.FileNotFound => error.PkgConfigNotInstalled,
1999 error.InvalidName => error.PkgConfigNotInstalled,
2000 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2001 error.ChildExecFailed => error.PkgConfigFailed,
2002 else => return err,
2003 };
2004 self.pkg_config_pkg_list = result;
2005 return result;
2006 }
2007}
2008
2009test "addPackage" {
2010 if (builtin.os.tag == .wasi) return error.SkipZigTest;
2011
2012 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
2013 defer arena.deinit();
2014
2015 var builder = try Builder.create(
2016 arena.allocator(),
2017 "test",
2018 "test",
2019 "test",
2020 "test",
2021 );
2022 defer builder.destroy();
2023
2024 const pkg_dep = Pkg{
2025 .name = "pkg_dep",
2026 .source = .{ .path = "/not/a/pkg_dep.zig" },
2027 };
2028 const pkg_top = Pkg{
2029 .name = "pkg_dep",
2030 .source = .{ .path = "/not/a/pkg_top.zig" },
2031 .dependencies = &[_]Pkg{pkg_dep},
2032 };
2033
2034 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
2035 exe.addPackage(pkg_top);
2036
2037 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
2038
2039 const dupe = exe.packages.items[0];
2040 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
2041}
2042
2043fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
2044 const cond = opt orelse return;
2045 try args.ensureUnusedCapacity(1);
2046 if (cond) {
2047 args.appendAssumeCapacity("-f" ++ name);
2048 } else {
2049 args.appendAssumeCapacity("-fno-" ++ name);
2050 }
2051}
2052
2053const TransitiveDeps = struct {
2054 link_objects: ArrayList(LinkObject),
2055 seen_system_libs: StringHashMap(void),
2056 seen_steps: std.AutoHashMap(*const Step, void),
2057 is_linking_libcpp: bool,
2058 is_linking_libc: bool,
2059 frameworks: *StringHashMap(FrameworkLinkInfo),
2060
2061 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
2062 try td.link_objects.ensureUnusedCapacity(link_objects.len);
2063
2064 for (link_objects) |link_object| {
2065 try td.link_objects.append(link_object);
2066 switch (link_object) {
2067 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2068 else => {},
2069 }
2070 }
2071 }
2072
2073 fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
2074 // Inherit dependency on libc and libc++
2075 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2076 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2077
2078 // Inherit dependencies on darwin frameworks
2079 if (!dyn) {
2080 var it = other.frameworks.iterator();
2081 while (it.next()) |framework| {
2082 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2083 }
2084 }
2085
2086 // Inherit dependencies on system libraries and static libraries.
2087 for (other.link_objects.items) |other_link_object| {
2088 switch (other_link_object) {
2089 .system_lib => |system_lib| {
2090 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2091 continue;
2092
2093 if (dyn)
2094 continue;
2095
2096 try td.link_objects.append(other_link_object);
2097 },
2098 .other_step => |inner_other| {
2099 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2100 continue;
2101
2102 if (!dyn)
2103 try td.link_objects.append(other_link_object);
2104
2105 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2106 },
2107 else => continue,
2108 }
2109 }
2110 }
2111};
lib/std/build/LogStep.zig deleted-25
......@@ -1,25 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const build = @import("../build.zig");
4const Step = build.Step;
5const Builder = build.Builder;
6const LogStep = @This();
7
8pub const base_id = .log;
9
10step: Step,
11builder: *Builder,
12data: []const u8,
13
14pub fn init(builder: *Builder, data: []const u8) LogStep {
15 return LogStep{
16 .builder = builder,
17 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
18 .data = builder.dupe(data),
19 };
20}
21
22fn make(step: *Step) anyerror!void {
23 const self = @fieldParentPtr(LogStep, "step", step);
24 log.info("{s}", .{self.data});
25}
lib/std/build/OptionsStep.zig deleted-365
......@@ -1,365 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const fs = std.fs;
5const Step = build.Step;
6const Builder = build.Builder;
7const GeneratedFile = build.GeneratedFile;
8const LibExeObjStep = build.LibExeObjStep;
9const FileSource = build.FileSource;
10
11const OptionsStep = @This();
12
13pub const base_id = .options;
14
15step: Step,
16generated_file: GeneratedFile,
17builder: *Builder,
18
19contents: std.ArrayList(u8),
20artifact_args: std.ArrayList(OptionArtifactArg),
21file_source_args: std.ArrayList(OptionFileSourceArg),
22
23pub fn create(builder: *Builder) *OptionsStep {
24 const self = builder.allocator.create(OptionsStep) catch unreachable;
25 self.* = .{
26 .builder = builder,
27 .step = Step.init(.options, "options", builder.allocator, make),
28 .generated_file = undefined,
29 .contents = std.ArrayList(u8).init(builder.allocator),
30 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
31 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
32 };
33 self.generated_file = .{ .step = &self.step };
34
35 return self;
36}
37
38pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
39 const out = self.contents.writer();
40 switch (T) {
41 []const []const u8 => {
42 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
43 for (value) |slice| {
44 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
45 }
46 out.writeAll("};\n") catch unreachable;
47 return;
48 },
49 [:0]const u8 => {
50 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
51 return;
52 },
53 []const u8 => {
54 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
55 return;
56 },
57 ?[:0]const u8 => {
58 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
59 if (value) |payload| {
60 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
61 } else {
62 out.writeAll("null;\n") catch unreachable;
63 }
64 return;
65 },
66 ?[]const u8 => {
67 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
68 if (value) |payload| {
69 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
70 } else {
71 out.writeAll("null;\n") catch unreachable;
72 }
73 return;
74 },
75 std.builtin.Version => {
76 out.print(
77 \\pub const {}: @import("std").builtin.Version = .{{
78 \\ .major = {d},
79 \\ .minor = {d},
80 \\ .patch = {d},
81 \\}};
82 \\
83 , .{
84 std.zig.fmtId(name),
85
86 value.major,
87 value.minor,
88 value.patch,
89 }) catch unreachable;
90 return;
91 },
92 std.SemanticVersion => {
93 out.print(
94 \\pub const {}: @import("std").SemanticVersion = .{{
95 \\ .major = {d},
96 \\ .minor = {d},
97 \\ .patch = {d},
98 \\
99 , .{
100 std.zig.fmtId(name),
101
102 value.major,
103 value.minor,
104 value.patch,
105 }) catch unreachable;
106 if (value.pre) |some| {
107 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
108 }
109 if (value.build) |some| {
110 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
111 }
112 out.writeAll("};\n") catch unreachable;
113 return;
114 },
115 else => {},
116 }
117 switch (@typeInfo(T)) {
118 .Enum => |enum_info| {
119 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
120 inline for (enum_info.fields) |field| {
121 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
122 }
123 out.writeAll("};\n") catch unreachable;
124 out.print("pub const {}: {s} = {s}.{s};\n", .{
125 std.zig.fmtId(name),
126 std.zig.fmtId(@typeName(T)),
127 std.zig.fmtId(@typeName(T)),
128 std.zig.fmtId(@tagName(value)),
129 }) catch unreachable;
130 return;
131 },
132 else => {},
133 }
134 out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable;
135 printLiteral(out, value, 0) catch unreachable;
136 out.writeAll(";\n") catch unreachable;
137}
138
139// TODO: non-recursive?
140fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
141 const T = @TypeOf(val);
142 switch (@typeInfo(T)) {
143 .Array => {
144 try out.print("{s} {{\n", .{@typeName(T)});
145 for (val) |item| {
146 try out.writeByteNTimes(' ', indent + 4);
147 try printLiteral(out, item, indent + 4);
148 try out.writeAll(",\n");
149 }
150 try out.writeByteNTimes(' ', indent);
151 try out.writeAll("}");
152 },
153 .Pointer => |p| {
154 if (p.size != .Slice) {
155 @compileError("Non-slice pointers are not yet supported in build options");
156 }
157 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
158 for (val) |item| {
159 try out.writeByteNTimes(' ', indent + 4);
160 try printLiteral(out, item, indent + 4);
161 try out.writeAll(",\n");
162 }
163 try out.writeByteNTimes(' ', indent);
164 try out.writeAll("}");
165 },
166 .Optional => {
167 if (val) |inner| {
168 return printLiteral(out, inner, indent);
169 } else {
170 return out.writeAll("null");
171 }
172 },
173 .Void,
174 .Bool,
175 .Int,
176 .ComptimeInt,
177 .Float,
178 .Null,
179 => try out.print("{any}", .{val}),
180 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
181 }
182}
183
184/// The value is the path in the cache dir.
185/// Adds a dependency automatically.
186pub fn addOptionFileSource(
187 self: *OptionsStep,
188 name: []const u8,
189 source: FileSource,
190) void {
191 self.file_source_args.append(.{
192 .name = name,
193 .source = source.dupe(self.builder),
194 }) catch unreachable;
195 source.addStepDependencies(&self.step);
196}
197
198/// The value is the path in the cache dir.
199/// Adds a dependency automatically.
200pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
201 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
202 self.step.dependOn(&artifact.step);
203}
204
205pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg {
206 return .{ .name = package_name, .source = self.getSource() };
207}
208
209pub fn getSource(self: *OptionsStep) FileSource {
210 return .{ .generated = &self.generated_file };
211}
212
213fn make(step: *Step) !void {
214 const self = @fieldParentPtr(OptionsStep, "step", step);
215
216 for (self.artifact_args.items) |item| {
217 self.addOption(
218 []const u8,
219 item.name,
220 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
221 );
222 }
223
224 for (self.file_source_args.items) |item| {
225 self.addOption(
226 []const u8,
227 item.name,
228 item.source.getPath(self.builder),
229 );
230 }
231
232 const options_directory = self.builder.pathFromRoot(
233 try fs.path.join(
234 self.builder.allocator,
235 &[_][]const u8{ self.builder.cache_root, "options" },
236 ),
237 );
238
239 try fs.cwd().makePath(options_directory);
240
241 const options_file = try fs.path.join(
242 self.builder.allocator,
243 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
244 );
245
246 try fs.cwd().writeFile(options_file, self.contents.items);
247
248 self.generated_file.path = options_file;
249}
250
251fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
252 // This implementation is copied from `WriteFileStep.make`
253
254 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
255
256 // Random bytes to make OptionsStep unique. Refresh this with
257 // new random bytes when OptionsStep implementation is modified
258 // in a non-backwards-compatible way.
259 hash.update("yL0Ya4KkmcCjBlP8");
260 hash.update(self.contents.items);
261
262 var digest: [48]u8 = undefined;
263 hash.final(&digest);
264 var hash_basename: [64]u8 = undefined;
265 _ = fs.base64_encoder.encode(&hash_basename, &digest);
266 return hash_basename;
267}
268
269const OptionArtifactArg = struct {
270 name: []const u8,
271 artifact: *LibExeObjStep,
272};
273
274const OptionFileSourceArg = struct {
275 name: []const u8,
276 source: FileSource,
277};
278
279test "OptionsStep" {
280 if (builtin.os.tag == .wasi) return error.SkipZigTest;
281
282 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
283 defer arena.deinit();
284 var builder = try Builder.create(
285 arena.allocator(),
286 "test",
287 "test",
288 "test",
289 "test",
290 );
291 defer builder.destroy();
292
293 const options = builder.addOptions();
294
295 // TODO this regressed at some point
296 //const KeywordEnum = enum {
297 // @"0.8.1",
298 //};
299
300 const nested_array = [2][2]u16{
301 [2]u16{ 300, 200 },
302 [2]u16{ 300, 200 },
303 };
304 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
305
306 options.addOption(usize, "option1", 1);
307 options.addOption(?usize, "option2", null);
308 options.addOption(?usize, "option3", 3);
309 options.addOption(comptime_int, "option4", 4);
310 options.addOption([]const u8, "string", "zigisthebest");
311 options.addOption(?[]const u8, "optional_string", null);
312 options.addOption([2][2]u16, "nested_array", nested_array);
313 options.addOption([]const []const u16, "nested_slice", nested_slice);
314 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
315 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
316 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
317
318 try std.testing.expectEqualStrings(
319 \\pub const option1: usize = 1;
320 \\pub const option2: ?usize = null;
321 \\pub const option3: ?usize = 3;
322 \\pub const option4: comptime_int = 4;
323 \\pub const string: []const u8 = "zigisthebest";
324 \\pub const optional_string: ?[]const u8 = null;
325 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
326 \\ [2]u16 {
327 \\ 300,
328 \\ 200,
329 \\ },
330 \\ [2]u16 {
331 \\ 300,
332 \\ 200,
333 \\ },
334 \\};
335 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
336 \\ &[_]u16 {
337 \\ 300,
338 \\ 200,
339 \\ },
340 \\ &[_]u16 {
341 \\ 300,
342 \\ 200,
343 \\ },
344 \\};
345 //\\pub const KeywordEnum = enum {
346 //\\ @"0.8.1",
347 //\\};
348 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
349 \\pub const version: @import("std").builtin.Version = .{
350 \\ .major = 0,
351 \\ .minor = 1,
352 \\ .patch = 2,
353 \\};
354 \\pub const semantic_version: @import("std").SemanticVersion = .{
355 \\ .major = 0,
356 \\ .minor = 1,
357 \\ .patch = 2,
358 \\ .pre = "foo",
359 \\ .build = "bar",
360 \\};
361 \\
362 , options.contents.items);
363
364 _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
365}
lib/std/build/RemoveDirStep.zig deleted-31
......@@ -1,31 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const RemoveDirStep = @This();
8
9pub const base_id = .remove_dir;
10
11step: Step,
12builder: *Builder,
13dir_path: []const u8,
14
15pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
16 return RemoveDirStep{
17 .builder = builder,
18 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
19 .dir_path = builder.dupePath(dir_path),
20 };
21}
22
23fn make(step: *Step) !void {
24 const self = @fieldParentPtr(RemoveDirStep, "step", step);
25
26 const full_path = self.builder.pathFromRoot(self.dir_path);
27 fs.cwd().deleteTree(full_path) catch |err| {
28 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
29 return err;
30 };
31}
lib/std/build/RunStep.zig deleted-378
......@@ -1,378 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const Step = build.Step;
5const Builder = build.Builder;
6const LibExeObjStep = build.LibExeObjStep;
7const WriteFileStep = build.WriteFileStep;
8const fs = std.fs;
9const mem = std.mem;
10const process = std.process;
11const ArrayList = std.ArrayList;
12const EnvMap = process.EnvMap;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
15
16const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
17
18const RunStep = @This();
19
20pub const base_id: Step.Id = .run;
21
22step: Step,
23builder: *Builder,
24
25/// See also addArg and addArgs to modifying this directly
26argv: ArrayList(Arg),
27
28/// Set this to modify the current working directory
29cwd: ?[]const u8,
30
31/// Override this field to modify the environment, or use setEnvironmentVariable
32env_map: ?*EnvMap,
33
34stdout_action: StdIoAction = .inherit,
35stderr_action: StdIoAction = .inherit,
36
37stdin_behavior: std.ChildProcess.StdIo = .Inherit,
38
39/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
40expected_exit_code: ?u8 = 0,
41
42/// Print the command before running it
43print: bool,
44
45pub const StdIoAction = union(enum) {
46 inherit,
47 ignore,
48 expect_exact: []const u8,
49 expect_matches: []const []const u8,
50};
51
52pub const Arg = union(enum) {
53 artifact: *LibExeObjStep,
54 file_source: build.FileSource,
55 bytes: []u8,
56};
57
58pub fn create(builder: *Builder, name: []const u8) *RunStep {
59 const self = builder.allocator.create(RunStep) catch unreachable;
60 self.* = RunStep{
61 .builder = builder,
62 .step = Step.init(base_id, name, builder.allocator, make),
63 .argv = ArrayList(Arg).init(builder.allocator),
64 .cwd = null,
65 .env_map = null,
66 .print = builder.verbose,
67 };
68 return self;
69}
70
71pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
72 self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
73 self.step.dependOn(&artifact.step);
74}
75
76pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void {
77 self.argv.append(Arg{
78 .file_source = file_source.dupe(self.builder),
79 }) catch unreachable;
80 file_source.addStepDependencies(&self.step);
81}
82
83pub fn addArg(self: *RunStep, arg: []const u8) void {
84 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
85}
86
87pub fn addArgs(self: *RunStep, args: []const []const u8) void {
88 for (args) |arg| {
89 self.addArg(arg);
90 }
91}
92
93pub fn clearEnvironment(self: *RunStep) void {
94 const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable;
95 new_env_map.* = EnvMap.init(self.builder.allocator);
96 self.env_map = new_env_map;
97}
98
99pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
100 addPathDirInternal(&self.step, self.builder, search_path);
101}
102
103/// For internal use only, users of `RunStep` should use `addPathDir` directly.
104pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u8) void {
105 const env_map = getEnvMapInternal(step, builder.allocator);
106
107 const key = "PATH";
108 var prev_path = env_map.get(key);
109
110 if (prev_path) |pp| {
111 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
112 env_map.put(key, new_path) catch unreachable;
113 } else {
114 env_map.put(key, builder.dupePath(search_path)) catch unreachable;
115 }
116}
117
118pub fn getEnvMap(self: *RunStep) *EnvMap {
119 return getEnvMapInternal(&self.step, self.builder.allocator);
120}
121
122fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
123 const maybe_env_map = switch (step.id) {
124 .run => step.cast(RunStep).?.env_map,
125 .emulatable_run => step.cast(build.EmulatableRunStep).?.env_map,
126 else => unreachable,
127 };
128 return maybe_env_map orelse {
129 const env_map = allocator.create(EnvMap) catch unreachable;
130 env_map.* = process.getEnvMap(allocator) catch unreachable;
131 switch (step.id) {
132 .run => step.cast(RunStep).?.env_map = env_map,
133 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
134 else => unreachable,
135 }
136 return env_map;
137 };
138}
139
140pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
141 const env_map = self.getEnvMap();
142 env_map.put(
143 self.builder.dupe(key),
144 self.builder.dupe(value),
145 ) catch unreachable;
146}
147
148pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
149 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
153 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
154}
155
156fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
157 return switch (action) {
158 .ignore => .Ignore,
159 .inherit => .Inherit,
160 .expect_exact, .expect_matches => .Pipe,
161 };
162}
163
164fn make(step: *Step) !void {
165 const self = @fieldParentPtr(RunStep, "step", step);
166
167 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
168 for (self.argv.items) |arg| {
169 switch (arg) {
170 .bytes => |bytes| try argv_list.append(bytes),
171 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
172 .artifact => |artifact| {
173 if (artifact.target.isWindows()) {
174 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
175 self.addPathForDynLibs(artifact);
176 }
177 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
178 try argv_list.append(executable_path);
179 },
180 }
181 }
182
183 try runCommand(
184 argv_list.items,
185 self.builder,
186 self.expected_exit_code,
187 self.stdout_action,
188 self.stderr_action,
189 self.stdin_behavior,
190 self.env_map,
191 self.cwd,
192 self.print,
193 );
194}
195
196pub fn runCommand(
197 argv: []const []const u8,
198 builder: *Builder,
199 expected_exit_code: ?u8,
200 stdout_action: StdIoAction,
201 stderr_action: StdIoAction,
202 stdin_behavior: std.ChildProcess.StdIo,
203 env_map: ?*EnvMap,
204 maybe_cwd: ?[]const u8,
205 print: bool,
206) !void {
207 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
208
209 if (!std.process.can_spawn) {
210 const cmd = try std.mem.join(builder.allocator, " ", argv);
211 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
212 builder.allocator.free(cmd);
213 return ExecError.ExecNotSupported;
214 }
215
216 var child = std.ChildProcess.init(argv, builder.allocator);
217 child.cwd = cwd;
218 child.env_map = env_map orelse builder.env_map;
219
220 child.stdin_behavior = stdin_behavior;
221 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
222 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
223
224 if (print)
225 printCmd(cwd, argv);
226
227 child.spawn() catch |err| {
228 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
229 return err;
230 };
231
232 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
233
234 var stdout: ?[]const u8 = null;
235 defer if (stdout) |s| builder.allocator.free(s);
236
237 switch (stdout_action) {
238 .expect_exact, .expect_matches => {
239 stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
240 },
241 .inherit, .ignore => {},
242 }
243
244 var stderr: ?[]const u8 = null;
245 defer if (stderr) |s| builder.allocator.free(s);
246
247 switch (stderr_action) {
248 .expect_exact, .expect_matches => {
249 stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
250 },
251 .inherit, .ignore => {},
252 }
253
254 const term = child.wait() catch |err| {
255 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
256 return err;
257 };
258
259 switch (term) {
260 .Exited => |code| blk: {
261 const expected_code = expected_exit_code orelse break :blk;
262
263 if (code != expected_code) {
264 if (builder.prominent_compile_errors) {
265 std.debug.print("Run step exited with error code {} (expected {})\n", .{
266 code,
267 expected_code,
268 });
269 } else {
270 std.debug.print("The following command exited with error code {} (expected {}):\n", .{
271 code,
272 expected_code,
273 });
274 printCmd(cwd, argv);
275 }
276
277 return error.UnexpectedExitCode;
278 }
279 },
280 else => {
281 std.debug.print("The following command terminated unexpectedly:\n", .{});
282 printCmd(cwd, argv);
283 return error.UncleanExit;
284 },
285 }
286
287 switch (stderr_action) {
288 .inherit, .ignore => {},
289 .expect_exact => |expected_bytes| {
290 if (!mem.eql(u8, expected_bytes, stderr.?)) {
291 std.debug.print(
292 \\
293 \\========= Expected this stderr: =========
294 \\{s}
295 \\========= But found: ====================
296 \\{s}
297 \\
298 , .{ expected_bytes, stderr.? });
299 printCmd(cwd, argv);
300 return error.TestFailed;
301 }
302 },
303 .expect_matches => |matches| for (matches) |match| {
304 if (mem.indexOf(u8, stderr.?, match) == null) {
305 std.debug.print(
306 \\
307 \\========= Expected to find in stderr: =========
308 \\{s}
309 \\========= But stderr does not contain it: =====
310 \\{s}
311 \\
312 , .{ match, stderr.? });
313 printCmd(cwd, argv);
314 return error.TestFailed;
315 }
316 },
317 }
318
319 switch (stdout_action) {
320 .inherit, .ignore => {},
321 .expect_exact => |expected_bytes| {
322 if (!mem.eql(u8, expected_bytes, stdout.?)) {
323 std.debug.print(
324 \\
325 \\========= Expected this stdout: =========
326 \\{s}
327 \\========= But found: ====================
328 \\{s}
329 \\
330 , .{ expected_bytes, stdout.? });
331 printCmd(cwd, argv);
332 return error.TestFailed;
333 }
334 },
335 .expect_matches => |matches| for (matches) |match| {
336 if (mem.indexOf(u8, stdout.?, match) == null) {
337 std.debug.print(
338 \\
339 \\========= Expected to find in stdout: =========
340 \\{s}
341 \\========= But stdout does not contain it: =====
342 \\{s}
343 \\
344 , .{ match, stdout.? });
345 printCmd(cwd, argv);
346 return error.TestFailed;
347 }
348 },
349 }
350}
351
352fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
353 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
354 for (argv) |arg| {
355 std.debug.print("{s} ", .{arg});
356 }
357 std.debug.print("\n", .{});
358}
359
360fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
361 addPathForDynLibsInternal(&self.step, self.builder, artifact);
362}
363
364/// This should only be used for internal usage, this is called automatically
365/// for the user.
366pub fn addPathForDynLibsInternal(step: *Step, builder: *Builder, artifact: *LibExeObjStep) void {
367 for (artifact.link_objects.items) |link_object| {
368 switch (link_object) {
369 .other_step => |other| {
370 if (other.target.isWindows() and other.isDynamicLibrary()) {
371 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
372 addPathForDynLibsInternal(step, builder, other);
373 }
374 },
375 else => {},
376 }
377 }
378}
lib/std/build/TranslateCStep.zig deleted-112
......@@ -1,112 +0,0 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = build.LibExeObjStep;
6const CheckFileStep = build.CheckFileStep;
7const fs = std.fs;
8const mem = std.mem;
9const CrossTarget = std.zig.CrossTarget;
10
11const TranslateCStep = @This();
12
13pub const base_id = .translate_c;
14
15step: Step,
16builder: *Builder,
17source: build.FileSource,
18include_dirs: std.ArrayList([]const u8),
19c_macros: std.ArrayList([]const u8),
20output_dir: ?[]const u8,
21out_basename: []const u8,
22target: CrossTarget = CrossTarget{},
23output_file: build.GeneratedFile,
24
25pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
26 const self = builder.allocator.create(TranslateCStep) catch unreachable;
27 self.* = TranslateCStep{
28 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
29 .builder = builder,
30 .source = source,
31 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
32 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
33 .output_dir = null,
34 .out_basename = undefined,
35 .output_file = build.GeneratedFile{ .step = &self.step },
36 };
37 source.addStepDependencies(&self.step);
38 return self;
39}
40
41pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
42 self.target = target;
43}
44
45/// Creates a step to build an executable from the translated source.
46pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
47 return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file });
48}
49
50pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
51 self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
52}
53
54pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
55 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
56}
57
58/// If the value is omitted, it is set to 1.
59/// `name` and `value` need not live longer than the function call.
60pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
61 const macro = build.constructCMacro(self.builder.allocator, name, value);
62 self.c_macros.append(macro) catch unreachable;
63}
64
65/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
66pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
67 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(TranslateCStep, "step", step);
72
73 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
74 try argv_list.append(self.builder.zig_exe);
75 try argv_list.append("translate-c");
76 try argv_list.append("-lc");
77
78 try argv_list.append("--enable-cache");
79
80 if (!self.target.isNative()) {
81 try argv_list.append("-target");
82 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
83 }
84
85 for (self.include_dirs.items) |include_dir| {
86 try argv_list.append("-I");
87 try argv_list.append(include_dir);
88 }
89
90 for (self.c_macros.items) |c_macro| {
91 try argv_list.append("-D");
92 try argv_list.append(c_macro);
93 }
94
95 try argv_list.append(self.source.getPath(self.builder));
96
97 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
98 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
99
100 self.out_basename = fs.path.basename(output_path);
101 if (self.output_dir) |output_dir| {
102 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
103 try self.builder.updateFile(output_path, full_dest);
104 } else {
105 self.output_dir = fs.path.dirname(output_path).?;
106 }
107
108 self.output_file.path = fs.path.join(
109 self.builder.allocator,
110 &[_][]const u8{ self.output_dir.?, self.out_basename },
111 ) catch unreachable;
112}
lib/std/build/WriteFileStep.zig deleted-117
......@@ -1,117 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const ArrayList = std.ArrayList;
7
8const WriteFileStep = @This();
9
10pub const base_id = .write_file;
11
12step: Step,
13builder: *Builder,
14output_dir: []const u8,
15files: std.TailQueue(File),
16
17pub const File = struct {
18 source: build.GeneratedFile,
19 basename: []const u8,
20 bytes: []const u8,
21};
22
23pub fn init(builder: *Builder) WriteFileStep {
24 return WriteFileStep{
25 .builder = builder,
26 .step = Step.init(.write_file, "writefile", builder.allocator, make),
27 .files = .{},
28 .output_dir = undefined,
29 };
30}
31
32pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
33 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
34 node.* = .{
35 .data = .{
36 .source = build.GeneratedFile{ .step = &self.step },
37 .basename = self.builder.dupePath(basename),
38 .bytes = self.builder.dupe(bytes),
39 },
40 };
41
42 self.files.append(node);
43}
44
45/// Gets a file source for the given basename. If the file does not exist, returns `null`.
46pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource {
47 var it = step.files.first;
48 while (it) |node| : (it = node.next) {
49 if (std.mem.eql(u8, node.data.basename, basename))
50 return build.FileSource{ .generated = &node.data.source };
51 }
52 return null;
53}
54
55fn make(step: *Step) !void {
56 const self = @fieldParentPtr(WriteFileStep, "step", step);
57
58 // The cache is used here not really as a way to speed things up - because writing
59 // the data to a file would probably be very fast - but as a way to find a canonical
60 // location to put build artifacts.
61
62 // If, for example, a hard-coded path was used as the location to put WriteFileStep
63 // files, then two WriteFileSteps executing in parallel might clobber each other.
64
65 // TODO port the cache system from the compiler to zig std lib. Until then
66 // we directly construct the path, and no "cache hit" detection happens;
67 // the files are always written.
68 // Note there is similar code over in ConfigHeaderStep.
69 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
70 // Random bytes to make WriteFileStep unique. Refresh this with
71 // new random bytes when WriteFileStep implementation is modified
72 // in a non-backwards-compatible way.
73 var hash = Hasher.init("eagVR1dYXoE7ARDP");
74
75 {
76 var it = self.files.first;
77 while (it) |node| : (it = node.next) {
78 hash.update(node.data.basename);
79 hash.update(node.data.bytes);
80 hash.update("|");
81 }
82 }
83 var digest: [16]u8 = undefined;
84 hash.final(&digest);
85 var hash_basename: [digest.len * 2]u8 = undefined;
86 _ = std.fmt.bufPrint(
87 &hash_basename,
88 "{s}",
89 .{std.fmt.fmtSliceHexLower(&digest)},
90 ) catch unreachable;
91
92 self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
93 self.builder.cache_root, "o", &hash_basename,
94 });
95 var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
96 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
97 return err;
98 };
99 defer dir.close();
100 {
101 var it = self.files.first;
102 while (it) |node| : (it = node.next) {
103 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
104 std.debug.print("unable to write {s} into {s}: {s}\n", .{
105 node.data.basename,
106 self.output_dir,
107 @errorName(err),
108 });
109 return err;
110 };
111 node.data.source.path = fs.path.join(
112 self.builder.allocator,
113 &[_][]const u8{ self.output_dir, node.data.basename },
114 ) catch unreachable;
115 }
116 }
117}
lib/std/builtin.zig+4-1
......@@ -131,13 +131,16 @@ pub const CodeModel = enum {
131131
132132/// This data structure is used by the Zig language code generation and
133133/// therefore must be kept in sync with the compiler implementation.
134pub const Mode = enum {
134pub const OptimizeMode = enum {
135135 Debug,
136136 ReleaseSafe,
137137 ReleaseFast,
138138 ReleaseSmall,
139139};
140140
141/// Deprecated; use OptimizeMode.
142pub const Mode = OptimizeMode;
143
141144/// This data structure is used by the Zig language code generation and
142145/// therefore must be kept in sync with the compiler implementation.
143146pub const CallingConvention = enum {
lib/std/std.zig+4-1
......@@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
99pub const AutoHashMap = hash_map.AutoHashMap;
1010pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
1111pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
12pub const Build = @import("Build.zig");
1213pub const BufMap = @import("buf_map.zig").BufMap;
1314pub const BufSet = @import("buf_set.zig").BufSet;
1415pub const ChildProcess = @import("child_process.zig").ChildProcess;
......@@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig");
4950pub const atomic = @import("atomic.zig");
5051pub const base64 = @import("base64.zig");
5152pub const bit_set = @import("bit_set.zig");
52pub const build = @import("build.zig");
5353pub const builtin = @import("builtin.zig");
5454pub const c = @import("c.zig");
5555pub const coff = @import("coff.zig");
......@@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig");
9696pub const zig = @import("zig.zig");
9797pub const start = @import("start.zig");
9898
99/// deprecated: use `Build`.
100pub const build = Build;
101
99102const root = @import("root");
100103const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
101104
lib/std/target.zig+553
......@@ -1880,6 +1880,559 @@ pub const Target = struct {
18801880 => 16,
18811881 };
18821882 }
1883
1884 pub const CType = enum {
1885 short,
1886 ushort,
1887 int,
1888 uint,
1889 long,
1890 ulong,
1891 longlong,
1892 ulonglong,
1893 float,
1894 double,
1895 longdouble,
1896 };
1897
1898 pub fn c_type_byte_size(t: Target, c_type: CType) u16 {
1899 return switch (c_type) {
1900 .short,
1901 .ushort,
1902 .int,
1903 .uint,
1904 .long,
1905 .ulong,
1906 .longlong,
1907 .ulonglong,
1908 => @divExact(c_type_bit_size(t, c_type), 8),
1909
1910 .float => 4,
1911 .double => 8,
1912
1913 .longdouble => switch (c_type_bit_size(t, c_type)) {
1914 16 => 2,
1915 32 => 4,
1916 64 => 8,
1917 80 => @intCast(u16, mem.alignForward(10, c_type_alignment(t, .longdouble))),
1918 128 => 16,
1919 else => unreachable,
1920 },
1921 };
1922 }
1923
1924 pub fn c_type_bit_size(target: Target, c_type: CType) u16 {
1925 switch (target.os.tag) {
1926 .freestanding, .other => switch (target.cpu.arch) {
1927 .msp430 => switch (c_type) {
1928 .short, .ushort, .int, .uint => return 16,
1929 .float, .long, .ulong => return 32,
1930 .longlong, .ulonglong, .double, .longdouble => return 64,
1931 },
1932 .avr => switch (c_type) {
1933 .short, .ushort, .int, .uint => return 16,
1934 .long, .ulong, .float, .double, .longdouble => return 32,
1935 .longlong, .ulonglong => return 64,
1936 },
1937 .tce, .tcele => switch (c_type) {
1938 .short, .ushort => return 16,
1939 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
1940 .float, .double, .longdouble => return 32,
1941 },
1942 .mips64, .mips64el => switch (c_type) {
1943 .short, .ushort => return 16,
1944 .int, .uint, .float => return 32,
1945 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
1946 .longlong, .ulonglong, .double => return 64,
1947 .longdouble => return 128,
1948 },
1949 .x86_64 => switch (c_type) {
1950 .short, .ushort => return 16,
1951 .int, .uint, .float => return 32,
1952 .long, .ulong => switch (target.abi) {
1953 .gnux32, .muslx32 => return 32,
1954 else => return 64,
1955 },
1956 .longlong, .ulonglong, .double => return 64,
1957 .longdouble => return 80,
1958 },
1959 else => switch (c_type) {
1960 .short, .ushort => return 16,
1961 .int, .uint, .float => return 32,
1962 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
1963 .longlong, .ulonglong, .double => return 64,
1964 .longdouble => switch (target.cpu.arch) {
1965 .x86 => switch (target.abi) {
1966 .android => return 64,
1967 else => return 80,
1968 },
1969
1970 .powerpc,
1971 .powerpcle,
1972 .powerpc64,
1973 .powerpc64le,
1974 => switch (target.abi) {
1975 .musl,
1976 .musleabi,
1977 .musleabihf,
1978 .muslx32,
1979 => return 64,
1980 else => return 128,
1981 },
1982
1983 .riscv32,
1984 .riscv64,
1985 .aarch64,
1986 .aarch64_be,
1987 .aarch64_32,
1988 .s390x,
1989 .sparc,
1990 .sparc64,
1991 .sparcel,
1992 .wasm32,
1993 .wasm64,
1994 => return 128,
1995
1996 else => return 64,
1997 },
1998 },
1999 },
2000
2001 .linux,
2002 .freebsd,
2003 .netbsd,
2004 .dragonfly,
2005 .openbsd,
2006 .wasi,
2007 .emscripten,
2008 .plan9,
2009 .solaris,
2010 .haiku,
2011 .ananas,
2012 .fuchsia,
2013 .minix,
2014 => switch (target.cpu.arch) {
2015 .msp430 => switch (c_type) {
2016 .short, .ushort, .int, .uint => return 16,
2017 .long, .ulong, .float => return 32,
2018 .longlong, .ulonglong, .double, .longdouble => return 64,
2019 },
2020 .avr => switch (c_type) {
2021 .short, .ushort, .int, .uint => return 16,
2022 .long, .ulong, .float, .double, .longdouble => return 32,
2023 .longlong, .ulonglong => return 64,
2024 },
2025 .tce, .tcele => switch (c_type) {
2026 .short, .ushort => return 16,
2027 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
2028 .float, .double, .longdouble => return 32,
2029 },
2030 .mips64, .mips64el => switch (c_type) {
2031 .short, .ushort => return 16,
2032 .int, .uint, .float => return 32,
2033 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
2034 .longlong, .ulonglong, .double => return 64,
2035 .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
2036 },
2037 .x86_64 => switch (c_type) {
2038 .short, .ushort => return 16,
2039 .int, .uint, .float => return 32,
2040 .long, .ulong => switch (target.abi) {
2041 .gnux32, .muslx32 => return 32,
2042 else => return 64,
2043 },
2044 .longlong, .ulonglong, .double => return 64,
2045 .longdouble => return 80,
2046 },
2047 else => switch (c_type) {
2048 .short, .ushort => return 16,
2049 .int, .uint, .float => return 32,
2050 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
2051 .longlong, .ulonglong, .double => return 64,
2052 .longdouble => switch (target.cpu.arch) {
2053 .x86 => switch (target.abi) {
2054 .android => return 64,
2055 else => return 80,
2056 },
2057
2058 .powerpc,
2059 .powerpcle,
2060 => switch (target.abi) {
2061 .musl,
2062 .musleabi,
2063 .musleabihf,
2064 .muslx32,
2065 => return 64,
2066 else => switch (target.os.tag) {
2067 .freebsd, .netbsd, .openbsd => return 64,
2068 else => return 128,
2069 },
2070 },
2071
2072 .powerpc64,
2073 .powerpc64le,
2074 => switch (target.abi) {
2075 .musl,
2076 .musleabi,
2077 .musleabihf,
2078 .muslx32,
2079 => return 64,
2080 else => switch (target.os.tag) {
2081 .freebsd, .openbsd => return 64,
2082 else => return 128,
2083 },
2084 },
2085
2086 .riscv32,
2087 .riscv64,
2088 .aarch64,
2089 .aarch64_be,
2090 .aarch64_32,
2091 .s390x,
2092 .mips64,
2093 .mips64el,
2094 .sparc,
2095 .sparc64,
2096 .sparcel,
2097 .wasm32,
2098 .wasm64,
2099 => return 128,
2100
2101 else => return 64,
2102 },
2103 },
2104 },
2105
2106 .windows, .uefi => switch (target.cpu.arch) {
2107 .x86 => switch (c_type) {
2108 .short, .ushort => return 16,
2109 .int, .uint, .float => return 32,
2110 .long, .ulong => return 32,
2111 .longlong, .ulonglong, .double => return 64,
2112 .longdouble => switch (target.abi) {
2113 .gnu, .gnuilp32, .cygnus => return 80,
2114 else => return 64,
2115 },
2116 },
2117 .x86_64 => switch (c_type) {
2118 .short, .ushort => return 16,
2119 .int, .uint, .float => return 32,
2120 .long, .ulong => switch (target.abi) {
2121 .cygnus => return 64,
2122 else => return 32,
2123 },
2124 .longlong, .ulonglong, .double => return 64,
2125 .longdouble => switch (target.abi) {
2126 .gnu, .gnuilp32, .cygnus => return 80,
2127 else => return 64,
2128 },
2129 },
2130 else => switch (c_type) {
2131 .short, .ushort => return 16,
2132 .int, .uint, .float => return 32,
2133 .long, .ulong => return 32,
2134 .longlong, .ulonglong, .double => return 64,
2135 .longdouble => return 64,
2136 },
2137 },
2138
2139 .macos, .ios, .tvos, .watchos => switch (c_type) {
2140 .short, .ushort => return 16,
2141 .int, .uint, .float => return 32,
2142 .long, .ulong => switch (target.cpu.arch) {
2143 .x86, .arm, .aarch64_32 => return 32,
2144 .x86_64 => switch (target.abi) {
2145 .gnux32, .muslx32 => return 32,
2146 else => return 64,
2147 },
2148 else => return 64,
2149 },
2150 .longlong, .ulonglong, .double => return 64,
2151 .longdouble => switch (target.cpu.arch) {
2152 .x86 => switch (target.abi) {
2153 .android => return 64,
2154 else => return 80,
2155 },
2156 .x86_64 => return 80,
2157 else => return 64,
2158 },
2159 },
2160
2161 .nvcl, .cuda => switch (c_type) {
2162 .short, .ushort => return 16,
2163 .int, .uint, .float => return 32,
2164 .long, .ulong => switch (target.cpu.arch) {
2165 .nvptx => return 32,
2166 .nvptx64 => return 64,
2167 else => return 64,
2168 },
2169 .longlong, .ulonglong, .double => return 64,
2170 .longdouble => return 64,
2171 },
2172
2173 .amdhsa, .amdpal => switch (c_type) {
2174 .short, .ushort => return 16,
2175 .int, .uint, .float => return 32,
2176 .long, .ulong, .longlong, .ulonglong, .double => return 64,
2177 .longdouble => return 128,
2178 },
2179
2180 .cloudabi,
2181 .kfreebsd,
2182 .lv2,
2183 .zos,
2184 .rtems,
2185 .nacl,
2186 .aix,
2187 .ps4,
2188 .ps5,
2189 .elfiamcu,
2190 .mesa3d,
2191 .contiki,
2192 .hermit,
2193 .hurd,
2194 .opencl,
2195 .glsl450,
2196 .vulkan,
2197 .driverkit,
2198 .shadermodel,
2199 => @panic("TODO specify the C integer and float type sizes for this OS"),
2200 }
2201 }
2202
2203 pub fn c_type_alignment(target: Target, c_type: CType) u16 {
2204 // Overrides for unusual alignments
2205 switch (target.cpu.arch) {
2206 .avr => switch (c_type) {
2207 .short, .ushort => return 2,
2208 else => return 1,
2209 },
2210 .x86 => switch (target.os.tag) {
2211 .windows, .uefi => switch (c_type) {
2212 .longlong, .ulonglong, .double => return 8,
2213 .longdouble => switch (target.abi) {
2214 .gnu, .gnuilp32, .cygnus => return 4,
2215 else => return 8,
2216 },
2217 else => {},
2218 },
2219 else => {},
2220 },
2221 else => {},
2222 }
2223
2224 // Next-power-of-two-aligned, up to a maximum.
2225 return @min(
2226 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2227 switch (target.cpu.arch) {
2228 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2229 .netbsd => switch (target.abi) {
2230 .gnueabi,
2231 .gnueabihf,
2232 .eabi,
2233 .eabihf,
2234 .android,
2235 .musleabi,
2236 .musleabihf,
2237 => 8,
2238
2239 else => @as(u16, 4),
2240 },
2241 .ios, .tvos, .watchos => 4,
2242 else => 8,
2243 },
2244
2245 .msp430,
2246 .avr,
2247 => 2,
2248
2249 .arc,
2250 .csky,
2251 .x86,
2252 .xcore,
2253 .dxil,
2254 .loongarch32,
2255 .tce,
2256 .tcele,
2257 .le32,
2258 .amdil,
2259 .hsail,
2260 .spir,
2261 .spirv32,
2262 .kalimba,
2263 .shave,
2264 .renderscript32,
2265 .ve,
2266 .spu_2,
2267 => 4,
2268
2269 .aarch64_32,
2270 .amdgcn,
2271 .amdil64,
2272 .bpfel,
2273 .bpfeb,
2274 .hexagon,
2275 .hsail64,
2276 .loongarch64,
2277 .m68k,
2278 .mips,
2279 .mipsel,
2280 .sparc,
2281 .sparcel,
2282 .sparc64,
2283 .lanai,
2284 .le64,
2285 .nvptx,
2286 .nvptx64,
2287 .r600,
2288 .s390x,
2289 .spir64,
2290 .spirv64,
2291 .renderscript64,
2292 => 8,
2293
2294 .aarch64,
2295 .aarch64_be,
2296 .mips64,
2297 .mips64el,
2298 .powerpc,
2299 .powerpcle,
2300 .powerpc64,
2301 .powerpc64le,
2302 .riscv32,
2303 .riscv64,
2304 .x86_64,
2305 .wasm32,
2306 .wasm64,
2307 => 16,
2308 },
2309 );
2310 }
2311
2312 pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 {
2313 // Overrides for unusual alignments
2314 switch (target.cpu.arch) {
2315 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2316 .netbsd => switch (target.abi) {
2317 .gnueabi,
2318 .gnueabihf,
2319 .eabi,
2320 .eabihf,
2321 .android,
2322 .musleabi,
2323 .musleabihf,
2324 => {},
2325
2326 else => switch (c_type) {
2327 .longdouble => return 4,
2328 else => {},
2329 },
2330 },
2331 .ios, .tvos, .watchos => switch (c_type) {
2332 .longdouble => return 4,
2333 else => {},
2334 },
2335 else => {},
2336 },
2337 .arc => switch (c_type) {
2338 .longdouble => return 4,
2339 else => {},
2340 },
2341 .avr => switch (c_type) {
2342 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
2343 .short, .ushort => return 2,
2344 .double => return 4,
2345 .longlong, .ulonglong => return 8,
2346 },
2347 .x86 => switch (target.os.tag) {
2348 .windows, .uefi => switch (c_type) {
2349 .longdouble => switch (target.abi) {
2350 .gnu, .gnuilp32, .cygnus => return 4,
2351 else => return 8,
2352 },
2353 else => {},
2354 },
2355 else => switch (c_type) {
2356 .longdouble => return 4,
2357 else => {},
2358 },
2359 },
2360 else => {},
2361 }
2362
2363 // Next-power-of-two-aligned, up to a maximum.
2364 return @min(
2365 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2366 switch (target.cpu.arch) {
2367 .msp430 => @as(u16, 2),
2368
2369 .csky,
2370 .xcore,
2371 .dxil,
2372 .loongarch32,
2373 .tce,
2374 .tcele,
2375 .le32,
2376 .amdil,
2377 .hsail,
2378 .spir,
2379 .spirv32,
2380 .kalimba,
2381 .shave,
2382 .renderscript32,
2383 .ve,
2384 .spu_2,
2385 => 4,
2386
2387 .arc,
2388 .arm,
2389 .armeb,
2390 .avr,
2391 .thumb,
2392 .thumbeb,
2393 .aarch64_32,
2394 .amdgcn,
2395 .amdil64,
2396 .bpfel,
2397 .bpfeb,
2398 .hexagon,
2399 .hsail64,
2400 .x86,
2401 .loongarch64,
2402 .m68k,
2403 .mips,
2404 .mipsel,
2405 .sparc,
2406 .sparcel,
2407 .sparc64,
2408 .lanai,
2409 .le64,
2410 .nvptx,
2411 .nvptx64,
2412 .r600,
2413 .s390x,
2414 .spir64,
2415 .spirv64,
2416 .renderscript64,
2417 => 8,
2418
2419 .aarch64,
2420 .aarch64_be,
2421 .mips64,
2422 .mips64el,
2423 .powerpc,
2424 .powerpcle,
2425 .powerpc64,
2426 .powerpc64le,
2427 .riscv32,
2428 .riscv64,
2429 .x86_64,
2430 .wasm32,
2431 .wasm64,
2432 => 16,
2433 },
2434 );
2435 }
18832436};
18842437
18852438test {
src/Sema.zig+1-1
......@@ -26076,7 +26076,7 @@ fn coerceVarArgParam(
2607626076 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
2607726077 .Float => float: {
2607826078 const target = sema.mod.getTarget();
26079 const double_bits = @import("type.zig").CType.sizeInBits(.double, target);
26079 const double_bits = target.c_type_bit_size(.double);
2608026080 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
2608126081 if (inst_bits >= double_bits) break :float inst;
2608226082 switch (double_bits) {
src/codegen/c.zig-1
......@@ -16,7 +16,6 @@ const trace = @import("../tracy.zig").trace;
1616const LazySrcLoc = Module.LazySrcLoc;
1717const Air = @import("../Air.zig");
1818const Liveness = @import("../Liveness.zig");
19const CType = @import("../type.zig").CType;
2019
2120const target_util = @import("../target.zig");
2221const libcFloatPrefix = target_util.libcFloatPrefix;
src/codegen/llvm.zig+2-3
......@@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig");
1919const Value = @import("../value.zig").Value;
2020const Type = @import("../type.zig").Type;
2121const LazySrcLoc = Module.LazySrcLoc;
22const CType = @import("../type.zig").CType;
2322const x86_64_abi = @import("../arch/x86_64/abi.zig");
2423const wasm_c_abi = @import("../arch/wasm/abi.zig");
2524const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
......@@ -11043,8 +11042,8 @@ fn backendSupportsF128(target: std.Target) bool {
1104311042fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
1104411043 return switch (scalar_ty.tag()) {
1104511044 .f16 => backendSupportsF16(target),
11046 .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target),
11047 .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target),
11045 .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target),
11046 .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target),
1104811047 else => true,
1104911048 };
1105011049}
src/link/MachO/zld.zig+7-4
......@@ -3596,7 +3596,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35963596 man.hash.addOptionalBytes(options.sysroot);
35973597 try man.addOptionalFile(options.entitlements);
35983598
3599 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
3599 // We don't actually care whether it's a cache hit or miss; we just
3600 // need the digest and the lock.
36003601 _ = try man.hit();
36013602 digest = man.final();
36023603
......@@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
41774178 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
41784179 };
41794180 // Again failure here only means an unnecessary cache miss.
4180 man.writeManifest() catch |err| {
4181 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4182 };
4181 if (man.have_exclusive_lock) {
4182 man.writeManifest() catch |err| {
4183 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4184 };
4185 }
41834186 // We hang on to this lock so that the output file path can be used without
41844187 // other processes clobbering it.
41854188 macho_file.base.lock = man.toOwnedLock();
src/type.zig+45-585
......@@ -2937,24 +2937,24 @@ pub const Type = extern union {
29372937 .anyframe_T,
29382938 => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
29392939
2940 .c_short => return AbiAlignmentAdvanced{ .scalar = CType.short.alignment(target) },
2941 .c_ushort => return AbiAlignmentAdvanced{ .scalar = CType.ushort.alignment(target) },
2942 .c_int => return AbiAlignmentAdvanced{ .scalar = CType.int.alignment(target) },
2943 .c_uint => return AbiAlignmentAdvanced{ .scalar = CType.uint.alignment(target) },
2944 .c_long => return AbiAlignmentAdvanced{ .scalar = CType.long.alignment(target) },
2945 .c_ulong => return AbiAlignmentAdvanced{ .scalar = CType.ulong.alignment(target) },
2946 .c_longlong => return AbiAlignmentAdvanced{ .scalar = CType.longlong.alignment(target) },
2947 .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = CType.ulonglong.alignment(target) },
2948 .c_longdouble => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },
2940 .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) },
2941 .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) },
2942 .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) },
2943 .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) },
2944 .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) },
2945 .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) },
2946 .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) },
2947 .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) },
2948 .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29492949
29502950 .f16 => return AbiAlignmentAdvanced{ .scalar = 2 },
2951 .f32 => return AbiAlignmentAdvanced{ .scalar = CType.float.alignment(target) },
2952 .f64 => switch (CType.double.sizeInBits(target)) {
2953 64 => return AbiAlignmentAdvanced{ .scalar = CType.double.alignment(target) },
2951 .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) },
2952 .f64 => switch (target.c_type_bit_size(.double)) {
2953 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) },
29542954 else => return AbiAlignmentAdvanced{ .scalar = 8 },
29552955 },
2956 .f80 => switch (CType.longdouble.sizeInBits(target)) {
2957 80 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },
2956 .f80 => switch (target.c_type_bit_size(.longdouble)) {
2957 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29582958 else => {
29592959 var payload: Payload.Bits = .{
29602960 .base = .{ .tag = .int_unsigned },
......@@ -2964,8 +2964,8 @@ pub const Type = extern union {
29642964 return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) };
29652965 },
29662966 },
2967 .f128 => switch (CType.longdouble.sizeInBits(target)) {
2968 128 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },
2967 .f128 => switch (target.c_type_bit_size(.longdouble)) {
2968 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29692969 else => return AbiAlignmentAdvanced{ .scalar = 16 },
29702970 },
29712971
......@@ -3434,21 +3434,22 @@ pub const Type = extern union {
34343434 else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
34353435 },
34363436
3437 .c_short => return AbiSizeAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) },
3438 .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) },
3439 .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) },
3440 .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) },
3441 .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) },
3442 .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) },
3443 .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) },
3444 .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) },
3437 .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) },
3438 .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) },
3439 .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) },
3440 .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) },
3441 .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) },
3442 .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) },
3443 .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) },
3444 .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) },
3445 .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
34453446
34463447 .f16 => return AbiSizeAdvanced{ .scalar = 2 },
34473448 .f32 => return AbiSizeAdvanced{ .scalar = 4 },
34483449 .f64 => return AbiSizeAdvanced{ .scalar = 8 },
34493450 .f128 => return AbiSizeAdvanced{ .scalar = 16 },
3450 .f80 => switch (CType.longdouble.sizeInBits(target)) {
3451 80 => return AbiSizeAdvanced{ .scalar = std.mem.alignForward(10, CType.longdouble.alignment(target)) },
3451 .f80 => switch (target.c_type_bit_size(.longdouble)) {
3452 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
34523453 else => {
34533454 var payload: Payload.Bits = .{
34543455 .base = .{ .tag = .int_unsigned },
......@@ -3458,14 +3459,6 @@ pub const Type = extern union {
34583459 return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) };
34593460 },
34603461 },
3461 .c_longdouble => switch (CType.longdouble.sizeInBits(target)) {
3462 16 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f16, target) },
3463 32 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f32, target) },
3464 64 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f64, target) },
3465 80 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f80, target) },
3466 128 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f128, target) },
3467 else => unreachable,
3468 },
34693462
34703463 // TODO revisit this when we have the concept of the error tag type
34713464 .anyerror_void_error_union,
......@@ -3748,15 +3741,15 @@ pub const Type = extern union {
37483741 .manyptr_const_u8_sentinel_0,
37493742 => return target.cpu.arch.ptrBitWidth(),
37503743
3751 .c_short => return CType.short.sizeInBits(target),
3752 .c_ushort => return CType.ushort.sizeInBits(target),
3753 .c_int => return CType.int.sizeInBits(target),
3754 .c_uint => return CType.uint.sizeInBits(target),
3755 .c_long => return CType.long.sizeInBits(target),
3756 .c_ulong => return CType.ulong.sizeInBits(target),
3757 .c_longlong => return CType.longlong.sizeInBits(target),
3758 .c_ulonglong => return CType.ulonglong.sizeInBits(target),
3759 .c_longdouble => return CType.longdouble.sizeInBits(target),
3744 .c_short => return target.c_type_bit_size(.short),
3745 .c_ushort => return target.c_type_bit_size(.ushort),
3746 .c_int => return target.c_type_bit_size(.int),
3747 .c_uint => return target.c_type_bit_size(.uint),
3748 .c_long => return target.c_type_bit_size(.long),
3749 .c_ulong => return target.c_type_bit_size(.ulong),
3750 .c_longlong => return target.c_type_bit_size(.longlong),
3751 .c_ulonglong => return target.c_type_bit_size(.ulonglong),
3752 .c_longdouble => return target.c_type_bit_size(.longdouble),
37603753
37613754 .error_set,
37623755 .error_set_single,
......@@ -4631,14 +4624,14 @@ pub const Type = extern union {
46314624 .i128 => return .{ .signedness = .signed, .bits = 128 },
46324625 .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },
46334626 .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },
4634 .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },
4635 .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) },
4636 .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) },
4637 .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) },
4638 .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) },
4639 .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) },
4640 .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) },
4641 .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) },
4627 .c_short => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) },
4628 .c_ushort => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) },
4629 .c_int => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) },
4630 .c_uint => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) },
4631 .c_long => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) },
4632 .c_ulong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) },
4633 .c_longlong => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) },
4634 .c_ulonglong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) },
46424635
46434636 .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty,
46444637 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
......@@ -4724,7 +4717,7 @@ pub const Type = extern union {
47244717 .f64 => 64,
47254718 .f80 => 80,
47264719 .f128, .comptime_float => 128,
4727 .c_longdouble => CType.longdouble.sizeInBits(target),
4720 .c_longdouble => target.c_type_bit_size(.longdouble),
47284721
47294722 else => unreachable,
47304723 };
......@@ -6689,536 +6682,3 @@ pub const Type = extern union {
66896682 /// to packed struct layout to find out all the places in the codebase you need to edit!
66906683 pub const packed_struct_layout_version = 2;
66916684};
6692
6693pub const CType = enum {
6694 short,
6695 ushort,
6696 int,
6697 uint,
6698 long,
6699 ulong,
6700 longlong,
6701 ulonglong,
6702 longdouble,
6703
6704 // We don't have a `c_float`/`c_double` type in Zig, but these
6705 // are useful for querying target-correct alignment and checking
6706 // whether C's double is f64 or f32
6707 float,
6708 double,
6709
6710 pub fn sizeInBits(self: CType, target: Target) u16 {
6711 switch (target.os.tag) {
6712 .freestanding, .other => switch (target.cpu.arch) {
6713 .msp430 => switch (self) {
6714 .short, .ushort, .int, .uint => return 16,
6715 .float, .long, .ulong => return 32,
6716 .longlong, .ulonglong, .double, .longdouble => return 64,
6717 },
6718 .avr => switch (self) {
6719 .short, .ushort, .int, .uint => return 16,
6720 .long, .ulong, .float, .double, .longdouble => return 32,
6721 .longlong, .ulonglong => return 64,
6722 },
6723 .tce, .tcele => switch (self) {
6724 .short, .ushort => return 16,
6725 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
6726 .float, .double, .longdouble => return 32,
6727 },
6728 .mips64, .mips64el => switch (self) {
6729 .short, .ushort => return 16,
6730 .int, .uint, .float => return 32,
6731 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
6732 .longlong, .ulonglong, .double => return 64,
6733 .longdouble => return 128,
6734 },
6735 .x86_64 => switch (self) {
6736 .short, .ushort => return 16,
6737 .int, .uint, .float => return 32,
6738 .long, .ulong => switch (target.abi) {
6739 .gnux32, .muslx32 => return 32,
6740 else => return 64,
6741 },
6742 .longlong, .ulonglong, .double => return 64,
6743 .longdouble => return 80,
6744 },
6745 else => switch (self) {
6746 .short, .ushort => return 16,
6747 .int, .uint, .float => return 32,
6748 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
6749 .longlong, .ulonglong, .double => return 64,
6750 .longdouble => switch (target.cpu.arch) {
6751 .x86 => switch (target.abi) {
6752 .android => return 64,
6753 else => return 80,
6754 },
6755
6756 .powerpc,
6757 .powerpcle,
6758 .powerpc64,
6759 .powerpc64le,
6760 => switch (target.abi) {
6761 .musl,
6762 .musleabi,
6763 .musleabihf,
6764 .muslx32,
6765 => return 64,
6766 else => return 128,
6767 },
6768
6769 .riscv32,
6770 .riscv64,
6771 .aarch64,
6772 .aarch64_be,
6773 .aarch64_32,
6774 .s390x,
6775 .sparc,
6776 .sparc64,
6777 .sparcel,
6778 .wasm32,
6779 .wasm64,
6780 => return 128,
6781
6782 else => return 64,
6783 },
6784 },
6785 },
6786
6787 .linux,
6788 .freebsd,
6789 .netbsd,
6790 .dragonfly,
6791 .openbsd,
6792 .wasi,
6793 .emscripten,
6794 .plan9,
6795 .solaris,
6796 .haiku,
6797 .ananas,
6798 .fuchsia,
6799 .minix,
6800 => switch (target.cpu.arch) {
6801 .msp430 => switch (self) {
6802 .short, .ushort, .int, .uint => return 16,
6803 .long, .ulong, .float => return 32,
6804 .longlong, .ulonglong, .double, .longdouble => return 64,
6805 },
6806 .avr => switch (self) {
6807 .short, .ushort, .int, .uint => return 16,
6808 .long, .ulong, .float, .double, .longdouble => return 32,
6809 .longlong, .ulonglong => return 64,
6810 },
6811 .tce, .tcele => switch (self) {
6812 .short, .ushort => return 16,
6813 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
6814 .float, .double, .longdouble => return 32,
6815 },
6816 .mips64, .mips64el => switch (self) {
6817 .short, .ushort => return 16,
6818 .int, .uint, .float => return 32,
6819 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
6820 .longlong, .ulonglong, .double => return 64,
6821 .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
6822 },
6823 .x86_64 => switch (self) {
6824 .short, .ushort => return 16,
6825 .int, .uint, .float => return 32,
6826 .long, .ulong => switch (target.abi) {
6827 .gnux32, .muslx32 => return 32,
6828 else => return 64,
6829 },
6830 .longlong, .ulonglong, .double => return 64,
6831 .longdouble => return 80,
6832 },
6833 else => switch (self) {
6834 .short, .ushort => return 16,
6835 .int, .uint, .float => return 32,
6836 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
6837 .longlong, .ulonglong, .double => return 64,
6838 .longdouble => switch (target.cpu.arch) {
6839 .x86 => switch (target.abi) {
6840 .android => return 64,
6841 else => return 80,
6842 },
6843
6844 .powerpc,
6845 .powerpcle,
6846 => switch (target.abi) {
6847 .musl,
6848 .musleabi,
6849 .musleabihf,
6850 .muslx32,
6851 => return 64,
6852 else => switch (target.os.tag) {
6853 .freebsd, .netbsd, .openbsd => return 64,
6854 else => return 128,
6855 },
6856 },
6857
6858 .powerpc64,
6859 .powerpc64le,
6860 => switch (target.abi) {
6861 .musl,
6862 .musleabi,
6863 .musleabihf,
6864 .muslx32,
6865 => return 64,
6866 else => switch (target.os.tag) {
6867 .freebsd, .openbsd => return 64,
6868 else => return 128,
6869 },
6870 },
6871
6872 .riscv32,
6873 .riscv64,
6874 .aarch64,
6875 .aarch64_be,
6876 .aarch64_32,
6877 .s390x,
6878 .mips64,
6879 .mips64el,
6880 .sparc,
6881 .sparc64,
6882 .sparcel,
6883 .wasm32,
6884 .wasm64,
6885 => return 128,
6886
6887 else => return 64,
6888 },
6889 },
6890 },
6891
6892 .windows, .uefi => switch (target.cpu.arch) {
6893 .x86 => switch (self) {
6894 .short, .ushort => return 16,
6895 .int, .uint, .float => return 32,
6896 .long, .ulong => return 32,
6897 .longlong, .ulonglong, .double => return 64,
6898 .longdouble => switch (target.abi) {
6899 .gnu, .gnuilp32, .cygnus => return 80,
6900 else => return 64,
6901 },
6902 },
6903 .x86_64 => switch (self) {
6904 .short, .ushort => return 16,
6905 .int, .uint, .float => return 32,
6906 .long, .ulong => switch (target.abi) {
6907 .cygnus => return 64,
6908 else => return 32,
6909 },
6910 .longlong, .ulonglong, .double => return 64,
6911 .longdouble => switch (target.abi) {
6912 .gnu, .gnuilp32, .cygnus => return 80,
6913 else => return 64,
6914 },
6915 },
6916 else => switch (self) {
6917 .short, .ushort => return 16,
6918 .int, .uint, .float => return 32,
6919 .long, .ulong => return 32,
6920 .longlong, .ulonglong, .double => return 64,
6921 .longdouble => return 64,
6922 },
6923 },
6924
6925 .macos, .ios, .tvos, .watchos => switch (self) {
6926 .short, .ushort => return 16,
6927 .int, .uint, .float => return 32,
6928 .long, .ulong => switch (target.cpu.arch) {
6929 .x86, .arm, .aarch64_32 => return 32,
6930 .x86_64 => switch (target.abi) {
6931 .gnux32, .muslx32 => return 32,
6932 else => return 64,
6933 },
6934 else => return 64,
6935 },
6936 .longlong, .ulonglong, .double => return 64,
6937 .longdouble => switch (target.cpu.arch) {
6938 .x86 => switch (target.abi) {
6939 .android => return 64,
6940 else => return 80,
6941 },
6942 .x86_64 => return 80,
6943 else => return 64,
6944 },
6945 },
6946
6947 .nvcl, .cuda => switch (self) {
6948 .short, .ushort => return 16,
6949 .int, .uint, .float => return 32,
6950 .long, .ulong => switch (target.cpu.arch) {
6951 .nvptx => return 32,
6952 .nvptx64 => return 64,
6953 else => return 64,
6954 },
6955 .longlong, .ulonglong, .double => return 64,
6956 .longdouble => return 64,
6957 },
6958
6959 .amdhsa, .amdpal => switch (self) {
6960 .short, .ushort => return 16,
6961 .int, .uint, .float => return 32,
6962 .long, .ulong, .longlong, .ulonglong, .double => return 64,
6963 .longdouble => return 128,
6964 },
6965
6966 .cloudabi,
6967 .kfreebsd,
6968 .lv2,
6969 .zos,
6970 .rtems,
6971 .nacl,
6972 .aix,
6973 .ps4,
6974 .ps5,
6975 .elfiamcu,
6976 .mesa3d,
6977 .contiki,
6978 .hermit,
6979 .hurd,
6980 .opencl,
6981 .glsl450,
6982 .vulkan,
6983 .driverkit,
6984 .shadermodel,
6985 => @panic("TODO specify the C integer and float type sizes for this OS"),
6986 }
6987 }
6988
6989 pub fn alignment(self: CType, target: Target) u16 {
6990
6991 // Overrides for unusual alignments
6992 switch (target.cpu.arch) {
6993 .avr => switch (self) {
6994 .short, .ushort => return 2,
6995 else => return 1,
6996 },
6997 .x86 => switch (target.os.tag) {
6998 .windows, .uefi => switch (self) {
6999 .longlong, .ulonglong, .double => return 8,
7000 .longdouble => switch (target.abi) {
7001 .gnu, .gnuilp32, .cygnus => return 4,
7002 else => return 8,
7003 },
7004 else => {},
7005 },
7006 else => {},
7007 },
7008 else => {},
7009 }
7010
7011 // Next-power-of-two-aligned, up to a maximum.
7012 return @min(
7013 std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8),
7014 switch (target.cpu.arch) {
7015 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
7016 .netbsd => switch (target.abi) {
7017 .gnueabi,
7018 .gnueabihf,
7019 .eabi,
7020 .eabihf,
7021 .android,
7022 .musleabi,
7023 .musleabihf,
7024 => 8,
7025
7026 else => @as(u16, 4),
7027 },
7028 .ios, .tvos, .watchos => 4,
7029 else => 8,
7030 },
7031
7032 .msp430,
7033 .avr,
7034 => 2,
7035
7036 .arc,
7037 .csky,
7038 .x86,
7039 .xcore,
7040 .dxil,
7041 .loongarch32,
7042 .tce,
7043 .tcele,
7044 .le32,
7045 .amdil,
7046 .hsail,
7047 .spir,
7048 .spirv32,
7049 .kalimba,
7050 .shave,
7051 .renderscript32,
7052 .ve,
7053 .spu_2,
7054 => 4,
7055
7056 .aarch64_32,
7057 .amdgcn,
7058 .amdil64,
7059 .bpfel,
7060 .bpfeb,
7061 .hexagon,
7062 .hsail64,
7063 .loongarch64,
7064 .m68k,
7065 .mips,
7066 .mipsel,
7067 .sparc,
7068 .sparcel,
7069 .sparc64,
7070 .lanai,
7071 .le64,
7072 .nvptx,
7073 .nvptx64,
7074 .r600,
7075 .s390x,
7076 .spir64,
7077 .spirv64,
7078 .renderscript64,
7079 => 8,
7080
7081 .aarch64,
7082 .aarch64_be,
7083 .mips64,
7084 .mips64el,
7085 .powerpc,
7086 .powerpcle,
7087 .powerpc64,
7088 .powerpc64le,
7089 .riscv32,
7090 .riscv64,
7091 .x86_64,
7092 .wasm32,
7093 .wasm64,
7094 => 16,
7095 },
7096 );
7097 }
7098
7099 pub fn preferredAlignment(self: CType, target: Target) u16 {
7100
7101 // Overrides for unusual alignments
7102 switch (target.cpu.arch) {
7103 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
7104 .netbsd => switch (target.abi) {
7105 .gnueabi,
7106 .gnueabihf,
7107 .eabi,
7108 .eabihf,
7109 .android,
7110 .musleabi,
7111 .musleabihf,
7112 => {},
7113
7114 else => switch (self) {
7115 .longdouble => return 4,
7116 else => {},
7117 },
7118 },
7119 .ios, .tvos, .watchos => switch (self) {
7120 .longdouble => return 4,
7121 else => {},
7122 },
7123 else => {},
7124 },
7125 .arc => switch (self) {
7126 .longdouble => return 4,
7127 else => {},
7128 },
7129 .avr => switch (self) {
7130 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
7131 .short, .ushort => return 2,
7132 .double => return 4,
7133 .longlong, .ulonglong => return 8,
7134 },
7135 .x86 => switch (target.os.tag) {
7136 .windows, .uefi => switch (self) {
7137 .longdouble => switch (target.abi) {
7138 .gnu, .gnuilp32, .cygnus => return 4,
7139 else => return 8,
7140 },
7141 else => {},
7142 },
7143 else => switch (self) {
7144 .longdouble => return 4,
7145 else => {},
7146 },
7147 },
7148 else => {},
7149 }
7150
7151 // Next-power-of-two-aligned, up to a maximum.
7152 return @min(
7153 std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8),
7154 switch (target.cpu.arch) {
7155 .msp430 => @as(u16, 2),
7156
7157 .csky,
7158 .xcore,
7159 .dxil,
7160 .loongarch32,
7161 .tce,
7162 .tcele,
7163 .le32,
7164 .amdil,
7165 .hsail,
7166 .spir,
7167 .spirv32,
7168 .kalimba,
7169 .shave,
7170 .renderscript32,
7171 .ve,
7172 .spu_2,
7173 => 4,
7174
7175 .arc,
7176 .arm,
7177 .armeb,
7178 .avr,
7179 .thumb,
7180 .thumbeb,
7181 .aarch64_32,
7182 .amdgcn,
7183 .amdil64,
7184 .bpfel,
7185 .bpfeb,
7186 .hexagon,
7187 .hsail64,
7188 .x86,
7189 .loongarch64,
7190 .m68k,
7191 .mips,
7192 .mipsel,
7193 .sparc,
7194 .sparcel,
7195 .sparc64,
7196 .lanai,
7197 .le64,
7198 .nvptx,
7199 .nvptx64,
7200 .r600,
7201 .s390x,
7202 .spir64,
7203 .spirv64,
7204 .renderscript64,
7205 => 8,
7206
7207 .aarch64,
7208 .aarch64_be,
7209 .mips64,
7210 .mips64el,
7211 .powerpc,
7212 .powerpcle,
7213 .powerpc64,
7214 .powerpc64le,
7215 .riscv32,
7216 .riscv64,
7217 .x86_64,
7218 .wasm32,
7219 .wasm64,
7220 => 16,
7221 },
7222 );
7223 }
7224};
test/cases/compile_errors/invalid_member_of_builtin_enum.zig+2-2
......@@ -1,6 +1,6 @@
11const builtin = @import("std").builtin;
22export fn entry() void {
3 const foo = builtin.Mode.x86;
3 const foo = builtin.OptimizeMode.x86;
44 _ = foo;
55}
66
......@@ -8,5 +8,5 @@ export fn entry() void {
88// backend=stage2
99// target=native
1010//
11// :3:30: error: enum 'builtin.Mode' has no member named 'x86'
11// :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86'
1212// :?:18: note: enum declared here
test/link/bss/build.zig+8-5
......@@ -1,12 +1,15 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55 const test_step = b.step("test", "Test");
66
7 const exe = b.addExecutable("bss", "main.zig");
7 const exe = b.addExecutable(.{
8 .name = "bss",
9 .root_source_file = .{ .path = "main.zig" },
10 .optimize = optimize,
11 });
812 b.default_step.dependOn(&exe.step);
9 exe.setBuildMode(mode);
1013
1114 const run = exe.run();
1215 run.expectStdOutEqual("0, 1, 0\n");
test/link/common_symbols/build.zig+12-7
......@@ -1,14 +1,19 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const lib_a = b.addStaticLibrary("a", null);
6 const lib_a = b.addStaticLibrary(.{
7 .name = "a",
8 .optimize = optimize,
9 .target = .{},
10 });
711 lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"});
8 lib_a.setBuildMode(mode);
912
10 const test_exe = b.addTest("main.zig");
11 test_exe.setBuildMode(mode);
13 const test_exe = b.addTest(.{
14 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,
16 });
1217 test_exe.linkLibrary(lib_a);
1318
1419 const test_step = b.step("test", "Test it");
test/link/common_symbols_alignment/build.zig+14-7
......@@ -1,14 +1,21 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
56
6 const lib_a = b.addStaticLibrary("a", null);
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
712 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});
8 lib_a.setBuildMode(mode);
913
10 const test_exe = b.addTest("main.zig");
11 test_exe.setBuildMode(mode);
14 const test_exe = b.addTest(.{
15 .root_source_file = .{ .path = "main.zig" },
16 .optimize = optimize,
17 .target = target,
18 });
1219 test_exe.linkLibrary(lib_a);
1320
1421 const test_step = b.step("test", "Test it");
test/link/interdependent_static_c_libs/build.zig+19-9
......@@ -1,20 +1,30 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
56
6 const lib_a = b.addStaticLibrary("a", null);
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
712 lib_a.addCSourceFile("a.c", &[_][]const u8{});
8 lib_a.setBuildMode(mode);
913 lib_a.addIncludePath(".");
1014
11 const lib_b = b.addStaticLibrary("b", null);
15 const lib_b = b.addStaticLibrary(.{
16 .name = "b",
17 .optimize = optimize,
18 .target = target,
19 });
1220 lib_b.addCSourceFile("b.c", &[_][]const u8{});
13 lib_b.setBuildMode(mode);
1421 lib_b.addIncludePath(".");
1522
16 const test_exe = b.addTest("main.zig");
17 test_exe.setBuildMode(mode);
23 const test_exe = b.addTest(.{
24 .root_source_file = .{ .path = "main.zig" },
25 .optimize = optimize,
26 .target = target,
27 });
1828 test_exe.linkLibrary(lib_a);
1929 test_exe.linkLibrary(lib_b);
2030 test_exe.addIncludePath(".");
test/link/macho/bugs/13056/build.zig+6-5
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
......@@ -11,7 +10,10 @@ pub fn build(b: *Builder) void {
1110
1211 const test_step = b.step("test", "Test the program");
1312
14 const exe = b.addExecutable("test", null);
13 const exe = b.addExecutable(.{
14 .name = "test",
15 .optimize = optimize,
16 });
1517 b.default_step.dependOn(&exe.step);
1618 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);
1719 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);
......@@ -20,7 +22,6 @@ pub fn build(b: *Builder) void {
2022 "-nostdinc++",
2123 });
2224 exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable);
23 exe.setBuildMode(mode);
2425
2526 const run_cmd = exe.run();
2627 run_cmd.expectStdErrEqual("x: 5\n");
test/link/macho/bugs/13457/build.zig+8-7
......@@ -1,16 +1,17 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
108
11 const exe = b.addExecutable("test", "main.zig");
12 exe.setBuildMode(mode);
13 exe.setTarget(target);
9 const exe = b.addExecutable(.{
10 .name = "test",
11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = optimize,
13 .target = target,
14 });
1415
1516 const run = exe.runEmulatable();
1617 test_step.dependOn(&run.step);
test/link/macho/dead_strip/build.zig+14-10
......@@ -1,9 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
......@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {
119
1210 {
1311 // Without -dead_strip, we expect `iAmUnused` symbol present
14 const exe = createScenario(b, mode, target);
12 const exe = createScenario(b, optimize, target);
1513
1614 const check = exe.checkObject(.macho);
1715 check.checkInSymtab();
......@@ -24,7 +22,7 @@ pub fn build(b: *Builder) void {
2422
2523 {
2624 // With -dead_strip, no `iAmUnused` symbol should be present
27 const exe = createScenario(b, mode, target);
25 const exe = createScenario(b, optimize, target);
2826 exe.link_gc_sections = true;
2927
3028 const check = exe.checkObject(.macho);
......@@ -37,11 +35,17 @@ pub fn build(b: *Builder) void {
3735 }
3836}
3937
40fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
41 const exe = b.addExecutable("test", null);
38fn createScenario(
39 b: *std.Build,
40 optimize: std.builtin.OptimizeMode,
41 target: std.zig.CrossTarget,
42) *std.Build.CompileStep {
43 const exe = b.addExecutable(.{
44 .name = "test",
45 .optimize = optimize,
46 .target = target,
47 });
4248 exe.addCSourceFile("main.c", &[0][]const u8{});
43 exe.setBuildMode(mode);
44 exe.setTarget(target);
4549 exe.linkLibC();
4650 return exe;
4751}
test/link/macho/dead_strip_dylibs/build.zig+9-9
......@@ -1,16 +1,14 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75
86 const test_step = b.step("test", "Test the program");
97 test_step.dependOn(b.getInstallStep());
108
119 {
1210 // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable
13 const exe = createScenario(b, mode);
11 const exe = createScenario(b, optimize);
1412
1513 const check = exe.checkObject(.macho);
1614 check.checkStart("cmd LOAD_DYLIB");
......@@ -27,7 +25,7 @@ pub fn build(b: *Builder) void {
2725
2826 {
2927 // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable
30 const exe = createScenario(b, mode);
28 const exe = createScenario(b, optimize);
3129 exe.dead_strip_dylibs = true;
3230
3331 const run_cmd = exe.run();
......@@ -36,10 +34,12 @@ pub fn build(b: *Builder) void {
3634 }
3735}
3836
39fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
40 const exe = b.addExecutable("test", null);
37fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
38 const exe = b.addExecutable(.{
39 .name = "test",
40 .optimize = optimize,
41 });
4142 exe.addCSourceFile("main.c", &[0][]const u8{});
42 exe.setBuildMode(mode);
4343 exe.linkLibC();
4444 exe.linkFramework("Cocoa");
4545 return exe;
test/link/macho/dylib/build.zig+13-9
......@@ -1,16 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test");
98 test_step.dependOn(b.getInstallStep());
109
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
12 dylib.setBuildMode(mode);
13 dylib.setTarget(target);
10 const dylib = b.addSharedLibrary(.{
11 .name = "a",
12 .version = .{ .major = 1, .minor = 0 },
13 .optimize = optimize,
14 .target = target,
15 });
1416 dylib.addCSourceFile("a.c", &.{});
1517 dylib.linkLibC();
1618 dylib.install();
......@@ -24,9 +26,11 @@ pub fn build(b: *Builder) void {
2426
2527 test_step.dependOn(&check_dylib.step);
2628
27 const exe = b.addExecutable("main", null);
28 exe.setTarget(target);
29 exe.setBuildMode(mode);
29 const exe = b.addExecutable(.{
30 .name = "main",
31 .optimize = optimize,
32 .target = target,
33 });
3034 exe.addCSourceFile("main.c", &.{});
3135 exe.linkSystemLibrary("a");
3236 exe.linkLibC();
test/link/macho/empty/build.zig+8-7
......@@ -1,21 +1,22 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test the program");
98 test_step.dependOn(b.getInstallStep());
109
11 const exe = b.addExecutable("test", null);
10 const exe = b.addExecutable(.{
11 .name = "test",
12 .optimize = optimize,
13 .target = target,
14 });
1215 exe.addCSourceFile("main.c", &[0][]const u8{});
1316 exe.addCSourceFile("empty.c", &[0][]const u8{});
14 exe.setBuildMode(mode);
15 exe.setTarget(target);
1617 exe.linkLibC();
1718
18 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
19 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
1920 run_cmd.expectStdOutEqual("Hello!\n");
2021 test_step.dependOn(&run_cmd.step);
2122}
test/link/macho/entry/build.zig+7-6
......@@ -1,15 +1,16 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test");
87 test_step.dependOn(b.getInstallStep());
98
10 const exe = b.addExecutable("main", null);
11 exe.setTarget(.{ .os_tag = .macos });
12 exe.setBuildMode(mode);
9 const exe = b.addExecutable(.{
10 .name = "main",
11 .optimize = optimize,
12 .target = .{ .os_tag = .macos },
13 });
1314 exe.addCSourceFile("main.c", &.{});
1415 exe.linkLibC();
1516 exe.entry_symbol_name = "_non_main";
test/link/macho/headerpad/build.zig+11-11
......@@ -1,17 +1,15 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
4pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
86
97 const test_step = b.step("test", "Test");
108 test_step.dependOn(b.getInstallStep());
119
1210 {
1311 // Test -headerpad_max_install_names
14 const exe = simpleExe(b, mode);
12 const exe = simpleExe(b, optimize);
1513 exe.headerpad_max_install_names = true;
1614
1715 const check = exe.checkObject(.macho);
......@@ -36,7 +34,7 @@ pub fn build(b: *Builder) void {
3634
3735 {
3836 // Test -headerpad
39 const exe = simpleExe(b, mode);
37 const exe = simpleExe(b, optimize);
4038 exe.headerpad_size = 0x10000;
4139
4240 const check = exe.checkObject(.macho);
......@@ -52,7 +50,7 @@ pub fn build(b: *Builder) void {
5250
5351 {
5452 // Test both flags with -headerpad overriding -headerpad_max_install_names
55 const exe = simpleExe(b, mode);
53 const exe = simpleExe(b, optimize);
5654 exe.headerpad_max_install_names = true;
5755 exe.headerpad_size = 0x10000;
5856
......@@ -69,7 +67,7 @@ pub fn build(b: *Builder) void {
6967
7068 {
7169 // Test both flags with -headerpad_max_install_names overriding -headerpad
72 const exe = simpleExe(b, mode);
70 const exe = simpleExe(b, optimize);
7371 exe.headerpad_size = 0x1000;
7472 exe.headerpad_max_install_names = true;
7573
......@@ -94,9 +92,11 @@ pub fn build(b: *Builder) void {
9492 }
9593}
9694
97fn simpleExe(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
98 const exe = b.addExecutable("main", null);
99 exe.setBuildMode(mode);
95fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
96 const exe = b.addExecutable(.{
97 .name = "main",
98 .optimize = optimize,
99 });
100100 exe.addCSourceFile("main.c", &.{});
101101 exe.linkLibC();
102102 exe.linkFramework("CoreFoundation");
test/link/macho/linksection/build.zig+9-6
......@@ -1,15 +1,18 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55 const target = std.zig.CrossTarget{ .os_tag = .macos };
66
77 const test_step = b.step("test", "Test");
88 test_step.dependOn(b.getInstallStep());
99
10 const obj = b.addObject("test", "main.zig");
11 obj.setBuildMode(mode);
12 obj.setTarget(target);
10 const obj = b.addObject(.{
11 .name = "test",
12 .root_source_file = .{ .path = "main.zig" },
13 .optimize = optimize,
14 .target = target,
15 });
1316
1417 const check = obj.checkObject(.macho);
1518
......@@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void {
1922 check.checkInSymtab();
2023 check.checkNext("{*} (__TEXT,__TestFn) external _testFn");
2124
22 if (mode == .Debug) {
25 if (optimize == .Debug) {
2326 check.checkInSymtab();
2427 check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}");
2528 }
test/link/macho/needed_framework/build.zig+6-6
......@@ -1,18 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75
86 const test_step = b.step("test", "Test the program");
97 test_step.dependOn(b.getInstallStep());
108
119 // -dead_strip_dylibs
1210 // -needed_framework Cocoa
13 const exe = b.addExecutable("test", null);
11 const exe = b.addExecutable(.{
12 .name = "test",
13 .optimize = optimize,
14 });
1415 exe.addCSourceFile("main.c", &[0][]const u8{});
15 exe.setBuildMode(mode);
1616 exe.linkLibC();
1717 exe.linkFrameworkNeeded("Cocoa");
1818 exe.dead_strip_dylibs = true;
test/link/macho/needed_library/build.zig+13-10
......@@ -1,27 +1,30 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
108 test_step.dependOn(b.getInstallStep());
119
12 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
13 dylib.setTarget(target);
14 dylib.setBuildMode(mode);
10 const dylib = b.addSharedLibrary(.{
11 .name = "a",
12 .version = .{ .major = 1, .minor = 0 },
13 .optimize = optimize,
14 .target = target,
15 });
1516 dylib.addCSourceFile("a.c", &.{});
1617 dylib.linkLibC();
1718 dylib.install();
1819
1920 // -dead_strip_dylibs
2021 // -needed-la
21 const exe = b.addExecutable("test", null);
22 const exe = b.addExecutable(.{
23 .name = "test",
24 .optimize = optimize,
25 .target = target,
26 });
2227 exe.addCSourceFile("main.c", &[0][]const u8{});
23 exe.setBuildMode(mode);
24 exe.setTarget(target);
2528 exe.linkLibC();
2629 exe.linkSystemLibraryNeeded("a");
2730 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
test/link/macho/objc/build.zig+7-6
......@@ -1,21 +1,22 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test the program");
87
9 const exe = b.addExecutable("test", null);
8 const exe = b.addExecutable(.{
9 .name = "test",
10 .optimize = optimize,
11 });
1012 exe.addIncludePath(".");
1113 exe.addCSourceFile("Foo.m", &[0][]const u8{});
1214 exe.addCSourceFile("test.m", &[0][]const u8{});
13 exe.setBuildMode(mode);
1415 exe.linkLibC();
1516 // TODO when we figure out how to ship framework stubs for cross-compilation,
1617 // populate paths to the sysroot here.
1718 exe.linkFramework("Foundation");
1819
19 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
20 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
2021 test_step.dependOn(&run_cmd.step);
2122}
test/link/macho/objcpp/build.zig+6-5
......@@ -1,17 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test the program");
87
9 const exe = b.addExecutable("test", null);
8 const exe = b.addExecutable(.{
9 .name = "test",
10 .optimize = optimize,
11 });
1012 b.default_step.dependOn(&exe.step);
1113 exe.addIncludePath(".");
1214 exe.addCSourceFile("Foo.mm", &[0][]const u8{});
1315 exe.addCSourceFile("test.mm", &[0][]const u8{});
14 exe.setBuildMode(mode);
1516 exe.linkLibCpp();
1617 // TODO when we figure out how to ship framework stubs for cross-compilation,
1718 // populate paths to the sysroot here.
test/link/macho/pagezero/build.zig+12-9
......@@ -1,17 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test");
98 test_step.dependOn(b.getInstallStep());
109
1110 {
12 const exe = b.addExecutable("pagezero", null);
13 exe.setTarget(target);
14 exe.setBuildMode(mode);
11 const exe = b.addExecutable(.{
12 .name = "pagezero",
13 .optimize = optimize,
14 .target = target,
15 });
1516 exe.addCSourceFile("main.c", &.{});
1617 exe.linkLibC();
1718 exe.pagezero_size = 0x4000;
......@@ -29,9 +30,11 @@ pub fn build(b: *Builder) void {
2930 }
3031
3132 {
32 const exe = b.addExecutable("no_pagezero", null);
33 exe.setTarget(target);
34 exe.setBuildMode(mode);
33 const exe = b.addExecutable(.{
34 .name = "no_pagezero",
35 .optimize = optimize,
36 .target = target,
37 });
3538 exe.addCSourceFile("main.c", &.{});
3639 exe.linkLibC();
3740 exe.pagezero_size = 0;
test/link/macho/search_strategy/build.zig+28-19
......@@ -1,9 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test");
......@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {
119
1210 {
1311 // -search_dylibs_first
14 const exe = createScenario(b, mode, target);
12 const exe = createScenario(b, optimize, target);
1513 exe.search_strategy = .dylibs_first;
1614
1715 const check = exe.checkObject(.macho);
......@@ -26,40 +24,51 @@ pub fn build(b: *Builder) void {
2624
2725 {
2826 // -search_paths_first
29 const exe = createScenario(b, mode, target);
27 const exe = createScenario(b, optimize, target);
3028 exe.search_strategy = .paths_first;
3129
32 const run = std.build.EmulatableRunStep.create(b, "run", exe);
30 const run = std.Build.EmulatableRunStep.create(b, "run", exe);
3331 run.cwd = b.pathFromRoot(".");
3432 run.expectStdOutEqual("Hello world");
3533 test_step.dependOn(&run.step);
3634 }
3735}
3836
39fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
40 const static = b.addStaticLibrary("a", null);
41 static.setTarget(target);
42 static.setBuildMode(mode);
37fn createScenario(
38 b: *std.Build,
39 optimize: std.builtin.OptimizeMode,
40 target: std.zig.CrossTarget,
41) *std.Build.CompileStep {
42 const static = b.addStaticLibrary(.{
43 .name = "a",
44 .optimize = optimize,
45 .target = target,
46 });
4347 static.addCSourceFile("a.c", &.{});
4448 static.linkLibC();
45 static.override_dest_dir = std.build.InstallDir{
49 static.override_dest_dir = std.Build.InstallDir{
4650 .custom = "static",
4751 };
4852 static.install();
4953
50 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
51 dylib.setTarget(target);
52 dylib.setBuildMode(mode);
54 const dylib = b.addSharedLibrary(.{
55 .name = "a",
56 .version = .{ .major = 1, .minor = 0 },
57 .optimize = optimize,
58 .target = target,
59 });
5360 dylib.addCSourceFile("a.c", &.{});
5461 dylib.linkLibC();
55 dylib.override_dest_dir = std.build.InstallDir{
62 dylib.override_dest_dir = std.Build.InstallDir{
5663 .custom = "dynamic",
5764 };
5865 dylib.install();
5966
60 const exe = b.addExecutable("main", null);
61 exe.setTarget(target);
62 exe.setBuildMode(mode);
67 const exe = b.addExecutable(.{
68 .name = "main",
69 .optimize = optimize,
70 .target = target,
71 });
6372 exe.addCSourceFile("main.c", &.{});
6473 exe.linkSystemLibraryName("a");
6574 exe.linkLibC();
test/link/macho/stack_size/build.zig+7-6
......@@ -1,16 +1,17 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test");
98 test_step.dependOn(b.getInstallStep());
109
11 const exe = b.addExecutable("main", null);
12 exe.setTarget(target);
13 exe.setBuildMode(mode);
10 const exe = b.addExecutable(.{
11 .name = "main",
12 .optimize = optimize,
13 .target = target,
14 });
1415 exe.addCSourceFile("main.c", &.{});
1516 exe.linkLibC();
1617 exe.stack_size = 0x100000000;
test/link/macho/strict_validation/build.zig+8-7
......@@ -1,18 +1,19 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
4pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
86 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
97
108 const test_step = b.step("test", "Test");
119 test_step.dependOn(b.getInstallStep());
1210
13 const exe = b.addExecutable("main", "main.zig");
14 exe.setBuildMode(mode);
15 exe.setTarget(target);
11 const exe = b.addExecutable(.{
12 .name = "main",
13 .root_source_file = .{ .path = "main.zig" },
14 .optimize = optimize,
15 .target = target,
16 });
1617 exe.linkLibC();
1718
1819 const check_exe = exe.checkObject(.macho);
test/link/macho/tls/build.zig+13-9
......@@ -1,19 +1,23 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
9 lib.setBuildMode(mode);
10 lib.setTarget(target);
7 const lib = b.addSharedLibrary(.{
8 .name = "a",
9 .version = .{ .major = 1, .minor = 0 },
10 .optimize = optimize,
11 .target = target,
12 });
1113 lib.addCSourceFile("a.c", &.{});
1214 lib.linkLibC();
1315
14 const test_exe = b.addTest("main.zig");
15 test_exe.setBuildMode(mode);
16 test_exe.setTarget(target);
16 const test_exe = b.addTest(.{
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1721 test_exe.linkLibrary(lib);
1822 test_exe.linkLibC();
1923
test/link/macho/unwind_info/build.zig+18-14
......@@ -1,26 +1,24 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
4pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
86 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
97
108 const test_step = b.step("test", "Test the program");
119
12 testUnwindInfo(b, test_step, mode, target, false);
13 testUnwindInfo(b, test_step, mode, target, true);
10 testUnwindInfo(b, test_step, optimize, target, false);
11 testUnwindInfo(b, test_step, optimize, target, true);
1412}
1513
1614fn testUnwindInfo(
17 b: *Builder,
18 test_step: *std.build.Step,
19 mode: std.builtin.Mode,
15 b: *std.Build,
16 test_step: *std.Build.Step,
17 optimize: std.builtin.OptimizeMode,
2018 target: std.zig.CrossTarget,
2119 dead_strip: bool,
2220) void {
23 const exe = createScenario(b, mode, target);
21 const exe = createScenario(b, optimize, target);
2422 exe.link_gc_sections = dead_strip;
2523
2624 const check = exe.checkObject(.macho);
......@@ -52,8 +50,16 @@ fn testUnwindInfo(
5250 test_step.dependOn(&run_cmd.step);
5351}
5452
55fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
56 const exe = b.addExecutable("test", null);
53fn createScenario(
54 b: *std.Build,
55 optimize: std.builtin.OptimizeMode,
56 target: std.zig.CrossTarget,
57) *std.Build.CompileStep {
58 const exe = b.addExecutable(.{
59 .name = "test",
60 .optimize = optimize,
61 .target = target,
62 });
5763 b.default_step.dependOn(&exe.step);
5864 exe.addIncludePath(".");
5965 exe.addCSourceFiles(&[_][]const u8{
......@@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg
6167 "simple_string.cpp",
6268 "simple_string_owner.cpp",
6369 }, &[0][]const u8{});
64 exe.setBuildMode(mode);
65 exe.setTarget(target);
6670 exe.linkLibCpp();
6771 return exe;
6872}
test/link/macho/uuid/build.zig+17-12
......@@ -1,8 +1,6 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test");
75 test_step.dependOn(b.getInstallStep());
86
......@@ -27,23 +25,23 @@ pub fn build(b: *Builder) void {
2725}
2826
2927fn testUuid(
30 b: *Builder,
31 test_step: *std.build.Step,
32 mode: std.builtin.Mode,
28 b: *std.Build,
29 test_step: *std.Build.Step,
30 optimize: std.builtin.OptimizeMode,
3331 target: std.zig.CrossTarget,
3432 comptime exp: []const u8,
3533) void {
3634 // The calculated UUID value is independent of debug info and so it should
3735 // stay the same across builds.
3836 {
39 const dylib = simpleDylib(b, mode, target);
37 const dylib = simpleDylib(b, optimize, target);
4038 const check_dylib = dylib.checkObject(.macho);
4139 check_dylib.checkStart("cmd UUID");
4240 check_dylib.checkNext("uuid " ++ exp);
4341 test_step.dependOn(&check_dylib.step);
4442 }
4543 {
46 const dylib = simpleDylib(b, mode, target);
44 const dylib = simpleDylib(b, optimize, target);
4745 dylib.strip = true;
4846 const check_dylib = dylib.checkObject(.macho);
4947 check_dylib.checkStart("cmd UUID");
......@@ -52,10 +50,17 @@ fn testUuid(
5250 }
5351}
5452
55fn simpleDylib(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
56 const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0));
57 dylib.setTarget(target);
58 dylib.setBuildMode(mode);
53fn simpleDylib(
54 b: *std.Build,
55 optimize: std.builtin.OptimizeMode,
56 target: std.zig.CrossTarget,
57) *std.Build.CompileStep {
58 const dylib = b.addSharedLibrary(.{
59 .name = "test",
60 .version = .{ .major = 1, .minor = 0 },
61 .optimize = optimize,
62 .target = target,
63 });
5964 dylib.addCSourceFile("test.c", &.{});
6065 dylib.linkLibC();
6166 return dylib;
test/link/macho/weak_framework/build.zig+6-6
......@@ -1,16 +1,16 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75
86 const test_step = b.step("test", "Test the program");
97 test_step.dependOn(b.getInstallStep());
108
11 const exe = b.addExecutable("test", null);
9 const exe = b.addExecutable(.{
10 .name = "test",
11 .optimize = optimize,
12 });
1213 exe.addCSourceFile("main.c", &[0][]const u8{});
13 exe.setBuildMode(mode);
1414 exe.linkLibC();
1515 exe.linkFrameworkWeak("Cocoa");
1616
test/link/macho/weak_library/build.zig+13-10
......@@ -1,25 +1,28 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
108 test_step.dependOn(b.getInstallStep());
119
12 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
13 dylib.setTarget(target);
14 dylib.setBuildMode(mode);
10 const dylib = b.addSharedLibrary(.{
11 .name = "a",
12 .version = .{ .major = 1, .minor = 0, .patch = 0 },
13 .target = target,
14 .optimize = optimize,
15 });
1516 dylib.addCSourceFile("a.c", &.{});
1617 dylib.linkLibC();
1718 dylib.install();
1819
19 const exe = b.addExecutable("test", null);
20 const exe = b.addExecutable(.{
21 .name = "test",
22 .target = target,
23 .optimize = optimize,
24 });
2025 exe.addCSourceFile("main.c", &[0][]const u8{});
21 exe.setTarget(target);
22 exe.setBuildMode(mode);
2326 exe.linkLibC();
2427 exe.linkSystemLibraryWeak("a");
2528 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
test/link/static_lib_as_system_lib/build.zig+13-7
......@@ -1,17 +1,23 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
66
7 const lib_a = b.addStaticLibrary("a", null);
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
812 lib_a.addCSourceFile("a.c", &[_][]const u8{});
9 lib_a.setBuildMode(mode);
1013 lib_a.addIncludePath(".");
1114 lib_a.install();
1215
13 const test_exe = b.addTest("main.zig");
14 test_exe.setBuildMode(mode);
16 const test_exe = b.addTest(.{
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1521 test_exe.linkSystemLibrary("a"); // force linking liba.a as -la
1622 test_exe.addSystemIncludePath(".");
1723 const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable;
test/link/wasm/archive/build.zig+7-7
......@@ -1,17 +1,17 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
107 // The code in question will pull-in compiler-rt,
118 // and therefore link with its archive file.
12 const lib = b.addSharedLibrary("main", "main.zig", .unversioned);
13 lib.setBuildMode(mode);
14 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 const lib = b.addSharedLibrary(.{
10 .name = "main",
11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = b.standardOptimizeOption(.{}),
13 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
14 });
1515 lib.use_llvm = false;
1616 lib.use_lld = false;
1717 lib.strip = false;
test/link/wasm/basic-features/build.zig+12-8
......@@ -1,14 +1,18 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5
3pub fn build(b: *std.Build) void {
64 // Library with explicitly set cpu features
7 const lib = b.addSharedLibrary("lib", "main.zig", .unversioned);
8 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp };
10 lib.target.cpu_features_add.addFeature(0); // index 0 == atomics (see std.Target.wasm.Features)
11 lib.setBuildMode(mode);
5 const lib = b.addSharedLibrary(.{
6 .name = "lib",
7 .root_source_file = .{ .path = "main.zig" },
8 .optimize = b.standardOptimizeOption(.{}),
9 .target = .{
10 .cpu_arch = .wasm32,
11 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
12 .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}),
13 .os_tag = .freestanding,
14 },
15 });
1216 lib.use_llvm = false;
1317 lib.use_lld = false;
1418
test/link/wasm/bss/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/link/wasm/export-data/build.zig+7-5
......@@ -1,13 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
54 const test_step = b.step("test", "Test");
65 test_step.dependOn(b.getInstallStep());
76
8 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
9 lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions
10 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .optimize = .ReleaseSafe, // to make the output deterministic in address positions
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 });
1113 lib.use_lld = false;
1214 lib.export_symbol_names = &.{ "foo", "bar" };
1315 lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse
test/link/wasm/export/build.zig+21-12
......@@ -1,24 +1,33 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5
6 const no_export = b.addSharedLibrary("no-export", "main.zig", .unversioned);
7 no_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
8 no_export.setBuildMode(mode);
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const no_export = b.addSharedLibrary(.{
7 .name = "no-export",
8 .root_source_file = .{ .path = "main.zig" },
9 .optimize = optimize,
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 });
912 no_export.use_llvm = false;
1013 no_export.use_lld = false;
1114
12 const dynamic_export = b.addSharedLibrary("dynamic", "main.zig", .unversioned);
13 dynamic_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
14 dynamic_export.setBuildMode(mode);
15 const dynamic_export = b.addSharedLibrary(.{
16 .name = "dynamic",
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
20 });
1521 dynamic_export.rdynamic = true;
1622 dynamic_export.use_llvm = false;
1723 dynamic_export.use_lld = false;
1824
19 const force_export = b.addSharedLibrary("force", "main.zig", .unversioned);
20 force_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
21 force_export.setBuildMode(mode);
25 const force_export = b.addSharedLibrary(.{
26 .name = "force",
27 .root_source_file = .{ .path = "main.zig" },
28 .optimize = optimize,
29 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
30 });
2231 force_export.export_symbol_names = &.{"foo"};
2332 force_export.use_llvm = false;
2433 force_export.use_lld = false;
test/link/wasm/extern-mangle/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.import_symbols = true; // import `a` and `b`
1414 lib.rdynamic = true; // export `foo`
1515 lib.install();
test/link/wasm/extern/build.zig+7-5
......@@ -1,10 +1,12 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5 const exe = b.addExecutable("extern", "main.zig");
6 exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi });
7 exe.setBuildMode(mode);
3pub fn build(b: *std.Build) void {
4 const exe = b.addExecutable(.{
5 .name = "extern",
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
8 .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },
9 });
810 exe.addCSourceFile("foo.c", &.{});
911 exe.use_llvm = false;
1012 exe.use_lld = false;
test/link/wasm/function-table/build.zig+20-12
......@@ -1,29 +1,37 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test");
87 test_step.dependOn(b.getInstallStep());
98
10 const import_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 import_table.setBuildMode(mode);
12 import_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 const import_table = b.addSharedLibrary(.{
10 .name = "lib",
11 .root_source_file = .{ .path = "lib.zig" },
12 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
13 .optimize = optimize,
14 });
1315 import_table.use_llvm = false;
1416 import_table.use_lld = false;
1517 import_table.import_table = true;
1618
17 const export_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
18 export_table.setBuildMode(mode);
19 export_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
19 const export_table = b.addSharedLibrary(.{
20 .name = "lib",
21 .root_source_file = .{ .path = "lib.zig" },
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
23 .optimize = optimize,
24 });
2025 export_table.use_llvm = false;
2126 export_table.use_lld = false;
2227 export_table.export_table = true;
2328
24 const regular_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
25 regular_table.setBuildMode(mode);
26 regular_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
29 const regular_table = b.addSharedLibrary(.{
30 .name = "lib",
31 .root_source_file = .{ .path = "lib.zig" },
32 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
33 .optimize = optimize,
34 });
2735 regular_table.use_llvm = false;
2836 regular_table.use_lld = false;
2937
test/link/wasm/infer-features/build.zig+21-10
......@@ -1,21 +1,32 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
66 // Wasm Object file which we will use to infer the features from
7 const c_obj = b.addObject("c_obj", null);
8 c_obj.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 c_obj.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge };
7 const c_obj = b.addObject(.{
8 .name = "c_obj",
9 .optimize = optimize,
10 .target = .{
11 .cpu_arch = .wasm32,
12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
13 .os_tag = .freestanding,
14 },
15 });
1016 c_obj.addCSourceFile("foo.c", &.{});
11 c_obj.setBuildMode(mode);
1217
1318 // Wasm library that doesn't have any features specified. This will
1419 // infer its featureset from other linked object files.
15 const lib = b.addSharedLibrary("lib", "main.zig", .unversioned);
16 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
17 lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp };
18 lib.setBuildMode(mode);
20 const lib = b.addSharedLibrary(.{
21 .name = "lib",
22 .root_source_file = .{ .path = "main.zig" },
23 .optimize = optimize,
24 .target = .{
25 .cpu_arch = .wasm32,
26 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
27 .os_tag = .freestanding,
28 },
29 });
1930 lib.use_llvm = false;
2031 lib.use_lld = false;
2132 lib.addObject(c_obj);
test/link/wasm/producers/build.zig+7-7
......@@ -1,16 +1,16 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
73
4pub fn build(b: *std.Build) void {
85 const test_step = b.step("test", "Test");
96 test_step.dependOn(b.getInstallStep());
107
11 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
12 lib.setBuildMode(mode);
13 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
8 const lib = b.addSharedLibrary(.{
9 .name = "lib",
10 .root_source_file = .{ .path = "lib.zig" },
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 .optimize = b.standardOptimizeOption(.{}),
13 });
1414 lib.use_llvm = false;
1515 lib.use_lld = false;
1616 lib.strip = false;
test/link/wasm/segments/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/link/wasm/stack_pointer/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/link/wasm/type/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/src/compare_output.zig+25-11
......@@ -1,19 +1,18 @@
11// This is the implementation of the test harness.
22// For the actual test cases, see test/compare_output.zig.
33const std = @import("std");
4const build = std.build;
54const ArrayList = std.ArrayList;
65const fmt = std.fmt;
76const mem = std.mem;
87const fs = std.fs;
9const Mode = std.builtin.Mode;
8const OptimizeMode = std.builtin.OptimizeMode;
109
1110pub const CompareOutputContext = struct {
12 b: *build.Builder,
13 step: *build.Step,
11 b: *std.Build,
12 step: *std.Build.Step,
1413 test_index: usize,
1514 test_filter: ?[]const u8,
16 modes: []const Mode,
15 optimize_modes: []const OptimizeMode,
1716
1817 const Special = enum {
1918 None,
......@@ -102,7 +101,11 @@ pub const CompareOutputContext = struct {
102101 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
103102 }
104103
105 const exe = b.addExecutable("test", null);
104 const exe = b.addExecutable(.{
105 .name = "test",
106 .target = .{},
107 .optimize = .Debug,
108 });
106109 exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?);
107110
108111 const run = exe.run();
......@@ -113,19 +116,23 @@ pub const CompareOutputContext = struct {
113116 self.step.dependOn(&run.step);
114117 },
115118 Special.None => {
116 for (self.modes) |mode| {
119 for (self.optimize_modes) |optimize| {
117120 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
118121 "compare-output",
119122 case.name,
120 @tagName(mode),
123 @tagName(optimize),
121124 }) catch unreachable;
122125 if (self.test_filter) |filter| {
123126 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
124127 }
125128
126129 const basename = case.sources.items[0].filename;
127 const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?);
128 exe.setBuildMode(mode);
130 const exe = b.addExecutable(.{
131 .name = "test",
132 .root_source_file = write_src.getFileSource(basename).?,
133 .optimize = optimize,
134 .target = .{},
135 });
129136 if (case.link_libc) {
130137 exe.linkSystemLibrary("c");
131138 }
......@@ -139,13 +146,20 @@ pub const CompareOutputContext = struct {
139146 }
140147 },
141148 Special.RuntimeSafety => {
149 // TODO iterate over self.optimize_modes and test this in both
150 // debug and release safe mode
142151 const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable;
143152 if (self.test_filter) |filter| {
144153 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
145154 }
146155
147156 const basename = case.sources.items[0].filename;
148 const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?);
157 const exe = b.addExecutable(.{
158 .name = "test",
159 .root_source_file = write_src.getFileSource(basename).?,
160 .target = .{},
161 .optimize = .Debug,
162 });
149163 if (case.link_libc) {
150164 exe.linkSystemLibrary("c");
151165 }
test/src/run_translated_c.zig+8-6
......@@ -1,15 +1,14 @@
11// This is the implementation of the test harness for running translated
22// C code. For the actual test cases, see test/run_translated_c.zig.
33const std = @import("std");
4const build = std.build;
54const ArrayList = std.ArrayList;
65const fmt = std.fmt;
76const mem = std.mem;
87const fs = std.fs;
98
109pub const RunTranslatedCContext = struct {
11 b: *build.Builder,
12 step: *build.Step,
10 b: *std.Build,
11 step: *std.Build.Step,
1312 test_index: usize,
1413 test_filter: ?[]const u8,
1514 target: std.zig.CrossTarget,
......@@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct {
8584 for (case.sources.items) |src_file| {
8685 write_src.add(src_file.filename, src_file.source);
8786 }
88 const translate_c = b.addTranslateC(write_src.getFileSource(case.sources.items[0].filename).?);
87 const translate_c = b.addTranslateC(.{
88 .source_file = write_src.getFileSource(case.sources.items[0].filename).?,
89 .target = .{},
90 .optimize = .Debug,
91 });
8992
9093 translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name});
91 const exe = translate_c.addExecutable();
92 exe.setTarget(self.target);
94 const exe = translate_c.addExecutable(.{});
9395 exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});
9496 exe.linkLibC();
9597 const run = exe.run();
test/src/translate_c.zig+7-5
......@@ -1,7 +1,6 @@
11// This is the implementation of the test harness.
22// For the actual test cases, see test/translate_c.zig.
33const std = @import("std");
4const build = std.build;
54const ArrayList = std.ArrayList;
65const fmt = std.fmt;
76const mem = std.mem;
......@@ -9,8 +8,8 @@ const fs = std.fs;
98const CrossTarget = std.zig.CrossTarget;
109
1110pub const TranslateCContext = struct {
12 b: *build.Builder,
13 step: *build.Step,
11 b: *std.Build,
12 step: *std.Build.Step,
1413 test_index: usize,
1514 test_filter: ?[]const u8,
1615
......@@ -108,10 +107,13 @@ pub const TranslateCContext = struct {
108107 write_src.add(src_file.filename, src_file.source);
109108 }
110109
111 const translate_c = b.addTranslateC(write_src.getFileSource(case.sources.items[0].filename).?);
110 const translate_c = b.addTranslateC(.{
111 .source_file = write_src.getFileSource(case.sources.items[0].filename).?,
112 .target = case.target,
113 .optimize = .Debug,
114 });
112115
113116 translate_c.step.name = annotated_case_name;
114 translate_c.setTarget(case.target);
115117
116118 const check_file = translate_c.addCheckFile(case.expected_lines.items);
117119
test/standalone/brace_expansion/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68
79 const test_step = b.step("test", "Test it");
810 test_step.dependOn(&main.step);
test/standalone/c_compiler/build.zig+13-10
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
1817 const test_step = b.step("test", "Test the program");
1918
20 const exe_c = b.addExecutable("test_c", null);
19 const exe_c = b.addExecutable(.{
20 .name = "test_c",
21 .optimize = optimize,
22 .target = target,
23 });
2124 b.default_step.dependOn(&exe_c.step);
2225 exe_c.addCSourceFile("test.c", &[0][]const u8{});
23 exe_c.setBuildMode(mode);
24 exe_c.setTarget(target);
2526 exe_c.linkLibC();
2627
27 const exe_cpp = b.addExecutable("test_cpp", null);
28 const exe_cpp = b.addExecutable(.{
29 .name = "test_cpp",
30 .optimize = optimize,
31 .target = target,
32 });
2833 b.default_step.dependOn(&exe_cpp.step);
2934 exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{});
30 exe_cpp.setBuildMode(mode);
31 exe_cpp.setTarget(target);
3235 exe_cpp.linkLibCpp();
3336
3437 switch (target.getOsTag()) {
test/standalone/emit_asm_and_bin/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
79 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
810
test/standalone/empty_env/build.zig+7-4
......@@ -1,8 +1,11 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addExecutable("main", "main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addExecutable(.{
5 .name = "main",
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
8 });
69
710 const run = main.run();
811 run.clearEnvironment();
test/standalone/global_linkage/build.zig+19-9
......@@ -1,16 +1,26 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const obj1 = b.addStaticLibrary("obj1", "obj1.zig");
7 obj1.setBuildMode(mode);
6 const obj1 = b.addStaticLibrary(.{
7 .name = "obj1",
8 .root_source_file = .{ .path = "obj1.zig" },
9 .optimize = optimize,
10 .target = .{},
11 });
812
9 const obj2 = b.addStaticLibrary("obj2", "obj2.zig");
10 obj2.setBuildMode(mode);
13 const obj2 = b.addStaticLibrary(.{
14 .name = "obj2",
15 .root_source_file = .{ .path = "obj2.zig" },
16 .optimize = optimize,
17 .target = .{},
18 });
1119
12 const main = b.addTest("main.zig");
13 main.setBuildMode(mode);
20 const main = b.addTest(.{
21 .root_source_file = .{ .path = "main.zig" },
22 .optimize = optimize,
23 });
1424 main.linkLibrary(obj1);
1525 main.linkLibrary(obj2);
1626
test/standalone/install_raw_hex/build.zig+9-6
......@@ -1,8 +1,8 @@
11const builtin = @import("builtin");
22const std = @import("std");
3const CheckFileStep = std.build.CheckFileStep;
3const CheckFileStep = std.Build.CheckFileStep;
44
5pub fn build(b: *std.build.Builder) void {
5pub fn build(b: *std.Build) void {
66 const target = .{
77 .cpu_arch = .thumb,
88 .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
......@@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void {
1010 .abi = .gnueabihf,
1111 };
1212
13 const mode = b.standardReleaseOptions();
13 const optimize = b.standardOptimizeOption(.{});
1414
15 const elf = b.addExecutable("zig-nrf52-blink.elf", "main.zig");
16 elf.setTarget(target);
17 elf.setBuildMode(mode);
15 const elf = b.addExecutable(.{
16 .name = "zig-nrf52-blink.elf",
17 .root_source_file = .{ .path = "main.zig" },
18 .target = target,
19 .optimize = optimize,
20 });
1821
1922 const test_step = b.step("test", "Test the program");
2023 b.default_step.dependOn(test_step);
test/standalone/issue_11595/build.zig+9-7
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("zigtest", "main.zig");
19 exe.setBuildMode(mode);
17 const exe = b.addExecutable(.{
18 .name = "zigtest",
19 .root_source_file = .{ .path = "main.zig" },
20 .target = target,
21 .optimize = optimize,
22 });
2023 exe.install();
2124
2225 const c_sources = [_][]const u8{
......@@ -39,7 +42,6 @@ pub fn build(b: *Builder) void {
3942 exe.defineCMacro("QUX", "\"Q\" \"UX\"");
4043 exe.defineCMacro("QUUX", "\"QU\\\"UX\"");
4144
42 exe.setTarget(target);
4345 b.default_step.dependOn(&exe.step);
4446
4547 const test_step = b.step("test", "Test the program");
test/standalone/issue_12588/build.zig+8-6
......@@ -1,13 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target = b.standardTargetOptions(.{});
76
8 const obj = b.addObject("main", "main.zig");
9 obj.setBuildMode(mode);
10 obj.setTarget(target);
7 const obj = b.addObject(.{
8 .name = "main",
9 .root_source_file = .{ .path = "main.zig" },
10 .optimize = optimize,
11 .target = target,
12 });
1113 obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") };
1214 obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") };
1315 obj.emit_bin = .no_emit;
test/standalone/issue_12706/build.zig+9-7
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("main", "main.zig");
19 exe.setBuildMode(mode);
17 const exe = b.addExecutable(.{
18 .name = "main",
19 .root_source_file = .{ .path = "main.zig" },
20 .optimize = optimize,
21 .target = target,
22 });
2023 exe.install();
2124
2225 const c_sources = [_][]const u8{
......@@ -26,7 +29,6 @@ pub fn build(b: *Builder) void {
2629 exe.addCSourceFiles(&c_sources, &.{});
2730 exe.linkLibC();
2831
29 exe.setTarget(target);
3032 b.default_step.dependOn(&exe.step);
3133
3234 const test_step = b.step("test", "Test the program");
test/standalone/issue_13030/build.zig+8-7
......@@ -1,16 +1,17 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
5pub fn build(b: *std.Build) void {
6 const optimize = b.standardOptimizeOption(.{});
87 const target = b.standardTargetOptions(.{});
98
10 const obj = b.addObject("main", "main.zig");
11 obj.setBuildMode(mode);
12
13 obj.setTarget(target);
9 const obj = b.addObject(.{
10 .name = "main",
11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = optimize,
13 .target = target,
14 });
1415 b.default_step.dependOn(&obj.step);
1516
1617 const test_step = b.step("test", "Test the program");
test/standalone/issue_339/build.zig+8-3
......@@ -1,7 +1,12 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const obj = b.addObject("test", "test.zig");
3pub fn build(b: *std.Build) void {
4 const obj = b.addObject(.{
5 .name = "test",
6 .root_source_file = .{ .path = "test.zig" },
7 .target = b.standardTargetOptions(.{}),
8 .optimize = b.standardOptimizeOption(.{}),
9 });
510
611 const test_step = b.step("test", "Test the program");
712 test_step.dependOn(&obj.step);
test/standalone/issue_5825/build.zig+14-9
......@@ -1,22 +1,27 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
44 const target = .{
55 .cpu_arch = .x86_64,
66 .os_tag = .windows,
77 .abi = .msvc,
88 };
9 const mode = b.standardReleaseOptions();
10 const obj = b.addObject("issue_5825", "main.zig");
11 obj.setTarget(target);
12 obj.setBuildMode(mode);
9 const optimize = b.standardOptimizeOption(.{});
10 const obj = b.addObject(.{
11 .name = "issue_5825",
12 .root_source_file = .{ .path = "main.zig" },
13 .optimize = optimize,
14 .target = target,
15 });
1316
14 const exe = b.addExecutable("issue_5825", null);
17 const exe = b.addExecutable(.{
18 .name = "issue_5825",
19 .optimize = optimize,
20 .target = target,
21 });
1522 exe.subsystem = .Console;
1623 exe.linkSystemLibrary("kernel32");
1724 exe.linkSystemLibrary("ntdll");
18 exe.setTarget(target);
19 exe.setBuildMode(mode);
2025 exe.addObject(obj);
2126
2227 const test_step = b.step("test", "Test the program");
test/standalone/issue_7030/build.zig+9-6
......@@ -1,10 +1,13 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const exe = b.addExecutable("issue_7030", "main.zig");
5 exe.setTarget(.{
6 .cpu_arch = .wasm32,
7 .os_tag = .freestanding,
3pub fn build(b: *std.Build) void {
4 const exe = b.addExecutable(.{
5 .name = "issue_7030",
6 .root_source_file = .{ .path = "main.zig" },
7 .target = .{
8 .cpu_arch = .wasm32,
9 .os_tag = .freestanding,
10 },
811 });
912 exe.install();
1013 b.default_step.dependOn(&exe.step);
test/standalone/issue_794/build.zig+5-3
......@@ -1,7 +1,9 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const test_artifact = b.addTest("main.zig");
3pub fn build(b: *std.Build) void {
4 const test_artifact = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 });
57 test_artifact.addIncludePath("a_directory");
68
79 b.default_step.dependOn(&test_artifact.step);
test/standalone/issue_8550/build.zig+8-5
......@@ -1,6 +1,6 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) !void {
3pub fn build(b: *std.Build) !void {
44 const target = std.zig.CrossTarget{
55 .os_tag = .freestanding,
66 .cpu_arch = .arm,
......@@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void {
88 .explicit = &std.Target.arm.cpu.arm1176jz_s,
99 },
1010 };
11 const mode = b.standardReleaseOptions();
12 const kernel = b.addExecutable("kernel", "./main.zig");
11 const optimize = b.standardOptimizeOption(.{});
12 const kernel = b.addExecutable(.{
13 .name = "kernel",
14 .root_source_file = .{ .path = "./main.zig" },
15 .optimize = optimize,
16 .target = target,
17 });
1318 kernel.addObjectFile("./boot.S");
1419 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });
15 kernel.setBuildMode(mode);
16 kernel.setTarget(target);
1720 kernel.install();
1821
1922 const test_step = b.step("test", "Test it");
test/standalone/issue_9812/build.zig+6-4
......@@ -1,9 +1,11 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) !void {
4 const mode = b.standardReleaseOptions();
5 const zip_add = b.addTest("main.zig");
6 zip_add.setBuildMode(mode);
3pub fn build(b: *std.Build) !void {
4 const optimize = b.standardOptimizeOption(.{});
5 const zip_add = b.addTest(.{
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = optimize,
8 });
79 zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{
810 "-std=c99",
911 "-fno-sanitize=undefined",
test/standalone/load_dynamic_library/build.zig+17-7
......@@ -1,13 +1,23 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const opts = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
56
6 const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0));
7 lib.setBuildMode(opts);
7 const lib = b.addSharedLibrary(.{
8 .name = "add",
9 .root_source_file = .{ .path = "add.zig" },
10 .version = .{ .major = 1, .minor = 0 },
11 .optimize = optimize,
12 .target = target,
13 });
814
9 const main = b.addExecutable("main", "main.zig");
10 main.setBuildMode(opts);
15 const main = b.addExecutable(.{
16 .name = "main",
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1121
1222 const run = main.run();
1323 run.addArtifactArg(lib);
test/standalone/main_pkg_path/build.zig+5-3
......@@ -1,7 +1,9 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const test_exe = b.addTest("a/test.zig");
3pub fn build(b: *std.Build) void {
4 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "a/test.zig" },
6 });
57 test_exe.setMainPkgPath(".");
68
79 const test_step = b.step("test", "Test the program");
test/standalone/mix_c_files/build.zig+9-7
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("test", "main.zig");
17 const exe = b.addExecutable(.{
18 .name = "test",
19 .root_source_file = .{ .path = "main.zig" },
20 .optimize = optimize,
21 .target = target,
22 });
1923 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});
20 exe.setBuildMode(mode);
2124 exe.linkLibC();
22 exe.setTarget(target);
2325 b.default_step.dependOn(&exe.step);
2426
2527 const test_step = b.step("test", "Test the program");
test/standalone/mix_o_files/build.zig+14-4
......@@ -1,9 +1,19 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const obj = b.addObject("base64", "base64.zig");
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const exe = b.addExecutable("test", null);
6 const obj = b.addObject(.{
7 .name = "base64",
8 .root_source_file = .{ .path = "base64.zig" },
9 .optimize = optimize,
10 .target = .{},
11 });
12
13 const exe = b.addExecutable(.{
14 .name = "test",
15 .optimize = optimize,
16 });
717 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
818 exe.addObject(obj);
919 exe.linkSystemLibrary("c");
test/standalone/options/build.zig+7-5
......@@ -1,12 +1,14 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
3pub fn build(b: *std.Build) void {
44 const target = b.standardTargetOptions(.{});
5 const mode = b.standardReleaseOptions();
5 const optimize = b.standardOptimizeOption(.{});
66
7 const main = b.addTest("src/main.zig");
8 main.setTarget(target);
9 main.setBuildMode(mode);
7 const main = b.addTest(.{
8 .root_source_file = .{ .path = "src/main.zig" },
9 .target = target,
10 .optimize = optimize,
11 });
1012
1113 const options = b.addOptions();
1214 main.addOptions("build_options", options);
test/standalone/pie/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68 main.pie = true;
79
810 const test_step = b.step("test", "Test the program");
test/standalone/pkg_import/build.zig+9-8
......@@ -1,13 +1,14 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const exe = b.addExecutable("test", "test.zig");
5 exe.addPackagePath("my_pkg", "pkg.zig");
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
7 // This is duplicated to test that you are allowed to call
8 // b.standardReleaseOptions() twice.
9 exe.setBuildMode(b.standardReleaseOptions());
10 exe.setBuildMode(b.standardReleaseOptions());
6 const exe = b.addExecutable(.{
7 .name = "test",
8 .root_source_file = .{ .path = "test.zig" },
9 .optimize = optimize,
10 });
11 exe.addPackagePath("my_pkg", "pkg.zig");
1112
1213 const run = exe.run();
1314
test/standalone/shared_library/build.zig+15-6
......@@ -1,12 +1,21 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
45 const target = b.standardTargetOptions(.{});
5 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
6 lib.setTarget(target);
6 const lib = b.addSharedLibrary(.{
7 .name = "mathtest",
8 .root_source_file = .{ .path = "mathtest.zig" },
9 .version = .{ .major = 1, .minor = 0 },
10 .target = target,
11 .optimize = optimize,
12 });
713
8 const exe = b.addExecutable("test", null);
9 exe.setTarget(target);
14 const exe = b.addExecutable(.{
15 .name = "test",
16 .target = target,
17 .optimize = optimize,
18 });
1019 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1120 exe.linkLibrary(lib);
1221 exe.linkSystemLibrary("c");
test/standalone/static_c_lib/build.zig+12-7
......@@ -1,15 +1,20 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const foo = b.addStaticLibrary("foo", null);
6 const foo = b.addStaticLibrary(.{
7 .name = "foo",
8 .optimize = optimize,
9 .target = .{},
10 });
711 foo.addCSourceFile("foo.c", &[_][]const u8{});
8 foo.setBuildMode(mode);
912 foo.addIncludePath(".");
1013
11 const test_exe = b.addTest("foo.zig");
12 test_exe.setBuildMode(mode);
14 const test_exe = b.addTest(.{
15 .root_source_file = .{ .path = "foo.zig" },
16 .optimize = optimize,
17 });
1318 test_exe.linkLibrary(foo);
1419 test_exe.addIncludePath(".");
1520
test/standalone/test_runner_path/build.zig+6-3
......@@ -1,7 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const test_exe = b.addTestExe("test", "test.zig");
3pub fn build(b: *std.Build) void {
4 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "test.zig" },
6 .kind = .test_exe,
7 });
58 test_exe.test_runner = "test_runner.zig";
69
710 const test_run = test_exe.run();
test/standalone/use_alias/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68 main.addIncludePath(".");
79
810 const test_step = b.step("test", "Test it");
test/standalone/windows_spawn/build.zig+14-7
......@@ -1,13 +1,20 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const hello = b.addExecutable("hello", "hello.zig");
7 hello.setBuildMode(mode);
6 const hello = b.addExecutable(.{
7 .name = "hello",
8 .root_source_file = .{ .path = "hello.zig" },
9 .optimize = optimize,
10 });
11
12 const main = b.addExecutable(.{
13 .name = "main",
14 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,
16 });
817
9 const main = b.addExecutable("main", "main.zig");
10 main.setBuildMode(mode);
1118 const run = main.run();
1219 run.addArtifactArg(hello);
1320
test/tests.zig+117-100
......@@ -1,17 +1,17 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const debug = std.debug;
4const build = std.build;
54const CrossTarget = std.zig.CrossTarget;
65const io = std.io;
76const fs = std.fs;
87const mem = std.mem;
98const fmt = std.fmt;
109const ArrayList = std.ArrayList;
11const Mode = std.builtin.Mode;
12const LibExeObjStep = build.LibExeObjStep;
10const OptimizeMode = std.builtin.OptimizeMode;
11const CompileStep = std.Build.CompileStep;
1312const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
13const ExecError = std.Build.ExecError;
14const Step = std.Build.Step;
1515
1616// Cases
1717const compare_output = @import("compare_output.zig");
......@@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput
3030
3131const TestTarget = struct {
3232 target: CrossTarget = @as(CrossTarget, .{}),
33 mode: std.builtin.Mode = .Debug,
33 optimize_mode: std.builtin.OptimizeMode = .Debug,
3434 link_libc: bool = false,
3535 single_threaded: bool = false,
3636 disable_native: bool = false,
......@@ -423,38 +423,38 @@ const test_targets = blk: {
423423
424424 // Do the release tests last because they take a long time
425425 .{
426 .mode = .ReleaseFast,
426 .optimize_mode = .ReleaseFast,
427427 },
428428 .{
429429 .link_libc = true,
430 .mode = .ReleaseFast,
430 .optimize_mode = .ReleaseFast,
431431 },
432432 .{
433 .mode = .ReleaseFast,
433 .optimize_mode = .ReleaseFast,
434434 .single_threaded = true,
435435 },
436436
437437 .{
438 .mode = .ReleaseSafe,
438 .optimize_mode = .ReleaseSafe,
439439 },
440440 .{
441441 .link_libc = true,
442 .mode = .ReleaseSafe,
442 .optimize_mode = .ReleaseSafe,
443443 },
444444 .{
445 .mode = .ReleaseSafe,
445 .optimize_mode = .ReleaseSafe,
446446 .single_threaded = true,
447447 },
448448
449449 .{
450 .mode = .ReleaseSmall,
450 .optimize_mode = .ReleaseSmall,
451451 },
452452 .{
453453 .link_libc = true,
454 .mode = .ReleaseSmall,
454 .optimize_mode = .ReleaseSmall,
455455 },
456456 .{
457 .mode = .ReleaseSmall,
457 .optimize_mode = .ReleaseSmall,
458458 .single_threaded = true,
459459 },
460460 };
......@@ -462,14 +462,14 @@ const test_targets = blk: {
462462
463463const max_stdout_size = 1 * 1024 * 1024; // 1 MB
464464
465pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
465pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
466466 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
467467 cases.* = CompareOutputContext{
468468 .b = b,
469469 .step = b.step("test-compare-output", "Run the compare output tests"),
470470 .test_index = 0,
471471 .test_filter = test_filter,
472 .modes = modes,
472 .optimize_modes = optimize_modes,
473473 };
474474
475475 compare_output.addCases(cases);
......@@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes:
477477 return cases.step;
478478}
479479
480pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
480pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
481481 const cases = b.allocator.create(StackTracesContext) catch unreachable;
482482 cases.* = StackTracesContext{
483483 .b = b,
484484 .step = b.step("test-stack-traces", "Run the stack trace tests"),
485485 .test_index = 0,
486486 .test_filter = test_filter,
487 .modes = modes,
487 .optimize_modes = optimize_modes,
488488 };
489489
490490 stack_traces.addCases(cases);
......@@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []
493493}
494494
495495pub fn addStandaloneTests(
496 b: *build.Builder,
496 b: *std.Build,
497497 test_filter: ?[]const u8,
498 modes: []const Mode,
498 optimize_modes: []const OptimizeMode,
499499 skip_non_native: bool,
500500 enable_macos_sdk: bool,
501501 target: std.zig.CrossTarget,
......@@ -506,14 +506,14 @@ pub fn addStandaloneTests(
506506 enable_wasmtime: bool,
507507 enable_wine: bool,
508508 enable_symlinks_windows: bool,
509) *build.Step {
509) *Step {
510510 const cases = b.allocator.create(StandaloneContext) catch unreachable;
511511 cases.* = StandaloneContext{
512512 .b = b,
513513 .step = b.step("test-standalone", "Run the standalone tests"),
514514 .test_index = 0,
515515 .test_filter = test_filter,
516 .modes = modes,
516 .optimize_modes = optimize_modes,
517517 .skip_non_native = skip_non_native,
518518 .enable_macos_sdk = enable_macos_sdk,
519519 .target = target,
......@@ -532,20 +532,20 @@ pub fn addStandaloneTests(
532532}
533533
534534pub fn addLinkTests(
535 b: *build.Builder,
535 b: *std.Build,
536536 test_filter: ?[]const u8,
537 modes: []const Mode,
537 optimize_modes: []const OptimizeMode,
538538 enable_macos_sdk: bool,
539539 omit_stage2: bool,
540540 enable_symlinks_windows: bool,
541) *build.Step {
541) *Step {
542542 const cases = b.allocator.create(StandaloneContext) catch unreachable;
543543 cases.* = StandaloneContext{
544544 .b = b,
545545 .step = b.step("test-link", "Run the linker tests"),
546546 .test_index = 0,
547547 .test_filter = test_filter,
548 .modes = modes,
548 .optimize_modes = optimize_modes,
549549 .skip_non_native = true,
550550 .enable_macos_sdk = enable_macos_sdk,
551551 .target = .{},
......@@ -556,12 +556,17 @@ pub fn addLinkTests(
556556 return cases.step;
557557}
558558
559pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
559pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
560560 _ = test_filter;
561 _ = modes;
561 _ = optimize_modes;
562562 const step = b.step("test-cli", "Test the command line interface");
563563
564 const exe = b.addExecutable("test-cli", "test/cli.zig");
564 const exe = b.addExecutable(.{
565 .name = "test-cli",
566 .root_source_file = .{ .path = "test/cli.zig" },
567 .target = .{},
568 .optimize = .Debug,
569 });
565570 const run_cmd = exe.run();
566571 run_cmd.addArgs(&[_][]const u8{
567572 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,
......@@ -572,14 +577,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M
572577 return step;
573578}
574579
575pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
580pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
576581 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
577582 cases.* = CompareOutputContext{
578583 .b = b,
579584 .step = b.step("test-asm-link", "Run the assemble and link tests"),
580585 .test_index = 0,
581586 .test_filter = test_filter,
582 .modes = modes,
587 .optimize_modes = optimize_modes,
583588 };
584589
585590 assemble_and_link.addCases(cases);
......@@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode
587592 return cases.step;
588593}
589594
590pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
595pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
591596 const cases = b.allocator.create(TranslateCContext) catch unreachable;
592597 cases.* = TranslateCContext{
593598 .b = b,
......@@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
602607}
603608
604609pub fn addRunTranslatedCTests(
605 b: *build.Builder,
610 b: *std.Build,
606611 test_filter: ?[]const u8,
607612 target: std.zig.CrossTarget,
608) *build.Step {
613) *Step {
609614 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
610615 cases.* = .{
611616 .b = b,
......@@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests(
620625 return cases.step;
621626}
622627
623pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
628pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {
624629 const cases = b.allocator.create(GenHContext) catch unreachable;
625630 cases.* = GenHContext{
626631 .b = b,
......@@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
635640}
636641
637642pub fn addPkgTests(
638 b: *build.Builder,
643 b: *std.Build,
639644 test_filter: ?[]const u8,
640645 root_src: []const u8,
641646 name: []const u8,
642647 desc: []const u8,
643 modes: []const Mode,
648 optimize_modes: []const OptimizeMode,
644649 skip_single_threaded: bool,
645650 skip_non_native: bool,
646651 skip_libc: bool,
647652 skip_stage1: bool,
648653 skip_stage2: bool,
649) *build.Step {
654) *Step {
650655 const step = b.step(b.fmt("test-{s}", .{name}), desc);
651656
652657 for (test_targets) |test_target| {
......@@ -677,8 +682,8 @@ pub fn addPkgTests(
677682 else => if (skip_stage2) continue,
678683 };
679684
680 const want_this_mode = for (modes) |m| {
681 if (m == test_target.mode) break true;
685 const want_this_mode = for (optimize_modes) |m| {
686 if (m == test_target.optimize_mode) break true;
682687 } else false;
683688 if (!want_this_mode) continue;
684689
......@@ -691,21 +696,23 @@ pub fn addPkgTests(
691696
692697 const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable;
693698
694 const these_tests = b.addTest(root_src);
699 const these_tests = b.addTest(.{
700 .root_source_file = .{ .path = root_src },
701 .optimize = test_target.optimize_mode,
702 .target = test_target.target,
703 });
695704 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
696705 const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";
697706 these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{
698707 name,
699708 triple_prefix,
700 @tagName(test_target.mode),
709 @tagName(test_target.optimize_mode),
701710 libc_prefix,
702711 single_threaded_txt,
703712 backend_txt,
704713 }));
705714 these_tests.single_threaded = test_target.single_threaded;
706715 these_tests.setFilter(test_filter);
707 these_tests.setBuildMode(test_target.mode);
708 these_tests.setTarget(test_target.target);
709716 if (test_target.link_libc) {
710717 these_tests.linkSystemLibrary("c");
711718 }
......@@ -735,13 +742,13 @@ pub fn addPkgTests(
735742}
736743
737744pub const StackTracesContext = struct {
738 b: *build.Builder,
739 step: *build.Step,
745 b: *std.Build,
746 step: *Step,
740747 test_index: usize,
741748 test_filter: ?[]const u8,
742 modes: []const Mode,
749 optimize_modes: []const OptimizeMode,
743750
744 const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8;
751 const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8;
745752
746753 pub fn addCase(self: *StackTracesContext, config: anytype) void {
747754 if (@hasField(@TypeOf(config), "exclude")) {
......@@ -755,26 +762,26 @@ pub const StackTracesContext = struct {
755762 const exclude_os: []const std.Target.Os.Tag = &config.exclude_os;
756763 for (exclude_os) |os| if (os == builtin.os.tag) return;
757764 }
758 for (self.modes) |mode| {
759 switch (mode) {
765 for (self.optimize_modes) |optimize_mode| {
766 switch (optimize_mode) {
760767 .Debug => {
761768 if (@hasField(@TypeOf(config), "Debug")) {
762 self.addExpect(config.name, config.source, mode, config.Debug);
769 self.addExpect(config.name, config.source, optimize_mode, config.Debug);
763770 }
764771 },
765772 .ReleaseSafe => {
766773 if (@hasField(@TypeOf(config), "ReleaseSafe")) {
767 self.addExpect(config.name, config.source, mode, config.ReleaseSafe);
774 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe);
768775 }
769776 },
770777 .ReleaseFast => {
771778 if (@hasField(@TypeOf(config), "ReleaseFast")) {
772 self.addExpect(config.name, config.source, mode, config.ReleaseFast);
779 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast);
773780 }
774781 },
775782 .ReleaseSmall => {
776783 if (@hasField(@TypeOf(config), "ReleaseSmall")) {
777 self.addExpect(config.name, config.source, mode, config.ReleaseSmall);
784 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall);
778785 }
779786 },
780787 }
......@@ -785,7 +792,7 @@ pub const StackTracesContext = struct {
785792 self: *StackTracesContext,
786793 name: []const u8,
787794 source: []const u8,
788 mode: Mode,
795 optimize_mode: OptimizeMode,
789796 mode_config: anytype,
790797 ) void {
791798 if (@hasField(@TypeOf(mode_config), "exclude")) {
......@@ -803,7 +810,7 @@ pub const StackTracesContext = struct {
803810 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
804811 "stack-trace",
805812 name,
806 @tagName(mode),
813 @tagName(optimize_mode),
807814 }) catch unreachable;
808815 if (self.test_filter) |filter| {
809816 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
......@@ -812,14 +819,18 @@ pub const StackTracesContext = struct {
812819 const b = self.b;
813820 const src_basename = "source.zig";
814821 const write_src = b.addWriteFile(src_basename, source);
815 const exe = b.addExecutableSource("test", write_src.getFileSource(src_basename).?);
816 exe.setBuildMode(mode);
822 const exe = b.addExecutable(.{
823 .name = "test",
824 .root_source_file = write_src.getFileSource(src_basename).?,
825 .optimize = optimize_mode,
826 .target = .{},
827 });
817828
818829 const run_and_compare = RunAndCompareStep.create(
819830 self,
820831 exe,
821832 annotated_case_name,
822 mode,
833 optimize_mode,
823834 mode_config.expect,
824835 );
825836
......@@ -829,29 +840,29 @@ pub const StackTracesContext = struct {
829840 const RunAndCompareStep = struct {
830841 pub const base_id = .custom;
831842
832 step: build.Step,
843 step: Step,
833844 context: *StackTracesContext,
834 exe: *LibExeObjStep,
845 exe: *CompileStep,
835846 name: []const u8,
836 mode: Mode,
847 optimize_mode: OptimizeMode,
837848 expect_output: []const u8,
838849 test_index: usize,
839850
840851 pub fn create(
841852 context: *StackTracesContext,
842 exe: *LibExeObjStep,
853 exe: *CompileStep,
843854 name: []const u8,
844 mode: Mode,
855 optimize_mode: OptimizeMode,
845856 expect_output: []const u8,
846857 ) *RunAndCompareStep {
847858 const allocator = context.b.allocator;
848859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
849860 ptr.* = RunAndCompareStep{
850 .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
861 .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
851862 .context = context,
852863 .exe = exe,
853864 .name = name,
854 .mode = mode,
865 .optimize_mode = optimize_mode,
855866 .expect_output = expect_output,
856867 .test_index = context.test_index,
857868 };
......@@ -860,7 +871,7 @@ pub const StackTracesContext = struct {
860871 return ptr;
861872 }
862873
863 fn make(step: *build.Step) !void {
874 fn make(step: *Step) !void {
864875 const self = @fieldParentPtr(RunAndCompareStep, "step", step);
865876 const b = self.context.b;
866877
......@@ -932,7 +943,7 @@ pub const StackTracesContext = struct {
932943 // process result
933944 // - keep only basename of source file path
934945 // - replace address with symbolic string
935 // - replace function name with symbolic string when mode != .Debug
946 // - replace function name with symbolic string when optimize_mode != .Debug
936947 // - skip empty lines
937948 const got: []const u8 = got_result: {
938949 var buf = ArrayList(u8).init(b.allocator);
......@@ -968,7 +979,7 @@ pub const StackTracesContext = struct {
968979 // emit substituted line
969980 try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);
970981 try buf.appendSlice(" [address]");
971 if (self.mode == .Debug) {
982 if (self.optimize_mode == .Debug) {
972983 // On certain platforms (windows) or possibly depending on how we choose to link main
973984 // the object file extension may be present so we simply strip any extension.
974985 if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| {
......@@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct {
10031014};
10041015
10051016pub const StandaloneContext = struct {
1006 b: *build.Builder,
1007 step: *build.Step,
1017 b: *std.Build,
1018 step: *Step,
10081019 test_index: usize,
10091020 test_filter: ?[]const u8,
1010 modes: []const Mode,
1021 optimize_modes: []const OptimizeMode,
10111022 skip_non_native: bool,
10121023 enable_macos_sdk: bool,
10131024 target: std.zig.CrossTarget,
......@@ -1087,13 +1098,13 @@ pub const StandaloneContext = struct {
10871098 }
10881099 }
10891100
1090 const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug};
1091 for (modes) |mode| {
1092 const arg = switch (mode) {
1101 const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug};
1102 for (optimize_modes) |optimize_mode| {
1103 const arg = switch (optimize_mode) {
10931104 .Debug => "",
1094 .ReleaseFast => "-Drelease-fast",
1095 .ReleaseSafe => "-Drelease-safe",
1096 .ReleaseSmall => "-Drelease-small",
1105 .ReleaseFast => "-Doptimize=ReleaseFast",
1106 .ReleaseSafe => "-Doptimize=ReleaseSafe",
1107 .ReleaseSmall => "-Doptimize=ReleaseSmall",
10971108 };
10981109 const zig_args_base_len = zig_args.items.len;
10991110 if (arg.len > 0)
......@@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct {
11011112 defer zig_args.resize(zig_args_base_len) catch unreachable;
11021113
11031114 const run_cmd = b.addSystemCommand(zig_args.items);
1104 const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(mode) });
1115 const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) });
11051116 log_step.step.dependOn(&run_cmd.step);
11061117
11071118 self.step.dependOn(&log_step.step);
......@@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct {
11111122 pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void {
11121123 const b = self.b;
11131124
1114 for (self.modes) |mode| {
1125 for (self.optimize_modes) |optimize| {
11151126 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{
11161127 root_src,
1117 @tagName(mode),
1128 @tagName(optimize),
11181129 }) catch unreachable;
11191130 if (self.test_filter) |filter| {
11201131 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
11211132 }
11221133
1123 const exe = b.addExecutable("test", root_src);
1124 exe.setBuildMode(mode);
1134 const exe = b.addExecutable(.{
1135 .name = "test",
1136 .root_source_file = .{ .path = root_src },
1137 .optimize = optimize,
1138 .target = .{},
1139 });
11251140 if (link_libc) {
11261141 exe.linkSystemLibrary("c");
11271142 }
......@@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct {
11351150};
11361151
11371152pub const GenHContext = struct {
1138 b: *build.Builder,
1139 step: *build.Step,
1153 b: *std.Build,
1154 step: *Step,
11401155 test_index: usize,
11411156 test_filter: ?[]const u8,
11421157
......@@ -1163,23 +1178,23 @@ pub const GenHContext = struct {
11631178 };
11641179
11651180 const GenHCmpOutputStep = struct {
1166 step: build.Step,
1181 step: Step,
11671182 context: *GenHContext,
1168 obj: *LibExeObjStep,
1183 obj: *CompileStep,
11691184 name: []const u8,
11701185 test_index: usize,
11711186 case: *const TestCase,
11721187
11731188 pub fn create(
11741189 context: *GenHContext,
1175 obj: *LibExeObjStep,
1190 obj: *CompileStep,
11761191 name: []const u8,
11771192 case: *const TestCase,
11781193 ) *GenHCmpOutputStep {
11791194 const allocator = context.b.allocator;
11801195 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
11811196 ptr.* = GenHCmpOutputStep{
1182 .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make),
1197 .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make),
11831198 .context = context,
11841199 .obj = obj,
11851200 .name = name,
......@@ -1191,7 +1206,7 @@ pub const GenHContext = struct {
11911206 return ptr;
11921207 }
11931208
1194 fn make(step: *build.Step) !void {
1209 fn make(step: *Step) !void {
11951210 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
11961211 const b = self.context.b;
11971212
......@@ -1247,8 +1262,8 @@ pub const GenHContext = struct {
12471262 pub fn addCase(self: *GenHContext, case: *const TestCase) void {
12481263 const b = self.b;
12491264
1250 const mode = std.builtin.Mode.Debug;
1251 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable;
1265 const optimize_mode = std.builtin.OptimizeMode.Debug;
1266 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable;
12521267 if (self.test_filter) |filter| {
12531268 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
12541269 }
......@@ -1259,7 +1274,7 @@ pub const GenHContext = struct {
12591274 }
12601275
12611276 const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename);
1262 obj.setBuildMode(mode);
1277 obj.setBuildMode(optimize_mode);
12631278
12641279 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);
12651280
......@@ -1333,17 +1348,20 @@ const c_abi_targets = [_]CrossTarget{
13331348 },
13341349};
13351350
1336pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool) *build.Step {
1351pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
13371352 const step = b.step("test-c-abi", "Run the C ABI tests");
13381353
1339 const modes: [2]Mode = .{ .Debug, .ReleaseFast };
1354 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };
13401355
1341 for (modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |mode| for (c_abi_targets) |c_abi_target| {
1356 for (optimize_modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |optimize_mode| for (c_abi_targets) |c_abi_target| {
13421357 if (skip_non_native and !c_abi_target.isNative())
13431358 continue;
13441359
1345 const test_step = b.addTest("test/c_abi/main.zig");
1346 test_step.setTarget(c_abi_target);
1360 const test_step = b.addTest(.{
1361 .root_source_file = .{ .path = "test/c_abi/main.zig" },
1362 .optimize = optimize_mode,
1363 .target = c_abi_target,
1364 });
13471365 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {
13481366 // TODO NativeTargetInfo insists on dynamically linking musl
13491367 // for some reason?
......@@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool
13511369 }
13521370 test_step.linkLibC();
13531371 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});
1354 test_step.setBuildMode(mode);
13551372
13561373 if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) {
13571374 // LTO currently incorrectly strips stdcall name-mangled functions
......@@ -1363,7 +1380,7 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool
13631380 test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{
13641381 "test-c-abi",
13651382 triple_prefix,
1366 @tagName(mode),
1383 @tagName(optimize_mode),
13671384 }));
13681385
13691386 step.dependOn(&test_step.step);