authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-31 00:19:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-31 15:09:35-07:00
log36e2d992dd8c45ca89a51d508c6c413cff5ad2cd
tree5e8a3244cf44c24b216959a475e0277e1ed424af
parent73cf7b64291ed8b5dcb4cb52df103be08f15a347

combine std.build and std.build.Builder into std.Build

I've been wanting to do this for along time.

113 files changed, 7086 insertions(+), 7146 deletions(-)

build.zig+15-16
......@@ -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: {
......@@ -477,7 +476,7 @@ pub fn build(b: *Builder) !void {
477476 try addWasiUpdateStep(b, version);
478477}
479478
480fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
479fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
481480 const semver = try std.SemanticVersion.parse(version);
482481
483482 var target: std.zig.CrossTarget = .{
......@@ -514,10 +513,10 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
514513}
515514
516515fn addCompilerStep(
517 b: *Builder,
516 b: *std.Build,
518517 optimize: std.builtin.OptimizeMode,
519518 target: std.zig.CrossTarget,
520) *std.build.LibExeObjStep {
519) *std.Build.LibExeObjStep {
521520 const exe = b.addExecutable(.{
522521 .name = "zig",
523522 .root_source_file = .{ .path = "src/main.zig" },
......@@ -543,9 +542,9 @@ const exe_cflags = [_][]const u8{
543542};
544543
545544fn addCmakeCfgOptionsToExe(
546 b: *Builder,
545 b: *std.Build,
547546 cfg: CMakeConfig,
548 exe: *std.build.LibExeObjStep,
547 exe: *std.Build.LibExeObjStep,
549548 use_zig_libcxx: bool,
550549) !void {
551550 if (exe.target.isDarwin()) {
......@@ -624,7 +623,7 @@ fn addCmakeCfgOptionsToExe(
624623 }
625624}
626625
627fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
626fn addStaticLlvmOptionsToExe(exe: *std.Build.LibExeObjStep) !void {
628627 // Adds the Zig C++ sources which both stage1 and stage2 need.
629628 //
630629 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
......@@ -661,9 +660,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
661660}
662661
663662fn addCxxKnownPath(
664 b: *Builder,
663 b: *std.Build,
665664 ctx: CMakeConfig,
666 exe: *std.build.LibExeObjStep,
665 exe: *std.Build.LibExeObjStep,
667666 objname: []const u8,
668667 errtxt: ?[]const u8,
669668 need_cpp_includes: bool,
......@@ -696,7 +695,7 @@ fn addCxxKnownPath(
696695 }
697696}
698697
699fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
698fn addCMakeLibraryList(exe: *std.Build.LibExeObjStep, list: []const u8) void {
700699 var it = mem.tokenize(u8, list, ";");
701700 while (it.next()) |lib| {
702701 if (mem.startsWith(u8, lib, "-l")) {
......@@ -710,7 +709,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
710709}
711710
712711const CMakeConfig = struct {
713 llvm_linkage: std.build.LibExeObjStep.Linkage,
712 llvm_linkage: std.Build.LibExeObjStep.Linkage,
714713 cmake_binary_dir: []const u8,
715714 cmake_prefix_path: []const u8,
716715 cmake_static_library_prefix: []const u8,
......@@ -727,7 +726,7 @@ const CMakeConfig = struct {
727726
728727const max_config_h_bytes = 1 * 1024 * 1024;
729728
730fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
729fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
731730 if (config_h_path_option) |path| {
732731 var config_h_or_err = fs.cwd().openFile(path, .{});
733732 if (config_h_or_err) |*file| {
......@@ -773,7 +772,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
773772 } else unreachable; // TODO should not need `else unreachable`.
774773}
775774
776fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
775fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
777776 var ctx: CMakeConfig = .{
778777 .llvm_linkage = undefined,
779778 .cmake_binary_dir = undefined,
......@@ -862,7 +861,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
862861 return ctx;
863862}
864863
865fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
864fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
866865 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
867866 for (duplicated) |*byte| switch (byte.*) {
868867 '/' => byte.* = fs.path.sep,
doc/langref.html.in+10-10
......@@ -9528,9 +9528,9 @@ 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 {
9533pub fn build(b: *std.Build) void {
95349534 const optimize = b.standardOptimizeOption(.{});
95359535 const exe = b.addExecutable(.{
95369536 .name = "example",
......@@ -10551,9 +10551,9 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
1055110551 <p>This <code class="file">build.zig</code> file is automatically generated
1055210552 by <kbd>zig init-exe</kbd>.</p>
1055310553 {#code_begin|syntax|build_executable#}
10554const Builder = @import("std").build.Builder;
10554const std = @import("std");
1055510555
10556pub fn build(b: *Builder) void {
10556pub fn build(b: *std.Build) void {
1055710557 // Standard target options allows the person running `zig build` to choose
1055810558 // what target to build for. Here we do not override the defaults, which
1055910559 // means any target is allowed, and the default is native. Other options
......@@ -10588,9 +10588,9 @@ pub fn build(b: *Builder) void {
1058810588 <p>This <code class="file">build.zig</code> file is automatically generated
1058910589 by <kbd>zig init-lib</kbd>.</p>
1059010590 {#code_begin|syntax|build_library#}
10591const Builder = @import("std").build.Builder;
10591const std = @import("std");
1059210592
10593pub fn build(b: *Builder) void {
10593pub fn build(b: *std.Build) void {
1059410594 const optimize = b.standardOptimizeOption(.{});
1059510595 const lib = b.addStaticLibrary(.{
1059610596 .name = "example",
......@@ -10961,9 +10961,9 @@ int main(int argc, char **argv) {
1096110961}
1096210962 {#end_syntax_block#}
1096310963 {#code_begin|syntax|build_c#}
10964const Builder = @import("std").build.Builder;
10964const std = @import("std");
1096510965
10966pub fn build(b: *Builder) void {
10966pub fn build(b: *std.Build) void {
1096710967 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
1096810968
1096910969 const exe = b.addExecutable(.{
......@@ -11025,9 +11025,9 @@ int main(int argc, char **argv) {
1102511025}
1102611026 {#end_syntax_block#}
1102711027 {#code_begin|syntax|build_object#}
11028const Builder = @import("std").build.Builder;
11028const std = @import("std");
1102911029
11030pub fn build(b: *Builder) void {
11030pub fn build(b: *std.Build) void {
1103111031 const obj = b.addObject("base64", "base64.zig");
1103211032
1103311033 const exe = b.addExecutable(.{
lib/build_runner.zig+4-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,7 +41,7 @@ pub fn main() !void {
4241 return error.InvalidArgs;
4342 };
4443
45 const builder = try Builder.create(
44 const builder = try std.Build.create(
4645 allocator,
4746 zig_exe,
4847 build_root,
......@@ -58,7 +57,7 @@ pub fn main() !void {
5857 const stdout_stream = io.getStdOut().writer();
5958
6059 var install_prefix: ?[]const u8 = null;
61 var dir_list = Builder.DirList{};
60 var dir_list = std.Build.DirList{};
6261
6362 // before arg parsing, check for the NO_COLOR environment variable
6463 // if it exists, default the color setting to .off
......@@ -230,7 +229,7 @@ pub fn main() !void {
230229 };
231230}
232231
233fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
232fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
234233 // run the build script to collect the options
235234 if (!already_ran_build) {
236235 builder.resolveInstallPrefix(null, .{});
......@@ -330,7 +329,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
330329 );
331330}
332331
333fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void {
332fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void {
334333 usage(builder, already_ran_build, out_stream) catch {};
335334 process.exit(1);
336335}
lib/init-exe/build.zig+1-1
......@@ -3,7 +3,7 @@ const std = @import("std");
33// Although this function looks imperative, note that its job is to
44// declaratively construct a build graph that will be executed by an external
55// runner.
6pub fn build(b: *std.build.Builder) void {
6pub fn build(b: *std.Build) void {
77 // Standard target options allows the person running `zig build` to choose
88 // what target to build for. Here we do not override the defaults, which
99 // means any target is allowed, and the default is native. Other options
lib/init-lib/build.zig+1-1
......@@ -3,7 +3,7 @@ const std = @import("std");
33// Although this function looks imperative, note that its job is to
44// declaratively construct a build graph that will be executed by an external
55// runner.
6pub fn build(b: *std.build.Builder) void {
6pub fn build(b: *std.Build) void {
77 // Standard target options allows the person running `zig build` to choose
88 // what target to build for. Here we do not override the defaults, which
99 // means any target is allowed, and the default is native. Other options
lib/std/Build.zig created+1771
......@@ -0,0 +1,1771 @@
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
22pub const Step = @import("Build/Step.zig");
23pub const CheckFileStep = @import("Build/CheckFileStep.zig");
24pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
25pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
26pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
27pub const FmtStep = @import("Build/FmtStep.zig");
28pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
29pub const InstallDirStep = @import("Build/InstallDirStep.zig");
30pub const InstallFileStep = @import("Build/InstallFileStep.zig");
31pub const InstallRawStep = @import("Build/InstallRawStep.zig");
32pub const LibExeObjStep = @import("Build/LibExeObjStep.zig");
33pub const LogStep = @import("Build/LogStep.zig");
34pub const OptionsStep = @import("Build/OptionsStep.zig");
35pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
36pub const RunStep = @import("Build/RunStep.zig");
37pub const TranslateCStep = @import("Build/TranslateCStep.zig");
38pub const WriteFileStep = @import("Build/WriteFileStep.zig");
39
40install_tls: TopLevelStep,
41uninstall_tls: TopLevelStep,
42allocator: Allocator,
43user_input_options: UserInputOptionsMap,
44available_options_map: AvailableOptionsMap,
45available_options_list: ArrayList(AvailableOption),
46verbose: bool,
47verbose_link: bool,
48verbose_cc: bool,
49verbose_air: bool,
50verbose_llvm_ir: bool,
51verbose_cimport: bool,
52verbose_llvm_cpu_features: bool,
53/// The purpose of executing the command is for a human to read compile errors from the terminal
54prominent_compile_errors: bool,
55color: enum { auto, on, off } = .auto,
56reference_trace: ?u32 = null,
57invalid_user_input: bool,
58zig_exe: []const u8,
59default_step: *Step,
60env_map: *EnvMap,
61top_level_steps: ArrayList(*TopLevelStep),
62install_prefix: []const u8,
63dest_dir: ?[]const u8,
64lib_dir: []const u8,
65exe_dir: []const u8,
66h_dir: []const u8,
67install_path: []const u8,
68sysroot: ?[]const u8 = null,
69search_prefixes: ArrayList([]const u8),
70libc_file: ?[]const u8 = null,
71installed_files: ArrayList(InstalledFile),
72/// Path to the directory containing build.zig.
73build_root: []const u8,
74cache_root: []const u8,
75global_cache_root: []const u8,
76/// zig lib dir
77override_lib_dir: ?[]const u8,
78vcpkg_root: VcpkgRoot = .unattempted,
79pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
80args: ?[][]const u8 = null,
81debug_log_scopes: []const []const u8 = &.{},
82debug_compile_errors: bool = false,
83
84/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
85enable_darling: bool = false,
86/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
87enable_qemu: bool = false,
88/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
89enable_rosetta: bool = false,
90/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
91enable_wasmtime: bool = false,
92/// Use system Wine installation to run cross compiled Windows build artifacts.
93enable_wine: bool = false,
94/// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
95/// this will be the directory $glibc-build-dir/install/glibcs
96/// Given the example of the aarch64 target, this is the directory
97/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
98glibc_runtimes_dir: ?[]const u8 = null,
99
100/// Information about the native target. Computed before build() is invoked.
101host: NativeTargetInfo,
102
103dep_prefix: []const u8 = "",
104
105pub const ExecError = error{
106 ReadFailure,
107 ExitCodeFailure,
108 ProcessTerminated,
109 ExecNotSupported,
110} || std.ChildProcess.SpawnError;
111
112pub const PkgConfigError = error{
113 PkgConfigCrashed,
114 PkgConfigFailed,
115 PkgConfigNotInstalled,
116 PkgConfigInvalidOutput,
117};
118
119pub const PkgConfigPkg = struct {
120 name: []const u8,
121 desc: []const u8,
122};
123
124pub const CStd = enum {
125 C89,
126 C99,
127 C11,
128};
129
130const UserInputOptionsMap = StringHashMap(UserInputOption);
131const AvailableOptionsMap = StringHashMap(AvailableOption);
132
133const AvailableOption = struct {
134 name: []const u8,
135 type_id: TypeId,
136 description: []const u8,
137 /// If the `type_id` is `enum` this provides the list of enum options
138 enum_options: ?[]const []const u8,
139};
140
141const UserInputOption = struct {
142 name: []const u8,
143 value: UserValue,
144 used: bool,
145};
146
147const UserValue = union(enum) {
148 flag: void,
149 scalar: []const u8,
150 list: ArrayList([]const u8),
151 map: StringHashMap(*const UserValue),
152};
153
154const TypeId = enum {
155 bool,
156 int,
157 float,
158 @"enum",
159 string,
160 list,
161};
162
163const TopLevelStep = struct {
164 pub const base_id = .top_level;
165
166 step: Step,
167 description: []const u8,
168};
169
170pub const DirList = struct {
171 lib_dir: ?[]const u8 = null,
172 exe_dir: ?[]const u8 = null,
173 include_dir: ?[]const u8 = null,
174};
175
176pub fn create(
177 allocator: Allocator,
178 zig_exe: []const u8,
179 build_root: []const u8,
180 cache_root: []const u8,
181 global_cache_root: []const u8,
182) !*Build {
183 const env_map = try allocator.create(EnvMap);
184 env_map.* = try process.getEnvMap(allocator);
185
186 const host = try NativeTargetInfo.detect(.{});
187
188 const self = try allocator.create(Build);
189 self.* = Build{
190 .zig_exe = zig_exe,
191 .build_root = build_root,
192 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
193 .global_cache_root = global_cache_root,
194 .verbose = false,
195 .verbose_link = false,
196 .verbose_cc = false,
197 .verbose_air = false,
198 .verbose_llvm_ir = false,
199 .verbose_cimport = false,
200 .verbose_llvm_cpu_features = false,
201 .prominent_compile_errors = false,
202 .invalid_user_input = false,
203 .allocator = allocator,
204 .user_input_options = UserInputOptionsMap.init(allocator),
205 .available_options_map = AvailableOptionsMap.init(allocator),
206 .available_options_list = ArrayList(AvailableOption).init(allocator),
207 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
208 .default_step = undefined,
209 .env_map = env_map,
210 .search_prefixes = ArrayList([]const u8).init(allocator),
211 .install_prefix = undefined,
212 .lib_dir = undefined,
213 .exe_dir = undefined,
214 .h_dir = undefined,
215 .dest_dir = env_map.get("DESTDIR"),
216 .installed_files = ArrayList(InstalledFile).init(allocator),
217 .install_tls = TopLevelStep{
218 .step = Step.initNoOp(.top_level, "install", allocator),
219 .description = "Copy build artifacts to prefix path",
220 },
221 .uninstall_tls = TopLevelStep{
222 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
223 .description = "Remove build artifacts from prefix path",
224 },
225 .override_lib_dir = null,
226 .install_path = undefined,
227 .args = null,
228 .host = host,
229 };
230 try self.top_level_steps.append(&self.install_tls);
231 try self.top_level_steps.append(&self.uninstall_tls);
232 self.default_step = &self.install_tls.step;
233 return self;
234}
235
236fn createChild(
237 parent: *Build,
238 dep_name: []const u8,
239 build_root: []const u8,
240 args: anytype,
241) !*Build {
242 const child = try createChildOnly(parent, dep_name, build_root);
243 try applyArgs(child, args);
244 return child;
245}
246
247fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
248 const allocator = parent.allocator;
249 const child = try allocator.create(Build);
250 child.* = .{
251 .allocator = allocator,
252 .install_tls = .{
253 .step = Step.initNoOp(.top_level, "install", allocator),
254 .description = "Copy build artifacts to prefix path",
255 },
256 .uninstall_tls = .{
257 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
258 .description = "Remove build artifacts from prefix path",
259 },
260 .user_input_options = UserInputOptionsMap.init(allocator),
261 .available_options_map = AvailableOptionsMap.init(allocator),
262 .available_options_list = ArrayList(AvailableOption).init(allocator),
263 .verbose = parent.verbose,
264 .verbose_link = parent.verbose_link,
265 .verbose_cc = parent.verbose_cc,
266 .verbose_air = parent.verbose_air,
267 .verbose_llvm_ir = parent.verbose_llvm_ir,
268 .verbose_cimport = parent.verbose_cimport,
269 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
270 .prominent_compile_errors = parent.prominent_compile_errors,
271 .color = parent.color,
272 .reference_trace = parent.reference_trace,
273 .invalid_user_input = false,
274 .zig_exe = parent.zig_exe,
275 .default_step = undefined,
276 .env_map = parent.env_map,
277 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
278 .install_prefix = undefined,
279 .dest_dir = parent.dest_dir,
280 .lib_dir = parent.lib_dir,
281 .exe_dir = parent.exe_dir,
282 .h_dir = parent.h_dir,
283 .install_path = parent.install_path,
284 .sysroot = parent.sysroot,
285 .search_prefixes = ArrayList([]const u8).init(allocator),
286 .libc_file = parent.libc_file,
287 .installed_files = ArrayList(InstalledFile).init(allocator),
288 .build_root = build_root,
289 .cache_root = parent.cache_root,
290 .global_cache_root = parent.global_cache_root,
291 .override_lib_dir = parent.override_lib_dir,
292 .debug_log_scopes = parent.debug_log_scopes,
293 .debug_compile_errors = parent.debug_compile_errors,
294 .enable_darling = parent.enable_darling,
295 .enable_qemu = parent.enable_qemu,
296 .enable_rosetta = parent.enable_rosetta,
297 .enable_wasmtime = parent.enable_wasmtime,
298 .enable_wine = parent.enable_wine,
299 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
300 .host = parent.host,
301 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
302 };
303 try child.top_level_steps.append(&child.install_tls);
304 try child.top_level_steps.append(&child.uninstall_tls);
305 child.default_step = &child.install_tls.step;
306 return child;
307}
308
309fn applyArgs(b: *Build, args: anytype) !void {
310 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
311 const v = @field(args, field.name);
312 const T = @TypeOf(v);
313 switch (T) {
314 CrossTarget => {
315 try b.user_input_options.put(field.name, .{
316 .name = field.name,
317 .value = .{ .scalar = try v.zigTriple(b.allocator) },
318 .used = false,
319 });
320 try b.user_input_options.put("cpu", .{
321 .name = "cpu",
322 .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
323 .used = false,
324 });
325 },
326 []const u8 => {
327 try b.user_input_options.put(field.name, .{
328 .name = field.name,
329 .value = .{ .scalar = v },
330 .used = false,
331 });
332 },
333 else => switch (@typeInfo(T)) {
334 .Bool => {
335 try b.user_input_options.put(field.name, .{
336 .name = field.name,
337 .value = .{ .scalar = if (v) "true" else "false" },
338 .used = false,
339 });
340 },
341 .Enum => {
342 try b.user_input_options.put(field.name, .{
343 .name = field.name,
344 .value = .{ .scalar = @tagName(v) },
345 .used = false,
346 });
347 },
348 .Int => {
349 try b.user_input_options.put(field.name, .{
350 .name = field.name,
351 .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
352 .used = false,
353 });
354 },
355 else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
356 },
357 }
358 }
359 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
360 // Random bytes to make unique. Refresh this with new random bytes when
361 // implementation is modified in a non-backwards-compatible way.
362 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
363 hash.update(b.dep_prefix);
364 // TODO additionally update the hash with `args`.
365
366 var digest: [16]u8 = undefined;
367 hash.final(&digest);
368 var hash_basename: [digest.len * 2]u8 = undefined;
369 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
370 unreachable;
371
372 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
373 b.resolveInstallPrefix(install_prefix, .{});
374}
375
376pub fn destroy(self: *Build) void {
377 self.env_map.deinit();
378 self.top_level_steps.deinit();
379 self.allocator.destroy(self);
380}
381
382/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
383pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
384 if (self.dest_dir) |dest_dir| {
385 self.install_prefix = install_prefix orelse "/usr";
386 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
387 } else {
388 self.install_prefix = install_prefix orelse
389 (self.pathJoin(&.{ self.build_root, "zig-out" }));
390 self.install_path = self.install_prefix;
391 }
392
393 var lib_list = [_][]const u8{ self.install_path, "lib" };
394 var exe_list = [_][]const u8{ self.install_path, "bin" };
395 var h_list = [_][]const u8{ self.install_path, "include" };
396
397 if (dir_list.lib_dir) |dir| {
398 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
399 lib_list[1] = dir;
400 }
401
402 if (dir_list.exe_dir) |dir| {
403 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
404 exe_list[1] = dir;
405 }
406
407 if (dir_list.include_dir) |dir| {
408 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
409 h_list[1] = dir;
410 }
411
412 self.lib_dir = self.pathJoin(&lib_list);
413 self.exe_dir = self.pathJoin(&exe_list);
414 self.h_dir = self.pathJoin(&h_list);
415}
416
417pub fn addOptions(self: *Build) *OptionsStep {
418 return OptionsStep.create(self);
419}
420
421pub const ExecutableOptions = struct {
422 name: []const u8,
423 root_source_file: ?FileSource = null,
424 version: ?std.builtin.Version = null,
425 target: CrossTarget = .{},
426 optimize: std.builtin.Mode = .Debug,
427 linkage: ?LibExeObjStep.Linkage = null,
428};
429
430pub fn addExecutable(b: *Build, options: ExecutableOptions) *LibExeObjStep {
431 return LibExeObjStep.create(b, .{
432 .name = options.name,
433 .root_source_file = options.root_source_file,
434 .version = options.version,
435 .target = options.target,
436 .optimize = options.optimize,
437 .kind = .exe,
438 .linkage = options.linkage,
439 });
440}
441
442pub const ObjectOptions = struct {
443 name: []const u8,
444 root_source_file: ?FileSource = null,
445 target: CrossTarget,
446 optimize: std.builtin.Mode,
447};
448
449pub fn addObject(b: *Build, options: ObjectOptions) *LibExeObjStep {
450 return LibExeObjStep.create(b, .{
451 .name = options.name,
452 .root_source_file = options.root_source_file,
453 .target = options.target,
454 .optimize = options.optimize,
455 .kind = .obj,
456 });
457}
458
459pub const SharedLibraryOptions = struct {
460 name: []const u8,
461 root_source_file: ?FileSource = null,
462 version: ?std.builtin.Version = null,
463 target: CrossTarget,
464 optimize: std.builtin.Mode,
465};
466
467pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *LibExeObjStep {
468 return LibExeObjStep.create(b, .{
469 .name = options.name,
470 .root_source_file = options.root_source_file,
471 .kind = .lib,
472 .linkage = .dynamic,
473 .version = options.version,
474 .target = options.target,
475 .optimize = options.optimize,
476 });
477}
478
479pub const StaticLibraryOptions = struct {
480 name: []const u8,
481 root_source_file: ?FileSource = null,
482 target: CrossTarget,
483 optimize: std.builtin.Mode,
484 version: ?std.builtin.Version = null,
485};
486
487pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *LibExeObjStep {
488 return LibExeObjStep.create(b, .{
489 .name = options.name,
490 .root_source_file = options.root_source_file,
491 .kind = .lib,
492 .linkage = .static,
493 .version = options.version,
494 .target = options.target,
495 .optimize = options.optimize,
496 });
497}
498
499pub const TestOptions = struct {
500 name: []const u8 = "test",
501 kind: LibExeObjStep.Kind = .@"test",
502 root_source_file: FileSource,
503 target: CrossTarget = .{},
504 optimize: std.builtin.Mode = .Debug,
505 version: ?std.builtin.Version = null,
506};
507
508pub fn addTest(b: *Build, options: TestOptions) *LibExeObjStep {
509 return LibExeObjStep.create(b, .{
510 .name = options.name,
511 .kind = options.kind,
512 .root_source_file = options.root_source_file,
513 .target = options.target,
514 .optimize = options.optimize,
515 });
516}
517
518pub const AssemblyOptions = struct {
519 name: []const u8,
520 source_file: FileSource,
521 target: CrossTarget,
522 optimize: std.builtin.Mode,
523};
524
525pub fn addAssembly(b: *Build, options: AssemblyOptions) *LibExeObjStep {
526 const obj_step = LibExeObjStep.create(b, .{
527 .name = options.name,
528 .root_source_file = null,
529 .target = options.target,
530 .optimize = options.optimize,
531 });
532 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
533 return obj_step;
534}
535
536/// Initializes a RunStep with argv, which must at least have the path to the
537/// executable. More command line arguments can be added with `addArg`,
538/// `addArgs`, and `addArtifactArg`.
539/// Be careful using this function, as it introduces a system dependency.
540/// To run an executable built with zig build, see `LibExeObjStep.run`.
541pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
542 assert(argv.len >= 1);
543 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
544 run_step.addArgs(argv);
545 return run_step;
546}
547
548pub fn addConfigHeader(
549 b: *Build,
550 source: FileSource,
551 style: ConfigHeaderStep.Style,
552 values: anytype,
553) *ConfigHeaderStep {
554 const config_header_step = ConfigHeaderStep.create(b, source, style);
555 config_header_step.addValues(values);
556 return config_header_step;
557}
558
559/// Allocator.dupe without the need to handle out of memory.
560pub fn dupe(self: *Build, bytes: []const u8) []u8 {
561 return self.allocator.dupe(u8, bytes) catch unreachable;
562}
563
564/// Duplicates an array of strings without the need to handle out of memory.
565pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 {
566 const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
567 for (strings) |s, i| {
568 array[i] = self.dupe(s);
569 }
570 return array;
571}
572
573/// Duplicates a path and converts all slashes to the OS's canonical path separator.
574pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
575 const the_copy = self.dupe(bytes);
576 for (the_copy) |*byte| {
577 switch (byte.*) {
578 '/', '\\' => byte.* = fs.path.sep,
579 else => {},
580 }
581 }
582 return the_copy;
583}
584
585/// Duplicates a package recursively.
586pub fn dupePkg(self: *Build, package: Pkg) Pkg {
587 var the_copy = Pkg{
588 .name = self.dupe(package.name),
589 .source = package.source.dupe(self),
590 };
591
592 if (package.dependencies) |dependencies| {
593 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
594 the_copy.dependencies = new_dependencies;
595
596 for (dependencies) |dep_package, i| {
597 new_dependencies[i] = self.dupePkg(dep_package);
598 }
599 }
600 return the_copy;
601}
602
603pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
604 const write_file_step = self.addWriteFiles();
605 write_file_step.add(file_path, data);
606 return write_file_step;
607}
608
609pub fn addWriteFiles(self: *Build) *WriteFileStep {
610 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
611 write_file_step.* = WriteFileStep.init(self);
612 return write_file_step;
613}
614
615pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
616 const data = self.fmt(format, args);
617 const log_step = self.allocator.create(LogStep) catch unreachable;
618 log_step.* = LogStep.init(self, data);
619 return log_step;
620}
621
622pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
623 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
624 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
625 return remove_dir_step;
626}
627
628pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
629 return FmtStep.create(self, paths);
630}
631
632pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
633 return TranslateCStep.create(self, options);
634}
635
636pub fn make(self: *Build, step_names: []const []const u8) !void {
637 try self.makePath(self.cache_root);
638
639 var wanted_steps = ArrayList(*Step).init(self.allocator);
640 defer wanted_steps.deinit();
641
642 if (step_names.len == 0) {
643 try wanted_steps.append(self.default_step);
644 } else {
645 for (step_names) |step_name| {
646 const s = try self.getTopLevelStepByName(step_name);
647 try wanted_steps.append(s);
648 }
649 }
650
651 for (wanted_steps.items) |s| {
652 try self.makeOneStep(s);
653 }
654}
655
656pub fn getInstallStep(self: *Build) *Step {
657 return &self.install_tls.step;
658}
659
660pub fn getUninstallStep(self: *Build) *Step {
661 return &self.uninstall_tls.step;
662}
663
664fn makeUninstall(uninstall_step: *Step) anyerror!void {
665 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
666 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
667
668 for (self.installed_files.items) |installed_file| {
669 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
670 if (self.verbose) {
671 log.info("rm {s}", .{full_path});
672 }
673 fs.cwd().deleteTree(full_path) catch {};
674 }
675
676 // TODO remove empty directories
677}
678
679fn makeOneStep(self: *Build, s: *Step) anyerror!void {
680 if (s.loop_flag) {
681 log.err("Dependency loop detected:\n {s}", .{s.name});
682 return error.DependencyLoopDetected;
683 }
684 s.loop_flag = true;
685
686 for (s.dependencies.items) |dep| {
687 self.makeOneStep(dep) catch |err| {
688 if (err == error.DependencyLoopDetected) {
689 log.err(" {s}", .{s.name});
690 }
691 return err;
692 };
693 }
694
695 s.loop_flag = false;
696
697 try s.make();
698}
699
700fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
701 for (self.top_level_steps.items) |top_level_step| {
702 if (mem.eql(u8, top_level_step.step.name, name)) {
703 return &top_level_step.step;
704 }
705 }
706 log.err("Cannot run step '{s}' because it does not exist", .{name});
707 return error.InvalidStepName;
708}
709
710pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
711 const name = self.dupe(name_raw);
712 const description = self.dupe(description_raw);
713 const type_id = comptime typeToEnum(T);
714 const enum_options = if (type_id == .@"enum") blk: {
715 const fields = comptime std.meta.fields(T);
716 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
717
718 inline for (fields) |field| {
719 options.appendAssumeCapacity(field.name);
720 }
721
722 break :blk options.toOwnedSlice() catch unreachable;
723 } else null;
724 const available_option = AvailableOption{
725 .name = name,
726 .type_id = type_id,
727 .description = description,
728 .enum_options = enum_options,
729 };
730 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
731 panic("Option '{s}' declared twice", .{name});
732 }
733 self.available_options_list.append(available_option) catch unreachable;
734
735 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
736 option_ptr.used = true;
737 switch (type_id) {
738 .bool => switch (option_ptr.value) {
739 .flag => return true,
740 .scalar => |s| {
741 if (mem.eql(u8, s, "true")) {
742 return true;
743 } else if (mem.eql(u8, s, "false")) {
744 return false;
745 } else {
746 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
747 self.markInvalidUserInput();
748 return null;
749 }
750 },
751 .list, .map => {
752 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
753 name, @tagName(option_ptr.value),
754 });
755 self.markInvalidUserInput();
756 return null;
757 },
758 },
759 .int => switch (option_ptr.value) {
760 .flag, .list, .map => {
761 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
762 name, @tagName(option_ptr.value),
763 });
764 self.markInvalidUserInput();
765 return null;
766 },
767 .scalar => |s| {
768 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
769 error.Overflow => {
770 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
771 self.markInvalidUserInput();
772 return null;
773 },
774 else => {
775 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
776 self.markInvalidUserInput();
777 return null;
778 },
779 };
780 return n;
781 },
782 },
783 .float => switch (option_ptr.value) {
784 .flag, .map, .list => {
785 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
786 name, @tagName(option_ptr.value),
787 });
788 self.markInvalidUserInput();
789 return null;
790 },
791 .scalar => |s| {
792 const n = std.fmt.parseFloat(T, s) catch {
793 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
794 self.markInvalidUserInput();
795 return null;
796 };
797 return n;
798 },
799 },
800 .@"enum" => switch (option_ptr.value) {
801 .flag, .map, .list => {
802 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
803 name, @tagName(option_ptr.value),
804 });
805 self.markInvalidUserInput();
806 return null;
807 },
808 .scalar => |s| {
809 if (std.meta.stringToEnum(T, s)) |enum_lit| {
810 return enum_lit;
811 } else {
812 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
813 self.markInvalidUserInput();
814 return null;
815 }
816 },
817 },
818 .string => switch (option_ptr.value) {
819 .flag, .list, .map => {
820 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
821 name, @tagName(option_ptr.value),
822 });
823 self.markInvalidUserInput();
824 return null;
825 },
826 .scalar => |s| return s,
827 },
828 .list => switch (option_ptr.value) {
829 .flag, .map => {
830 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
831 name, @tagName(option_ptr.value),
832 });
833 self.markInvalidUserInput();
834 return null;
835 },
836 .scalar => |s| {
837 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
838 },
839 .list => |lst| return lst.items,
840 },
841 }
842}
843
844pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
845 const step_info = self.allocator.create(TopLevelStep) catch unreachable;
846 step_info.* = TopLevelStep{
847 .step = Step.initNoOp(.top_level, name, self.allocator),
848 .description = self.dupe(description),
849 };
850 self.top_level_steps.append(step_info) catch unreachable;
851 return &step_info.step;
852}
853
854pub const StandardOptimizeOptionOptions = struct {
855 preferred_optimize_mode: ?std.builtin.Mode = null,
856};
857
858pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode {
859 if (options.preferred_optimize_mode) |mode| {
860 if (self.option(bool, "release", "optimize for end users") orelse false) {
861 return mode;
862 } else {
863 return .Debug;
864 }
865 } else {
866 return self.option(
867 std.builtin.Mode,
868 "optimize",
869 "prioritize performance, safety, or binary size (-O flag)",
870 ) orelse .Debug;
871 }
872}
873
874pub const StandardTargetOptionsArgs = struct {
875 whitelist: ?[]const CrossTarget = null,
876
877 default_target: CrossTarget = CrossTarget{},
878};
879
880/// Exposes standard `zig build` options for choosing a target.
881pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
882 const maybe_triple = self.option(
883 []const u8,
884 "target",
885 "The CPU architecture, OS, and ABI to build for",
886 );
887 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
888
889 if (maybe_triple == null and mcpu == null) {
890 return args.default_target;
891 }
892
893 const triple = maybe_triple orelse "native";
894
895 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
896 const selected_target = CrossTarget.parse(.{
897 .arch_os_abi = triple,
898 .cpu_features = mcpu,
899 .diagnostics = &diags,
900 }) catch |err| switch (err) {
901 error.UnknownCpuModel => {
902 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
903 diags.cpu_name.?,
904 @tagName(diags.arch.?),
905 });
906 for (diags.arch.?.allCpuModels()) |cpu| {
907 log.err(" {s}", .{cpu.name});
908 }
909 self.markInvalidUserInput();
910 return args.default_target;
911 },
912 error.UnknownCpuFeature => {
913 log.err(
914 \\Unknown CPU feature: '{s}'
915 \\Available CPU features for architecture '{s}':
916 \\
917 , .{
918 diags.unknown_feature_name.?,
919 @tagName(diags.arch.?),
920 });
921 for (diags.arch.?.allFeaturesList()) |feature| {
922 log.err(" {s}: {s}", .{ feature.name, feature.description });
923 }
924 self.markInvalidUserInput();
925 return args.default_target;
926 },
927 error.UnknownOperatingSystem => {
928 log.err(
929 \\Unknown OS: '{s}'
930 \\Available operating systems:
931 \\
932 , .{diags.os_name.?});
933 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
934 log.err(" {s}", .{field.name});
935 }
936 self.markInvalidUserInput();
937 return args.default_target;
938 },
939 else => |e| {
940 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
941 self.markInvalidUserInput();
942 return args.default_target;
943 },
944 };
945
946 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
947
948 if (args.whitelist) |list| whitelist_check: {
949 // Make sure it's a match of one of the list.
950 var mismatch_triple = true;
951 var mismatch_cpu_features = true;
952 var whitelist_item = CrossTarget{};
953 for (list) |t| {
954 mismatch_cpu_features = true;
955 mismatch_triple = true;
956
957 const t_triple = t.zigTriple(self.allocator) catch unreachable;
958 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
959 mismatch_triple = false;
960 whitelist_item = t;
961 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
962 mismatch_cpu_features = false;
963 break :whitelist_check;
964 } else {
965 break;
966 }
967 }
968 }
969 if (mismatch_triple) {
970 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
971 selected_canonicalized_triple,
972 });
973 for (list) |t| {
974 const t_triple = t.zigTriple(self.allocator) catch unreachable;
975 log.err(" {s}", .{t_triple});
976 }
977 } else {
978 assert(mismatch_cpu_features);
979 const whitelist_cpu = whitelist_item.getCpu();
980 const selected_cpu = selected_target.getCpu();
981 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
982 selected_cpu.model.name,
983 });
984 log.err(" Supported feature Set: ", .{});
985 const all_features = whitelist_cpu.arch.allFeaturesList();
986 var populated_cpu_features = whitelist_cpu.model.features;
987 populated_cpu_features.populateDependencies(all_features);
988 for (all_features) |feature, i_usize| {
989 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
990 const in_cpu_set = populated_cpu_features.isEnabled(i);
991 if (in_cpu_set) {
992 log.err("{s} ", .{feature.name});
993 }
994 }
995 log.err(" Remove: ", .{});
996 for (all_features) |feature, i_usize| {
997 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
998 const in_cpu_set = populated_cpu_features.isEnabled(i);
999 const in_actual_set = selected_cpu.features.isEnabled(i);
1000 if (in_actual_set and !in_cpu_set) {
1001 log.err("{s} ", .{feature.name});
1002 }
1003 }
1004 }
1005 self.markInvalidUserInput();
1006 return args.default_target;
1007 }
1008
1009 return selected_target;
1010}
1011
1012pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool {
1013 const name = self.dupe(name_raw);
1014 const value = self.dupe(value_raw);
1015 const gop = try self.user_input_options.getOrPut(name);
1016 if (!gop.found_existing) {
1017 gop.value_ptr.* = UserInputOption{
1018 .name = name,
1019 .value = .{ .scalar = value },
1020 .used = false,
1021 };
1022 return false;
1023 }
1024
1025 // option already exists
1026 switch (gop.value_ptr.value) {
1027 .scalar => |s| {
1028 // turn it into a list
1029 var list = ArrayList([]const u8).init(self.allocator);
1030 list.append(s) catch unreachable;
1031 list.append(value) catch unreachable;
1032 self.user_input_options.put(name, .{
1033 .name = name,
1034 .value = .{ .list = list },
1035 .used = false,
1036 }) catch unreachable;
1037 },
1038 .list => |*list| {
1039 // append to the list
1040 list.append(value) catch unreachable;
1041 self.user_input_options.put(name, .{
1042 .name = name,
1043 .value = .{ .list = list.* },
1044 .used = false,
1045 }) catch unreachable;
1046 },
1047 .flag => {
1048 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1049 return true;
1050 },
1051 .map => |*map| {
1052 _ = map;
1053 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
1054 return true;
1055 },
1056 }
1057 return false;
1058}
1059
1060pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
1061 const name = self.dupe(name_raw);
1062 const gop = try self.user_input_options.getOrPut(name);
1063 if (!gop.found_existing) {
1064 gop.value_ptr.* = .{
1065 .name = name,
1066 .value = .{ .flag = {} },
1067 .used = false,
1068 };
1069 return false;
1070 }
1071
1072 // option already exists
1073 switch (gop.value_ptr.value) {
1074 .scalar => |s| {
1075 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1076 return true;
1077 },
1078 .list, .map => {
1079 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1080 return true;
1081 },
1082 .flag => {},
1083 }
1084 return false;
1085}
1086
1087fn typeToEnum(comptime T: type) TypeId {
1088 return switch (@typeInfo(T)) {
1089 .Int => .int,
1090 .Float => .float,
1091 .Bool => .bool,
1092 .Enum => .@"enum",
1093 else => switch (T) {
1094 []const u8 => .string,
1095 []const []const u8 => .list,
1096 else => @compileError("Unsupported type: " ++ @typeName(T)),
1097 },
1098 };
1099}
1100
1101fn markInvalidUserInput(self: *Build) void {
1102 self.invalid_user_input = true;
1103}
1104
1105pub fn validateUserInputDidItFail(self: *Build) bool {
1106 // make sure all args are used
1107 var it = self.user_input_options.iterator();
1108 while (it.next()) |entry| {
1109 if (!entry.value_ptr.used) {
1110 log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
1111 self.markInvalidUserInput();
1112 }
1113 }
1114
1115 return self.invalid_user_input;
1116}
1117
1118pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1119 return self.spawnChildEnvMap(null, self.env_map, argv);
1120}
1121
1122fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1123 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1124 for (argv) |arg| {
1125 std.debug.print("{s} ", .{arg});
1126 }
1127 std.debug.print("\n", .{});
1128}
1129
1130pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1131 if (self.verbose) {
1132 printCmd(cwd, argv);
1133 }
1134
1135 if (!std.process.can_spawn)
1136 return error.ExecNotSupported;
1137
1138 var child = std.ChildProcess.init(argv, self.allocator);
1139 child.cwd = cwd;
1140 child.env_map = env_map;
1141
1142 const term = child.spawnAndWait() catch |err| {
1143 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1144 return err;
1145 };
1146
1147 switch (term) {
1148 .Exited => |code| {
1149 if (code != 0) {
1150 log.err("The following command exited with error code {}:", .{code});
1151 printCmd(cwd, argv);
1152 return error.UncleanExit;
1153 }
1154 },
1155 else => {
1156 log.err("The following command terminated unexpectedly:", .{});
1157 printCmd(cwd, argv);
1158
1159 return error.UncleanExit;
1160 },
1161 }
1162}
1163
1164pub fn makePath(self: *Build, path: []const u8) !void {
1165 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1166 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1167 return err;
1168 };
1169}
1170
1171pub fn installArtifact(self: *Build, artifact: *LibExeObjStep) void {
1172 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1173}
1174
1175pub fn addInstallArtifact(self: *Build, artifact: *LibExeObjStep) *InstallArtifactStep {
1176 return InstallArtifactStep.create(self, artifact);
1177}
1178
1179///`dest_rel_path` is relative to prefix path
1180pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1181 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1182}
1183
1184pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
1185 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1186}
1187
1188///`dest_rel_path` is relative to bin path
1189pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1190 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1191}
1192
1193///`dest_rel_path` is relative to lib path
1194pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1195 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1196}
1197
1198/// Output format (BIN vs Intel HEX) determined by filename
1199pub fn installRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1200 const raw = self.addInstallRaw(artifact, dest_filename, options);
1201 self.getInstallStep().dependOn(&raw.step);
1202 return raw;
1203}
1204
1205///`dest_rel_path` is relative to install prefix path
1206pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1207 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1208}
1209
1210///`dest_rel_path` is relative to bin path
1211pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1212 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1213}
1214
1215///`dest_rel_path` is relative to lib path
1216pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1217 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1218}
1219
1220pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1221 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1222}
1223
1224pub fn addInstallRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1225 return InstallRawStep.create(self, artifact, dest_filename, options);
1226}
1227
1228pub fn addInstallFileWithDir(
1229 self: *Build,
1230 source: FileSource,
1231 install_dir: InstallDir,
1232 dest_rel_path: []const u8,
1233) *InstallFileStep {
1234 if (dest_rel_path.len == 0) {
1235 panic("dest_rel_path must be non-empty", .{});
1236 }
1237 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
1238 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1239 return install_step;
1240}
1241
1242pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
1243 const install_step = self.allocator.create(InstallDirStep) catch unreachable;
1244 install_step.* = InstallDirStep.init(self, options);
1245 return install_step;
1246}
1247
1248pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
1249 const file = InstalledFile{
1250 .dir = dir,
1251 .path = dest_rel_path,
1252 };
1253 self.installed_files.append(file.dupe(self)) catch unreachable;
1254}
1255
1256pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
1257 if (self.verbose) {
1258 log.info("cp {s} {s} ", .{ source_path, dest_path });
1259 }
1260 const cwd = fs.cwd();
1261 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1262 if (self.verbose) switch (prev_status) {
1263 .stale => log.info("# installed", .{}),
1264 .fresh => log.info("# up-to-date", .{}),
1265 };
1266}
1267
1268pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1269 if (self.verbose) {
1270 log.info("truncate {s}", .{dest_path});
1271 }
1272 const cwd = fs.cwd();
1273 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1274 error.FileNotFound => blk: {
1275 if (fs.path.dirname(dest_path)) |dirname| {
1276 try cwd.makePath(dirname);
1277 }
1278 break :blk try cwd.createFile(dest_path, .{});
1279 },
1280 else => |e| return e,
1281 };
1282 src_file.close();
1283}
1284
1285pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
1286 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
1287}
1288
1289/// Shorthand for `std.fs.path.join(Build.allocator, paths) catch unreachable`
1290pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
1291 return fs.path.join(self.allocator, paths) catch unreachable;
1292}
1293
1294pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
1295 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
1296}
1297
1298pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1299 // TODO report error for ambiguous situations
1300 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1301 for (self.search_prefixes.items) |search_prefix| {
1302 for (names) |name| {
1303 if (fs.path.isAbsolute(name)) {
1304 return name;
1305 }
1306 const full_path = self.pathJoin(&.{
1307 search_prefix,
1308 "bin",
1309 self.fmt("{s}{s}", .{ name, exe_extension }),
1310 });
1311 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1312 }
1313 }
1314 if (self.env_map.get("PATH")) |PATH| {
1315 for (names) |name| {
1316 if (fs.path.isAbsolute(name)) {
1317 return name;
1318 }
1319 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1320 while (it.next()) |path| {
1321 const full_path = self.pathJoin(&.{
1322 path,
1323 self.fmt("{s}{s}", .{ name, exe_extension }),
1324 });
1325 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1326 }
1327 }
1328 }
1329 for (names) |name| {
1330 if (fs.path.isAbsolute(name)) {
1331 return name;
1332 }
1333 for (paths) |path| {
1334 const full_path = self.pathJoin(&.{
1335 path,
1336 self.fmt("{s}{s}", .{ name, exe_extension }),
1337 });
1338 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1339 }
1340 }
1341 return error.FileNotFound;
1342}
1343
1344pub fn execAllowFail(
1345 self: *Build,
1346 argv: []const []const u8,
1347 out_code: *u8,
1348 stderr_behavior: std.ChildProcess.StdIo,
1349) ExecError![]u8 {
1350 assert(argv.len != 0);
1351
1352 if (!std.process.can_spawn)
1353 return error.ExecNotSupported;
1354
1355 const max_output_size = 400 * 1024;
1356 var child = std.ChildProcess.init(argv, self.allocator);
1357 child.stdin_behavior = .Ignore;
1358 child.stdout_behavior = .Pipe;
1359 child.stderr_behavior = stderr_behavior;
1360 child.env_map = self.env_map;
1361
1362 try child.spawn();
1363
1364 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1365 return error.ReadFailure;
1366 };
1367 errdefer self.allocator.free(stdout);
1368
1369 const term = try child.wait();
1370 switch (term) {
1371 .Exited => |code| {
1372 if (code != 0) {
1373 out_code.* = @truncate(u8, code);
1374 return error.ExitCodeFailure;
1375 }
1376 return stdout;
1377 },
1378 .Signal, .Stopped, .Unknown => |code| {
1379 out_code.* = @truncate(u8, code);
1380 return error.ProcessTerminated;
1381 },
1382 }
1383}
1384
1385pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1386 assert(argv.len != 0);
1387
1388 if (self.verbose) {
1389 printCmd(null, argv);
1390 }
1391
1392 if (!std.process.can_spawn) {
1393 if (src_step) |s| log.err("{s}...", .{s.name});
1394 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1395 printCmd(null, argv);
1396 std.os.abort();
1397 }
1398
1399 var code: u8 = undefined;
1400 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1401 error.ExecNotSupported => {
1402 if (src_step) |s| log.err("{s}...", .{s.name});
1403 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1404 printCmd(null, argv);
1405 std.os.abort();
1406 },
1407 error.FileNotFound => {
1408 if (src_step) |s| log.err("{s}...", .{s.name});
1409 log.err("Unable to spawn the following command: file not found", .{});
1410 printCmd(null, argv);
1411 std.os.exit(@truncate(u8, code));
1412 },
1413 error.ExitCodeFailure => {
1414 if (src_step) |s| log.err("{s}...", .{s.name});
1415 if (self.prominent_compile_errors) {
1416 log.err("The step exited with error code {d}", .{code});
1417 } else {
1418 log.err("The following command exited with error code {d}:", .{code});
1419 printCmd(null, argv);
1420 }
1421
1422 std.os.exit(@truncate(u8, code));
1423 },
1424 error.ProcessTerminated => {
1425 if (src_step) |s| log.err("{s}...", .{s.name});
1426 log.err("The following command terminated unexpectedly:", .{});
1427 printCmd(null, argv);
1428 std.os.exit(@truncate(u8, code));
1429 },
1430 else => |e| return e,
1431 };
1432}
1433
1434pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1435 return self.execFromStep(argv, null);
1436}
1437
1438pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1439 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
1440}
1441
1442pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1443 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1444 const base_dir = switch (dir) {
1445 .prefix => self.install_path,
1446 .bin => self.exe_dir,
1447 .lib => self.lib_dir,
1448 .header => self.h_dir,
1449 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1450 };
1451 return fs.path.resolve(
1452 self.allocator,
1453 &[_][]const u8{ base_dir, dest_rel_path },
1454 ) catch unreachable;
1455}
1456
1457pub const Dependency = struct {
1458 builder: *Build,
1459
1460 pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1461 var found: ?*LibExeObjStep = null;
1462 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1463 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1464 if (mem.eql(u8, inst.artifact.name, name)) {
1465 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1466 found = inst.artifact;
1467 }
1468 }
1469 return found orelse {
1470 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1471 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1472 log.info("available artifact: '{s}'", .{inst.artifact.name});
1473 }
1474 panic("unable to find artifact '{s}'", .{name});
1475 };
1476 }
1477};
1478
1479pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
1480 const build_runner = @import("root");
1481 const deps = build_runner.dependencies;
1482
1483 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1484 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1485 mem.endsWith(u8, decl.name, name) and
1486 decl.name.len == b.dep_prefix.len + name.len)
1487 {
1488 const build_zig = @field(deps.imports, decl.name);
1489 const build_root = @field(deps.build_root, decl.name);
1490 return dependencyInner(b, name, build_root, build_zig, args);
1491 }
1492 }
1493
1494 const full_path = b.pathFromRoot("build.zig.ini");
1495 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1496 std.process.exit(1);
1497}
1498
1499fn dependencyInner(
1500 b: *Build,
1501 name: []const u8,
1502 build_root: []const u8,
1503 comptime build_zig: type,
1504 args: anytype,
1505) *Dependency {
1506 const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1507 sub_builder.runBuild(build_zig) catch unreachable;
1508
1509 if (sub_builder.validateUserInputDidItFail()) {
1510 std.debug.dumpCurrentStackTrace(@returnAddress());
1511 }
1512
1513 const dep = b.allocator.create(Dependency) catch unreachable;
1514 dep.* = .{ .builder = sub_builder };
1515 return dep;
1516}
1517
1518pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
1519 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1520 .Void => build_zig.build(b),
1521 .ErrorUnion => try build_zig.build(b),
1522 else => @compileError("expected return type of build to be 'void' or '!void'"),
1523 }
1524}
1525
1526test "builder.findProgram compiles" {
1527 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1528
1529 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1530 defer arena.deinit();
1531
1532 const builder = try Build.create(
1533 arena.allocator(),
1534 "zig",
1535 "zig-cache",
1536 "zig-cache",
1537 "zig-cache",
1538 );
1539 defer builder.destroy();
1540 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1541}
1542
1543pub const Pkg = struct {
1544 name: []const u8,
1545 source: FileSource,
1546 dependencies: ?[]const Pkg = null,
1547};
1548
1549/// A file that is generated by a build step.
1550/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1551pub const GeneratedFile = struct {
1552 /// The step that generates the file
1553 step: *Step,
1554
1555 /// The path to the generated file. Must be either absolute or relative to the build root.
1556 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1557 path: ?[]const u8 = null,
1558
1559 pub fn getPath(self: GeneratedFile) []const u8 {
1560 return self.path orelse std.debug.panic(
1561 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1562 .{self.step.name},
1563 );
1564 }
1565};
1566
1567/// A file source is a reference to an existing or future file.
1568///
1569pub const FileSource = union(enum) {
1570 /// A plain file path, relative to build root or absolute.
1571 path: []const u8,
1572
1573 /// A file that is generated by an interface. Those files usually are
1574 /// not available until built by a build step.
1575 generated: *const GeneratedFile,
1576
1577 /// Returns a new file source that will have a relative path to the build root guaranteed.
1578 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1579 pub fn relative(path: []const u8) FileSource {
1580 std.debug.assert(!std.fs.path.isAbsolute(path));
1581 return FileSource{ .path = path };
1582 }
1583
1584 /// Returns a string that can be shown to represent the file source.
1585 /// Either returns the path or `"generated"`.
1586 pub fn getDisplayName(self: FileSource) []const u8 {
1587 return switch (self) {
1588 .path => self.path,
1589 .generated => "generated",
1590 };
1591 }
1592
1593 /// Adds dependencies this file source implies to the given step.
1594 pub fn addStepDependencies(self: FileSource, other_step: *Step) void {
1595 switch (self) {
1596 .path => {},
1597 .generated => |gen| other_step.dependOn(gen.step),
1598 }
1599 }
1600
1601 /// Should only be called during make(), returns a path relative to the build root or absolute.
1602 pub fn getPath(self: FileSource, builder: *Build) []const u8 {
1603 const path = switch (self) {
1604 .path => |p| builder.pathFromRoot(p),
1605 .generated => |gen| gen.getPath(),
1606 };
1607 return path;
1608 }
1609
1610 /// Duplicates the file source for a given builder.
1611 pub fn dupe(self: FileSource, b: *Build) FileSource {
1612 return switch (self) {
1613 .path => |p| .{ .path = b.dupePath(p) },
1614 .generated => |gen| .{ .generated = gen },
1615 };
1616 }
1617};
1618
1619/// Allocates a new string for assigning a value to a named macro.
1620/// If the value is omitted, it is set to 1.
1621/// `name` and `value` need not live longer than the function call.
1622pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1623 var macro = allocator.alloc(
1624 u8,
1625 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1626 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1627 mem.copy(u8, macro, name);
1628 if (value) |value_slice| {
1629 macro[name.len] = '=';
1630 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1631 }
1632 return macro;
1633}
1634
1635/// deprecated: use `InstallDirStep.Options`
1636pub const InstallDirectoryOptions = InstallDirStep.Options;
1637
1638pub const VcpkgRoot = union(VcpkgRootStatus) {
1639 unattempted: void,
1640 not_found: void,
1641 found: []const u8,
1642};
1643
1644pub const VcpkgRootStatus = enum {
1645 unattempted,
1646 not_found,
1647 found,
1648};
1649
1650pub const InstallDir = union(enum) {
1651 prefix: void,
1652 lib: void,
1653 bin: void,
1654 header: void,
1655 /// A path relative to the prefix
1656 custom: []const u8,
1657
1658 /// Duplicates the install directory including the path if set to custom.
1659 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
1660 if (self == .custom) {
1661 // Written with this temporary to avoid RLS problems
1662 const duped_path = builder.dupe(self.custom);
1663 return .{ .custom = duped_path };
1664 } else {
1665 return self;
1666 }
1667 }
1668};
1669
1670pub const InstalledFile = struct {
1671 dir: InstallDir,
1672 path: []const u8,
1673
1674 /// Duplicates the installed file path and directory.
1675 pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile {
1676 return .{
1677 .dir = self.dir.dupe(builder),
1678 .path = builder.dupe(self.path),
1679 };
1680 }
1681};
1682
1683pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1684 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1685 const all_features = cpu.arch.allFeaturesList();
1686 var populated_cpu_features = cpu.model.features;
1687 populated_cpu_features.populateDependencies(all_features);
1688
1689 if (populated_cpu_features.eql(cpu.features)) {
1690 // The CPU name alone is sufficient.
1691 return cpu.model.name;
1692 } else {
1693 var mcpu_buffer = ArrayList(u8).init(allocator);
1694 try mcpu_buffer.appendSlice(cpu.model.name);
1695
1696 for (all_features) |feature, i_usize| {
1697 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1698 const in_cpu_set = populated_cpu_features.isEnabled(i);
1699 const in_actual_set = cpu.features.isEnabled(i);
1700 if (in_cpu_set and !in_actual_set) {
1701 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1702 } else if (!in_cpu_set and in_actual_set) {
1703 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1704 }
1705 }
1706
1707 return try mcpu_buffer.toOwnedSlice();
1708 }
1709}
1710
1711test "dupePkg()" {
1712 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1713
1714 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1715 defer arena.deinit();
1716 var builder = try Build.create(
1717 arena.allocator(),
1718 "test",
1719 "test",
1720 "test",
1721 "test",
1722 );
1723 defer builder.destroy();
1724
1725 var pkg_dep = Pkg{
1726 .name = "pkg_dep",
1727 .source = .{ .path = "/not/a/pkg_dep.zig" },
1728 };
1729 var pkg_top = Pkg{
1730 .name = "pkg_top",
1731 .source = .{ .path = "/not/a/pkg_top.zig" },
1732 .dependencies = &[_]Pkg{pkg_dep},
1733 };
1734 const duped = builder.dupePkg(pkg_top);
1735
1736 const original_deps = pkg_top.dependencies.?;
1737 const dupe_deps = duped.dependencies.?;
1738
1739 // probably the same top level package details
1740 try std.testing.expectEqualStrings(pkg_top.name, duped.name);
1741
1742 // probably the same dependencies
1743 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1744 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1745
1746 // could segfault otherwise if pointers in duplicated package's fields are
1747 // the same as those in stack allocated package's fields
1748 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1749 try std.testing.expect(duped.name.ptr != pkg_top.name.ptr);
1750 try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr);
1751 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1752 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1753}
1754
1755test {
1756 _ = CheckFileStep;
1757 _ = CheckObjectStep;
1758 _ = EmulatableRunStep;
1759 _ = FmtStep;
1760 _ = InstallArtifactStep;
1761 _ = InstallDirStep;
1762 _ = InstallFileStep;
1763 _ = InstallRawStep;
1764 _ = LibExeObjStep;
1765 _ = LogStep;
1766 _ = OptionsStep;
1767 _ = RemoveDirStep;
1768 _ = RunStep;
1769 _ = TranslateCStep;
1770 _ = WriteFileStep;
1771}
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 unreachable;
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 unreachable;
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.LibExeObjStep).?;
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 unreachable;
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 unreachable;
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 unreachable;
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 unreachable;
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 unreachable;
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/ConfigHeaderStep.zig created+287
......@@ -0,0 +1,287 @@
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 switch (@typeInfo(field.type)) {
65 .Null => {
66 try self.values.put(field.name, .undef);
67 },
68 .Void => {
69 try self.values.put(field.name, .defined);
70 },
71 .Bool => {
72 try self.values.put(field.name, .{ .boolean = @field(values, field.name) });
73 },
74 .ComptimeInt => {
75 try self.values.put(field.name, .{ .int = @field(values, field.name) });
76 },
77 .EnumLiteral => {
78 try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) });
79 },
80 .Pointer => |ptr| {
81 switch (@typeInfo(ptr.child)) {
82 .Array => |array| {
83 if (ptr.size == .One and array.child == u8) {
84 try self.values.put(field.name, .{ .string = @field(values, field.name) });
85 continue;
86 }
87 },
88 else => {},
89 }
90
91 @compileError("unsupported ConfigHeaderStep value type: " ++
92 @typeName(field.type));
93 },
94 else => @compileError("unsupported ConfigHeaderStep value type: " ++
95 @typeName(field.type)),
96 }
97 }
98}
99
100fn make(step: *Step) !void {
101 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
102 const gpa = self.builder.allocator;
103 const src_path = self.source.getPath(self.builder);
104 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
105
106 // The cache is used here not really as a way to speed things up - because writing
107 // the data to a file would probably be very fast - but as a way to find a canonical
108 // location to put build artifacts.
109
110 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
111 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
112
113 // TODO port the cache system from the compiler to zig std lib. Until then
114 // we construct the path directly, and no "cache hit" detection happens;
115 // the files are always written.
116 // Note there is very similar code over in WriteFileStep
117 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
118 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
119 // random bytes when ConfigHeaderStep implementation is modified in a
120 // non-backwards-compatible way.
121 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
122 hash.update(self.source.getDisplayName());
123 hash.update(contents);
124
125 var digest: [16]u8 = undefined;
126 hash.final(&digest);
127 var hash_basename: [digest.len * 2]u8 = undefined;
128 _ = std.fmt.bufPrint(
129 &hash_basename,
130 "{s}",
131 .{std.fmt.fmtSliceHexLower(&digest)},
132 ) catch unreachable;
133
134 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
135 self.builder.cache_root, "o", &hash_basename,
136 });
137 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
138 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
139 return err;
140 };
141 defer dir.close();
142
143 var values_copy = try self.values.clone();
144 defer values_copy.deinit();
145
146 var output = std.ArrayList(u8).init(gpa);
147 defer output.deinit();
148 try output.ensureTotalCapacity(contents.len);
149
150 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
151
152 switch (self.style) {
153 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
154 .cmake => try render_cmake(contents, &output, &values_copy, src_path),
155 }
156
157 try dir.writeFile(self.output_basename, output.items);
158}
159
160fn render_autoconf(
161 contents: []const u8,
162 output: *std.ArrayList(u8),
163 values_copy: *std.StringHashMap(Value),
164 src_path: []const u8,
165) !void {
166 var any_errors = false;
167 var line_index: u32 = 0;
168 var line_it = std.mem.split(u8, contents, "\n");
169 while (line_it.next()) |line| : (line_index += 1) {
170 if (!std.mem.startsWith(u8, line, "#")) {
171 try output.appendSlice(line);
172 try output.appendSlice("\n");
173 continue;
174 }
175 var it = std.mem.tokenize(u8, line[1..], " \t\r");
176 const undef = it.next().?;
177 if (!std.mem.eql(u8, undef, "undef")) {
178 try output.appendSlice(line);
179 try output.appendSlice("\n");
180 continue;
181 }
182 const name = it.rest();
183 const kv = values_copy.fetchRemove(name) orelse {
184 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
185 src_path, line_index + 1, name,
186 });
187 any_errors = true;
188 continue;
189 };
190 try renderValue(output, name, kv.value);
191 }
192
193 {
194 var it = values_copy.iterator();
195 while (it.next()) |entry| {
196 const name = entry.key_ptr.*;
197 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
198 }
199 }
200
201 if (any_errors) {
202 return error.HeaderConfigFailed;
203 }
204}
205
206fn render_cmake(
207 contents: []const u8,
208 output: *std.ArrayList(u8),
209 values_copy: *std.StringHashMap(Value),
210 src_path: []const u8,
211) !void {
212 var any_errors = false;
213 var line_index: u32 = 0;
214 var line_it = std.mem.split(u8, contents, "\n");
215 while (line_it.next()) |line| : (line_index += 1) {
216 if (!std.mem.startsWith(u8, line, "#")) {
217 try output.appendSlice(line);
218 try output.appendSlice("\n");
219 continue;
220 }
221 var it = std.mem.tokenize(u8, line[1..], " \t\r");
222 const cmakedefine = it.next().?;
223 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
224 try output.appendSlice(line);
225 try output.appendSlice("\n");
226 continue;
227 }
228 const name = it.next() orelse {
229 std.debug.print("{s}:{d}: error: missing define name\n", .{
230 src_path, line_index + 1,
231 });
232 any_errors = true;
233 continue;
234 };
235 const kv = values_copy.fetchRemove(name) orelse {
236 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
237 src_path, line_index + 1, name,
238 });
239 any_errors = true;
240 continue;
241 };
242 try renderValue(output, name, kv.value);
243 }
244
245 {
246 var it = values_copy.iterator();
247 while (it.next()) |entry| {
248 const name = entry.key_ptr.*;
249 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
250 }
251 }
252
253 if (any_errors) {
254 return error.HeaderConfigFailed;
255 }
256}
257
258fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
259 switch (value) {
260 .undef => {
261 try output.appendSlice("/* #undef ");
262 try output.appendSlice(name);
263 try output.appendSlice(" */\n");
264 },
265 .defined => {
266 try output.appendSlice("#define ");
267 try output.appendSlice(name);
268 try output.appendSlice("\n");
269 },
270 .boolean => |b| {
271 try output.appendSlice("#define ");
272 try output.appendSlice(name);
273 try output.appendSlice(" ");
274 try output.appendSlice(if (b) "true\n" else "false\n");
275 },
276 .int => |i| {
277 try output.writer().print("#define {s} {d}\n", .{ name, i });
278 },
279 .ident => |ident| {
280 try output.writer().print("#define {s} {s}\n", .{ name, ident });
281 },
282 .string => |string| {
283 // TODO: use C-specific escaping instead of zig string literals
284 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
285 },
286 }
287}
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 LibExeObjStep = std.Build.LibExeObjStep;
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: *LibExeObjStep,
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: *LibExeObjStep) *EmulatableRunStep {
49 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
50 const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
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 unreachable;
158 const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
159 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
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 unreachable;
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 unreachable,
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 LibExeObjStep = std.Build.LibExeObjStep;
4const InstallDir = std.Build.InstallDir;
5const InstallArtifactStep = @This();
6
7pub const base_id = .install_artifact;
8
9step: Step,
10builder: *std.Build,
11artifact: *LibExeObjStep,
12dest_dir: InstallDir,
13pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,
15
16pub fn create(builder: *std.Build, artifact: *LibExeObjStep) *InstallArtifactStep {
17 if (artifact.install_step) |s| return s;
18
19 const self = builder.allocator.create(InstallArtifactStep) catch unreachable;
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 LibExeObjStep.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 LibExeObjStep = std.Build.LibExeObjStep;
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: *LibExeObjStep,
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: *LibExeObjStep,
44 dest_filename: []const u8,
45 options: CreateOptions,
46) *InstallRawStep {
47 const self = builder.allocator.create(InstallRawStep) catch unreachable;
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 fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable;
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/LibExeObjStep.zig created+2045
......@@ -0,0 +1,2045 @@
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 LibExeObjStep = @This();
31
32pub const base_id = .lib_exe_obj;
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: *LibExeObjStep,
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: *LibExeObjStep,
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) *LibExeObjStep {
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(LibExeObjStep) catch unreachable;
316 self.* = LibExeObjStep{
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 = undefined, // populated in computeOutFileNames
368 };
369 self.computeOutFileNames();
370 if (root_src) |rs| rs.addStepDependencies(&self.step);
371 return self;
372}
373
374fn computeOutFileNames(self: *LibExeObjStep) void {
375 self.target_info = NativeTargetInfo.detect(self.target) catch
376 unreachable;
377
378 const target = self.target_info.target;
379
380 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
381 .root_name = self.name,
382 .target = target,
383 .output_mode = switch (self.kind) {
384 .lib => .Lib,
385 .obj => .Obj,
386 .exe, .@"test", .test_exe => .Exe,
387 },
388 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
389 .dynamic => .Dynamic,
390 .static => .Static,
391 }) else null,
392 .version = self.version,
393 }) catch unreachable;
394
395 if (self.kind == .lib) {
396 if (self.linkage != null and self.linkage.? == .static) {
397 self.out_lib_filename = self.out_filename;
398 } else if (self.version) |version| {
399 if (target.isDarwin()) {
400 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
401 self.name,
402 version.major,
403 });
404 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
405 self.out_lib_filename = self.out_filename;
406 } else if (target.os.tag == .windows) {
407 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
408 } else {
409 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
410 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
411 self.out_lib_filename = self.out_filename;
412 }
413 } else {
414 if (target.isDarwin()) {
415 self.out_lib_filename = self.out_filename;
416 } else if (target.os.tag == .windows) {
417 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
418 } else {
419 self.out_lib_filename = self.out_filename;
420 }
421 }
422 if (self.output_dir != null) {
423 self.output_lib_path_source.path = self.builder.pathJoin(
424 &.{ self.output_dir.?, self.out_lib_filename },
425 );
426 }
427 }
428}
429
430pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
431 self.output_dir = self.builder.dupePath(dir);
432}
433
434pub fn install(self: *LibExeObjStep) void {
435 self.builder.installArtifact(self);
436}
437
438pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
439 return self.builder.installRaw(self, dest_filename, options);
440}
441
442pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
443 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
444 a.builder.getInstallStep().dependOn(&install_file.step);
445 a.installed_headers.append(&install_file.step) catch unreachable;
446}
447
448pub fn installHeadersDirectory(
449 a: *LibExeObjStep,
450 src_dir_path: []const u8,
451 dest_rel_path: []const u8,
452) void {
453 return installHeadersDirectoryOptions(a, .{
454 .source_dir = src_dir_path,
455 .install_dir = .header,
456 .install_subdir = dest_rel_path,
457 });
458}
459
460pub fn installHeadersDirectoryOptions(
461 a: *LibExeObjStep,
462 options: std.Build.InstallDirStep.Options,
463) void {
464 const install_dir = a.builder.addInstallDirectory(options);
465 a.builder.getInstallStep().dependOn(&install_dir.step);
466 a.installed_headers.append(&install_dir.step) catch unreachable;
467}
468
469pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
470 assert(l.kind == .lib);
471 const install_step = a.builder.getInstallStep();
472 // Copy each element from installed_headers, modifying the builder
473 // to be the new parent's builder.
474 for (l.installed_headers.items) |step| {
475 const step_copy = switch (step.id) {
476 inline .install_file, .install_dir => |id| blk: {
477 const T = id.Type();
478 const ptr = a.builder.allocator.create(T) catch unreachable;
479 ptr.* = step.cast(T).?.*;
480 ptr.override_source_builder = ptr.builder;
481 ptr.builder = a.builder;
482 break :blk &ptr.step;
483 },
484 else => unreachable,
485 };
486 a.installed_headers.append(step_copy) catch unreachable;
487 install_step.dependOn(step_copy);
488 }
489 a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
490}
491
492/// Creates a `RunStep` with an executable built with `addExecutable`.
493/// Add command line arguments with `addArg`.
494pub fn run(exe: *LibExeObjStep) *RunStep {
495 assert(exe.kind == .exe or exe.kind == .test_exe);
496
497 // It doesn't have to be native. We catch that if you actually try to run it.
498 // Consider that this is declarative; the run step may not be run unless a user
499 // option is supplied.
500 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
501 run_step.addArtifactArg(exe);
502
503 if (exe.kind == .test_exe) {
504 run_step.addArg(exe.builder.zig_exe);
505 }
506
507 if (exe.vcpkg_bin_path) |path| {
508 run_step.addPathDir(path);
509 }
510
511 return run_step;
512}
513
514/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
515/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
516/// When a binary cannot be ran through emulation or the option is disabled, a warning
517/// will be printed and the binary will *NOT* be ran.
518pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
519 assert(exe.kind == .exe or exe.kind == .test_exe);
520
521 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
522 if (exe.vcpkg_bin_path) |path| {
523 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
524 }
525 return run_step;
526}
527
528pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
529 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
530}
531
532pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
533 self.linker_script = source.dupe(self.builder);
534 source.addStepDependencies(&self.step);
535}
536
537pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
538 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
539}
540
541pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
542 self.frameworks.put(self.builder.dupe(framework_name), .{
543 .needed = true,
544 }) catch unreachable;
545}
546
547pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
548 self.frameworks.put(self.builder.dupe(framework_name), .{
549 .weak = true,
550 }) catch unreachable;
551}
552
553/// Returns whether the library, executable, or object depends on a particular system library.
554pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
555 if (isLibCLibrary(name)) {
556 return self.is_linking_libc;
557 }
558 if (isLibCppLibrary(name)) {
559 return self.is_linking_libcpp;
560 }
561 for (self.link_objects.items) |link_object| {
562 switch (link_object) {
563 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
564 else => continue,
565 }
566 }
567 return false;
568}
569
570pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
571 assert(lib.kind == .lib);
572 self.linkLibraryOrObject(lib);
573}
574
575pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
576 return self.kind == .lib and self.linkage == Linkage.dynamic;
577}
578
579pub fn isStaticLibrary(self: *LibExeObjStep) bool {
580 return self.kind == .lib and self.linkage != Linkage.dynamic;
581}
582
583pub fn producesPdbFile(self: *LibExeObjStep) bool {
584 if (!self.target.isWindows() and !self.target.isUefi()) return false;
585 if (self.target.getObjectFormat() == .c) return false;
586 if (self.strip == true) return false;
587 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
588}
589
590pub fn linkLibC(self: *LibExeObjStep) void {
591 self.is_linking_libc = true;
592}
593
594pub fn linkLibCpp(self: *LibExeObjStep) void {
595 self.is_linking_libcpp = true;
596}
597
598/// If the value is omitted, it is set to 1.
599/// `name` and `value` need not live longer than the function call.
600pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
601 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
602 self.c_macros.append(macro) catch unreachable;
603}
604
605/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
606pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
607 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
608}
609
610/// This one has no integration with anything, it just puts -lname on the command line.
611/// Prefer to use `linkSystemLibrary` instead.
612pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
613 self.link_objects.append(.{
614 .system_lib = .{
615 .name = self.builder.dupe(name),
616 .needed = false,
617 .weak = false,
618 .use_pkg_config = .no,
619 },
620 }) catch unreachable;
621}
622
623/// This one has no integration with anything, it just puts -needed-lname on the command line.
624/// Prefer to use `linkSystemLibraryNeeded` instead.
625pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
626 self.link_objects.append(.{
627 .system_lib = .{
628 .name = self.builder.dupe(name),
629 .needed = true,
630 .weak = false,
631 .use_pkg_config = .no,
632 },
633 }) catch unreachable;
634}
635
636/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
637/// command line. Prefer to use `linkSystemLibraryWeak` instead.
638pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
639 self.link_objects.append(.{
640 .system_lib = .{
641 .name = self.builder.dupe(name),
642 .needed = false,
643 .weak = true,
644 .use_pkg_config = .no,
645 },
646 }) catch unreachable;
647}
648
649/// This links against a system library, exclusively using pkg-config to find the library.
650/// Prefer to use `linkSystemLibrary` instead.
651pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
652 self.link_objects.append(.{
653 .system_lib = .{
654 .name = self.builder.dupe(lib_name),
655 .needed = false,
656 .weak = false,
657 .use_pkg_config = .force,
658 },
659 }) catch unreachable;
660}
661
662/// This links against a system library, exclusively using pkg-config to find the library.
663/// Prefer to use `linkSystemLibraryNeeded` instead.
664pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
665 self.link_objects.append(.{
666 .system_lib = .{
667 .name = self.builder.dupe(lib_name),
668 .needed = true,
669 .weak = false,
670 .use_pkg_config = .force,
671 },
672 }) catch unreachable;
673}
674
675/// Run pkg-config for the given library name and parse the output, returning the arguments
676/// that should be passed to zig to link the given library.
677pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
678 const pkg_name = match: {
679 // First we have to map the library name to pkg config name. Unfortunately,
680 // there are several examples where this is not straightforward:
681 // -lSDL2 -> pkg-config sdl2
682 // -lgdk-3 -> pkg-config gdk-3.0
683 // -latk-1.0 -> pkg-config atk
684 const pkgs = try getPkgConfigList(self.builder);
685
686 // Exact match means instant winner.
687 for (pkgs) |pkg| {
688 if (mem.eql(u8, pkg.name, lib_name)) {
689 break :match pkg.name;
690 }
691 }
692
693 // Next we'll try ignoring case.
694 for (pkgs) |pkg| {
695 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
696 break :match pkg.name;
697 }
698 }
699
700 // Now try appending ".0".
701 for (pkgs) |pkg| {
702 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
703 if (pos != 0) continue;
704 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
705 break :match pkg.name;
706 }
707 }
708 }
709
710 // Trimming "-1.0".
711 if (mem.endsWith(u8, lib_name, "-1.0")) {
712 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
713 for (pkgs) |pkg| {
714 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
715 break :match pkg.name;
716 }
717 }
718 }
719
720 return error.PackageNotFound;
721 };
722
723 var code: u8 = undefined;
724 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
725 "pkg-config",
726 pkg_name,
727 "--cflags",
728 "--libs",
729 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
730 error.ProcessTerminated => return error.PkgConfigCrashed,
731 error.ExecNotSupported => return error.PkgConfigFailed,
732 error.ExitCodeFailure => return error.PkgConfigFailed,
733 error.FileNotFound => return error.PkgConfigNotInstalled,
734 error.ChildExecFailed => return error.PkgConfigFailed,
735 else => return err,
736 };
737
738 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
739 defer zig_args.deinit();
740
741 var it = mem.tokenize(u8, stdout, " \r\n\t");
742 while (it.next()) |tok| {
743 if (mem.eql(u8, tok, "-I")) {
744 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
745 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
746 } else if (mem.startsWith(u8, tok, "-I")) {
747 try zig_args.append(tok);
748 } else if (mem.eql(u8, tok, "-L")) {
749 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
750 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
751 } else if (mem.startsWith(u8, tok, "-L")) {
752 try zig_args.append(tok);
753 } else if (mem.eql(u8, tok, "-l")) {
754 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
755 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
756 } else if (mem.startsWith(u8, tok, "-l")) {
757 try zig_args.append(tok);
758 } else if (mem.eql(u8, tok, "-D")) {
759 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
760 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
761 } else if (mem.startsWith(u8, tok, "-D")) {
762 try zig_args.append(tok);
763 } else if (self.builder.verbose) {
764 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
765 }
766 }
767
768 return zig_args.toOwnedSlice();
769}
770
771pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
772 self.linkSystemLibraryInner(name, .{});
773}
774
775pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
776 self.linkSystemLibraryInner(name, .{ .needed = true });
777}
778
779pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
780 self.linkSystemLibraryInner(name, .{ .weak = true });
781}
782
783fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
784 needed: bool = false,
785 weak: bool = false,
786}) void {
787 if (isLibCLibrary(name)) {
788 self.linkLibC();
789 return;
790 }
791 if (isLibCppLibrary(name)) {
792 self.linkLibCpp();
793 return;
794 }
795
796 self.link_objects.append(.{
797 .system_lib = .{
798 .name = self.builder.dupe(name),
799 .needed = opts.needed,
800 .weak = opts.weak,
801 .use_pkg_config = .yes,
802 },
803 }) catch unreachable;
804}
805
806pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
807 assert(self.kind == .@"test" or self.kind == .test_exe);
808 self.name_prefix = self.builder.dupe(text);
809}
810
811pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
812 assert(self.kind == .@"test" or self.kind == .test_exe);
813 self.filter = if (text) |t| self.builder.dupe(t) else null;
814}
815
816pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
817 assert(self.kind == .@"test" or self.kind == .test_exe);
818 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
819}
820
821/// Handy when you have many C/C++ source files and want them all to have the same flags.
822pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
823 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
824
825 const files_copy = self.builder.dupeStrings(files);
826 const flags_copy = self.builder.dupeStrings(flags);
827
828 c_source_files.* = .{
829 .files = files_copy,
830 .flags = flags_copy,
831 };
832 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
833}
834
835pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
836 self.addCSourceFileSource(.{
837 .args = flags,
838 .source = .{ .path = file },
839 });
840}
841
842pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
843 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
844 c_source_file.* = source.dupe(self.builder);
845 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
846 source.source.addStepDependencies(&self.step);
847}
848
849pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
850 self.verbose_link = value;
851}
852
853pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
854 self.verbose_cc = value;
855}
856
857pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
858 self.override_lib_dir = self.builder.dupePath(dir_path);
859}
860
861pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
862 self.main_pkg_path = self.builder.dupePath(dir_path);
863}
864
865pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
866 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
867}
868
869/// Returns the generated executable, library or object file.
870/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
871pub fn getOutputSource(self: *LibExeObjStep) FileSource {
872 return FileSource{ .generated = &self.output_path_source };
873}
874
875/// Returns the generated import library. This function can only be called for libraries.
876pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
877 assert(self.kind == .lib);
878 return FileSource{ .generated = &self.output_lib_path_source };
879}
880
881/// Returns the generated header file.
882/// This function can only be called for libraries or object files which have `emit_h` set.
883pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
884 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
885 assert(self.emit_h);
886 return FileSource{ .generated = &self.output_h_path_source };
887}
888
889/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
890pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
891 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
892 assert(self.target.isWindows() or self.target.isUefi());
893 return FileSource{ .generated = &self.output_pdb_path_source };
894}
895
896pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
897 self.link_objects.append(.{
898 .assembly_file = .{ .path = self.builder.dupe(path) },
899 }) catch unreachable;
900}
901
902pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
903 const source_duped = source.dupe(self.builder);
904 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
905 source_duped.addStepDependencies(&self.step);
906}
907
908pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
909 self.addObjectFileSource(.{ .path = source_file });
910}
911
912pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
913 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
914 source.addStepDependencies(&self.step);
915}
916
917pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
918 assert(obj.kind == .obj);
919 self.linkLibraryOrObject(obj);
920}
921
922pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
923pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
924pub const addLibPath = @compileError("deprecated, use addLibraryPath");
925pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
926
927pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
928 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
929}
930
931pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
932 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
933}
934
935pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
936 self.step.dependOn(&config_header.step);
937 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
938}
939
940pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
941 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
942}
943
944pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
945 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
946}
947
948pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
949 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
950}
951
952pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
953 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
954 self.addRecursiveBuildDeps(package);
955}
956
957pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
958 self.addPackage(options.getPackage(package_name));
959}
960
961fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
962 package.source.addStepDependencies(&self.step);
963 if (package.dependencies) |deps| {
964 for (deps) |dep| {
965 self.addRecursiveBuildDeps(dep);
966 }
967 }
968}
969
970pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
971 self.addPackage(Pkg{
972 .name = self.builder.dupe(name),
973 .source = .{ .path = self.builder.dupe(pkg_index_path) },
974 });
975}
976
977/// If Vcpkg was found on the system, it will be added to include and lib
978/// paths for the specified target.
979pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
980 // Ideally in the Unattempted case we would call the function recursively
981 // after findVcpkgRoot and have only one switch statement, but the compiler
982 // cannot resolve the error set.
983 switch (self.builder.vcpkg_root) {
984 .unattempted => {
985 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
986 VcpkgRoot{ .found = root }
987 else
988 .not_found;
989 },
990 .not_found => return error.VcpkgNotFound,
991 .found => {},
992 }
993
994 switch (self.builder.vcpkg_root) {
995 .unattempted => unreachable,
996 .not_found => return error.VcpkgNotFound,
997 .found => |root| {
998 const allocator = self.builder.allocator;
999 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1000 defer self.builder.allocator.free(triplet);
1001
1002 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1003 errdefer allocator.free(include_path);
1004 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1005
1006 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1007 try self.lib_paths.append(lib_path);
1008
1009 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1010 },
1011 }
1012}
1013
1014pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
1015 assert(self.kind == .@"test");
1016 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
1017 for (args) |arg, i| {
1018 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1019 }
1020 self.exec_cmd_args = duped_args;
1021}
1022
1023fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
1024 self.step.dependOn(&other.step);
1025 self.link_objects.append(.{ .other_step = other }) catch unreachable;
1026 self.include_dirs.append(.{ .other_step = other }) catch unreachable;
1027}
1028
1029fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1030 const builder = self.builder;
1031
1032 try zig_args.append("--pkg-begin");
1033 try zig_args.append(pkg.name);
1034 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1035
1036 if (pkg.dependencies) |dependencies| {
1037 for (dependencies) |sub_pkg| {
1038 try self.makePackageCmd(sub_pkg, zig_args);
1039 }
1040 }
1041
1042 try zig_args.append("--pkg-end");
1043}
1044
1045fn make(step: *Step) !void {
1046 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1047 const builder = self.builder;
1048
1049 if (self.root_src == null and self.link_objects.items.len == 0) {
1050 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1051 return error.NeedAnObject;
1052 }
1053
1054 var zig_args = ArrayList([]const u8).init(builder.allocator);
1055 defer zig_args.deinit();
1056
1057 zig_args.append(builder.zig_exe) catch unreachable;
1058
1059 const cmd = switch (self.kind) {
1060 .lib => "build-lib",
1061 .exe => "build-exe",
1062 .obj => "build-obj",
1063 .@"test" => "test",
1064 .test_exe => "test",
1065 };
1066 zig_args.append(cmd) catch unreachable;
1067
1068 if (builder.color != .auto) {
1069 try zig_args.append("--color");
1070 try zig_args.append(@tagName(builder.color));
1071 }
1072
1073 if (builder.reference_trace) |some| {
1074 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1075 }
1076
1077 try addFlag(&zig_args, "LLVM", self.use_llvm);
1078 try addFlag(&zig_args, "LLD", self.use_lld);
1079
1080 if (self.target.ofmt) |ofmt| {
1081 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1082 }
1083
1084 if (self.entry_symbol_name) |entry| {
1085 try zig_args.append("--entry");
1086 try zig_args.append(entry);
1087 }
1088
1089 if (self.stack_size) |stack_size| {
1090 try zig_args.append("--stack");
1091 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1092 }
1093
1094 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1095
1096 // We will add link objects from transitive dependencies, but we want to keep
1097 // all link objects in the same order provided.
1098 // This array is used to keep self.link_objects immutable.
1099 var transitive_deps: TransitiveDeps = .{
1100 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1101 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1102 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1103 .is_linking_libcpp = self.is_linking_libcpp,
1104 .is_linking_libc = self.is_linking_libc,
1105 .frameworks = &self.frameworks,
1106 };
1107
1108 try transitive_deps.seen_steps.put(&self.step, {});
1109 try transitive_deps.add(self.link_objects.items);
1110
1111 var prev_has_extra_flags = false;
1112
1113 for (transitive_deps.link_objects.items) |link_object| {
1114 switch (link_object) {
1115 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1116
1117 .other_step => |other| switch (other.kind) {
1118 .exe => @panic("Cannot link with an executable build artifact"),
1119 .test_exe => @panic("Cannot link with an executable build artifact"),
1120 .@"test" => @panic("Cannot link with a test"),
1121 .obj => {
1122 try zig_args.append(other.getOutputSource().getPath(builder));
1123 },
1124 .lib => l: {
1125 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1126 // Avoid putting a static library inside a static library.
1127 break :l;
1128 }
1129
1130 const full_path_lib = other.getOutputLibSource().getPath(builder);
1131 try zig_args.append(full_path_lib);
1132
1133 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1134 if (fs.path.dirname(full_path_lib)) |dirname| {
1135 try zig_args.append("-rpath");
1136 try zig_args.append(dirname);
1137 }
1138 }
1139 },
1140 },
1141
1142 .system_lib => |system_lib| {
1143 const prefix: []const u8 = prefix: {
1144 if (system_lib.needed) break :prefix "-needed-l";
1145 if (system_lib.weak) {
1146 if (self.target.isDarwin()) break :prefix "-weak-l";
1147 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1148 }
1149 break :prefix "-l";
1150 };
1151 switch (system_lib.use_pkg_config) {
1152 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1153 .yes, .force => {
1154 if (self.runPkgConfig(system_lib.name)) |args| {
1155 try zig_args.appendSlice(args);
1156 } else |err| switch (err) {
1157 error.PkgConfigInvalidOutput,
1158 error.PkgConfigCrashed,
1159 error.PkgConfigFailed,
1160 error.PkgConfigNotInstalled,
1161 error.PackageNotFound,
1162 => switch (system_lib.use_pkg_config) {
1163 .yes => {
1164 // pkg-config failed, so fall back to linking the library
1165 // by name directly.
1166 try zig_args.append(builder.fmt("{s}{s}", .{
1167 prefix,
1168 system_lib.name,
1169 }));
1170 },
1171 .force => {
1172 panic("pkg-config failed for library {s}", .{system_lib.name});
1173 },
1174 .no => unreachable,
1175 },
1176
1177 else => |e| return e,
1178 }
1179 },
1180 }
1181 },
1182
1183 .assembly_file => |asm_file| {
1184 if (prev_has_extra_flags) {
1185 try zig_args.append("-extra-cflags");
1186 try zig_args.append("--");
1187 prev_has_extra_flags = false;
1188 }
1189 try zig_args.append(asm_file.getPath(builder));
1190 },
1191
1192 .c_source_file => |c_source_file| {
1193 if (c_source_file.args.len == 0) {
1194 if (prev_has_extra_flags) {
1195 try zig_args.append("-cflags");
1196 try zig_args.append("--");
1197 prev_has_extra_flags = false;
1198 }
1199 } else {
1200 try zig_args.append("-cflags");
1201 for (c_source_file.args) |arg| {
1202 try zig_args.append(arg);
1203 }
1204 try zig_args.append("--");
1205 }
1206 try zig_args.append(c_source_file.source.getPath(builder));
1207 },
1208
1209 .c_source_files => |c_source_files| {
1210 if (c_source_files.flags.len == 0) {
1211 if (prev_has_extra_flags) {
1212 try zig_args.append("-cflags");
1213 try zig_args.append("--");
1214 prev_has_extra_flags = false;
1215 }
1216 } else {
1217 try zig_args.append("-cflags");
1218 for (c_source_files.flags) |flag| {
1219 try zig_args.append(flag);
1220 }
1221 try zig_args.append("--");
1222 }
1223 for (c_source_files.files) |file| {
1224 try zig_args.append(builder.pathFromRoot(file));
1225 }
1226 },
1227 }
1228 }
1229
1230 if (transitive_deps.is_linking_libcpp) {
1231 try zig_args.append("-lc++");
1232 }
1233
1234 if (transitive_deps.is_linking_libc) {
1235 try zig_args.append("-lc");
1236 }
1237
1238 if (self.image_base) |image_base| {
1239 try zig_args.append("--image-base");
1240 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1241 }
1242
1243 if (self.filter) |filter| {
1244 try zig_args.append("--test-filter");
1245 try zig_args.append(filter);
1246 }
1247
1248 if (self.test_evented_io) {
1249 try zig_args.append("--test-evented-io");
1250 }
1251
1252 if (self.name_prefix.len != 0) {
1253 try zig_args.append("--test-name-prefix");
1254 try zig_args.append(self.name_prefix);
1255 }
1256
1257 if (self.test_runner) |test_runner| {
1258 try zig_args.append("--test-runner");
1259 try zig_args.append(builder.pathFromRoot(test_runner));
1260 }
1261
1262 for (builder.debug_log_scopes) |log_scope| {
1263 try zig_args.append("--debug-log");
1264 try zig_args.append(log_scope);
1265 }
1266
1267 if (builder.debug_compile_errors) {
1268 try zig_args.append("--debug-compile-errors");
1269 }
1270
1271 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
1272 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
1273 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
1274 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
1275 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
1276 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
1277
1278 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1279 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1280 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1281 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1282 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1283 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1284 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1285
1286 if (self.emit_h) try zig_args.append("-femit-h");
1287
1288 try addFlag(&zig_args, "strip", self.strip);
1289 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1290
1291 switch (self.compress_debug_sections) {
1292 .none => {},
1293 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1294 }
1295
1296 if (self.link_eh_frame_hdr) {
1297 try zig_args.append("--eh-frame-hdr");
1298 }
1299 if (self.link_emit_relocs) {
1300 try zig_args.append("--emit-relocs");
1301 }
1302 if (self.link_function_sections) {
1303 try zig_args.append("-ffunction-sections");
1304 }
1305 if (self.link_gc_sections) |x| {
1306 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1307 }
1308 if (self.linker_allow_shlib_undefined) |x| {
1309 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1310 }
1311 if (self.link_z_notext) {
1312 try zig_args.append("-z");
1313 try zig_args.append("notext");
1314 }
1315 if (!self.link_z_relro) {
1316 try zig_args.append("-z");
1317 try zig_args.append("norelro");
1318 }
1319 if (self.link_z_lazy) {
1320 try zig_args.append("-z");
1321 try zig_args.append("lazy");
1322 }
1323 if (self.link_z_common_page_size) |size| {
1324 try zig_args.append("-z");
1325 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1326 }
1327 if (self.link_z_max_page_size) |size| {
1328 try zig_args.append("-z");
1329 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1330 }
1331
1332 if (self.libc_file) |libc_file| {
1333 try zig_args.append("--libc");
1334 try zig_args.append(libc_file.getPath(builder));
1335 } else if (builder.libc_file) |libc_file| {
1336 try zig_args.append("--libc");
1337 try zig_args.append(libc_file);
1338 }
1339
1340 switch (self.optimize) {
1341 .Debug => {}, // Skip since it's the default.
1342 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable,
1343 }
1344
1345 try zig_args.append("--cache-dir");
1346 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1347
1348 try zig_args.append("--global-cache-dir");
1349 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1350
1351 zig_args.append("--name") catch unreachable;
1352 zig_args.append(self.name) catch unreachable;
1353
1354 if (self.linkage) |some| switch (some) {
1355 .dynamic => try zig_args.append("-dynamic"),
1356 .static => try zig_args.append("-static"),
1357 };
1358 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1359 if (self.version) |version| {
1360 zig_args.append("--version") catch unreachable;
1361 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
1362 }
1363
1364 if (self.target.isDarwin()) {
1365 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1366 self.target.libPrefix(),
1367 self.name,
1368 self.target.dynamicLibSuffix(),
1369 });
1370 try zig_args.append("-install_name");
1371 try zig_args.append(install_name);
1372 }
1373 }
1374
1375 if (self.entitlements) |entitlements| {
1376 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1377 }
1378 if (self.pagezero_size) |pagezero_size| {
1379 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1380 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1381 }
1382 if (self.search_strategy) |strat| switch (strat) {
1383 .paths_first => try zig_args.append("-search_paths_first"),
1384 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1385 };
1386 if (self.headerpad_size) |headerpad_size| {
1387 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1388 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1389 }
1390 if (self.headerpad_max_install_names) {
1391 try zig_args.append("-headerpad_max_install_names");
1392 }
1393 if (self.dead_strip_dylibs) {
1394 try zig_args.append("-dead_strip_dylibs");
1395 }
1396
1397 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1398 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1399 if (self.disable_stack_probing) {
1400 try zig_args.append("-fno-stack-check");
1401 }
1402 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1403 if (self.red_zone) |red_zone| {
1404 if (red_zone) {
1405 try zig_args.append("-mred-zone");
1406 } else {
1407 try zig_args.append("-mno-red-zone");
1408 }
1409 }
1410 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1411 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1412
1413 if (self.disable_sanitize_c) {
1414 try zig_args.append("-fno-sanitize-c");
1415 }
1416 if (self.sanitize_thread) {
1417 try zig_args.append("-fsanitize-thread");
1418 }
1419 if (self.rdynamic) {
1420 try zig_args.append("-rdynamic");
1421 }
1422 if (self.import_memory) {
1423 try zig_args.append("--import-memory");
1424 }
1425 if (self.import_symbols) {
1426 try zig_args.append("--import-symbols");
1427 }
1428 if (self.import_table) {
1429 try zig_args.append("--import-table");
1430 }
1431 if (self.export_table) {
1432 try zig_args.append("--export-table");
1433 }
1434 if (self.initial_memory) |initial_memory| {
1435 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1436 }
1437 if (self.max_memory) |max_memory| {
1438 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1439 }
1440 if (self.shared_memory) {
1441 try zig_args.append("--shared-memory");
1442 }
1443 if (self.global_base) |global_base| {
1444 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1445 }
1446
1447 if (self.code_model != .default) {
1448 try zig_args.append("-mcmodel");
1449 try zig_args.append(@tagName(self.code_model));
1450 }
1451 if (self.wasi_exec_model) |model| {
1452 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1453 }
1454 for (self.export_symbol_names) |symbol_name| {
1455 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1456 }
1457
1458 if (!self.target.isNative()) {
1459 try zig_args.appendSlice(&.{
1460 "-target", try self.target.zigTriple(builder.allocator),
1461 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
1462 });
1463
1464 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1465 try zig_args.append("--dynamic-linker");
1466 try zig_args.append(dynamic_linker);
1467 }
1468 }
1469
1470 if (self.linker_script) |linker_script| {
1471 try zig_args.append("--script");
1472 try zig_args.append(linker_script.getPath(builder));
1473 }
1474
1475 if (self.version_script) |version_script| {
1476 try zig_args.append("--version-script");
1477 try zig_args.append(builder.pathFromRoot(version_script));
1478 }
1479
1480 if (self.kind == .@"test") {
1481 if (self.exec_cmd_args) |exec_cmd_args| {
1482 for (exec_cmd_args) |cmd_arg| {
1483 if (cmd_arg) |arg| {
1484 try zig_args.append("--test-cmd");
1485 try zig_args.append(arg);
1486 } else {
1487 try zig_args.append("--test-cmd-bin");
1488 }
1489 }
1490 } else {
1491 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1492
1493 switch (builder.host.getExternalExecutor(self.target_info, .{
1494 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1495 .link_libc = transitive_deps.is_linking_libc,
1496 })) {
1497 .native => {},
1498 .bad_dl, .bad_os_or_cpu => {
1499 try zig_args.append("--test-no-exec");
1500 },
1501 .rosetta => if (builder.enable_rosetta) {
1502 try zig_args.append("--test-cmd-bin");
1503 } else {
1504 try zig_args.append("--test-no-exec");
1505 },
1506 .qemu => |bin_name| ok: {
1507 if (builder.enable_qemu) qemu: {
1508 const glibc_dir_arg = if (need_cross_glibc)
1509 builder.glibc_runtimes_dir orelse break :qemu
1510 else
1511 null;
1512 try zig_args.append("--test-cmd");
1513 try zig_args.append(bin_name);
1514 if (glibc_dir_arg) |dir| {
1515 // TODO look into making this a call to `linuxTriple`. This
1516 // needs the directory to be called "i686" rather than
1517 // "x86" which is why we do it manually here.
1518 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1519 const cpu_arch = self.target.getCpuArch();
1520 const os_tag = self.target.getOsTag();
1521 const abi = self.target.getAbi();
1522 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1523 "i686"
1524 else
1525 @tagName(cpu_arch);
1526 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1527 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1528 });
1529
1530 try zig_args.append("--test-cmd");
1531 try zig_args.append("-L");
1532 try zig_args.append("--test-cmd");
1533 try zig_args.append(full_dir);
1534 }
1535 try zig_args.append("--test-cmd-bin");
1536 break :ok;
1537 }
1538 try zig_args.append("--test-no-exec");
1539 },
1540 .wine => |bin_name| if (builder.enable_wine) {
1541 try zig_args.append("--test-cmd");
1542 try zig_args.append(bin_name);
1543 try zig_args.append("--test-cmd-bin");
1544 } else {
1545 try zig_args.append("--test-no-exec");
1546 },
1547 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1548 try zig_args.append("--test-cmd");
1549 try zig_args.append(bin_name);
1550 try zig_args.append("--test-cmd");
1551 try zig_args.append("--dir=.");
1552 try zig_args.append("--test-cmd-bin");
1553 } else {
1554 try zig_args.append("--test-no-exec");
1555 },
1556 .darling => |bin_name| if (builder.enable_darling) {
1557 try zig_args.append("--test-cmd");
1558 try zig_args.append(bin_name);
1559 try zig_args.append("--test-cmd-bin");
1560 } else {
1561 try zig_args.append("--test-no-exec");
1562 },
1563 }
1564 }
1565 } else if (self.kind == .test_exe) {
1566 try zig_args.append("--test-no-exec");
1567 }
1568
1569 for (self.packages.items) |pkg| {
1570 try self.makePackageCmd(pkg, &zig_args);
1571 }
1572
1573 for (self.include_dirs.items) |include_dir| {
1574 switch (include_dir) {
1575 .raw_path => |include_path| {
1576 try zig_args.append("-I");
1577 try zig_args.append(builder.pathFromRoot(include_path));
1578 },
1579 .raw_path_system => |include_path| {
1580 if (builder.sysroot != null) {
1581 try zig_args.append("-iwithsysroot");
1582 } else {
1583 try zig_args.append("-isystem");
1584 }
1585
1586 const resolved_include_path = builder.pathFromRoot(include_path);
1587
1588 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1589 // We need to check for disk designator and strip it out from dir path so
1590 // that zig/clang can concat resolved_include_path with sysroot.
1591 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1592
1593 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1594 break :blk resolved_include_path[where + disk_designator.len ..];
1595 }
1596
1597 break :blk resolved_include_path;
1598 } else resolved_include_path;
1599
1600 try zig_args.append(common_include_path);
1601 },
1602 .other_step => |other| {
1603 if (other.emit_h) {
1604 const h_path = other.getOutputHSource().getPath(builder);
1605 try zig_args.append("-isystem");
1606 try zig_args.append(fs.path.dirname(h_path).?);
1607 }
1608 if (other.installed_headers.items.len > 0) {
1609 for (other.installed_headers.items) |install_step| {
1610 try install_step.make();
1611 }
1612 try zig_args.append("-I");
1613 try zig_args.append(builder.pathJoin(&.{
1614 other.builder.install_prefix, "include",
1615 }));
1616 }
1617 },
1618 .config_header_step => |config_header| {
1619 try zig_args.append("-I");
1620 try zig_args.append(config_header.output_dir);
1621 },
1622 }
1623 }
1624
1625 for (self.lib_paths.items) |lib_path| {
1626 try zig_args.append("-L");
1627 try zig_args.append(lib_path);
1628 }
1629
1630 for (self.rpaths.items) |rpath| {
1631 try zig_args.append("-rpath");
1632 try zig_args.append(rpath);
1633 }
1634
1635 for (self.c_macros.items) |c_macro| {
1636 try zig_args.append("-D");
1637 try zig_args.append(c_macro);
1638 }
1639
1640 if (self.target.isDarwin()) {
1641 for (self.framework_dirs.items) |dir| {
1642 if (builder.sysroot != null) {
1643 try zig_args.append("-iframeworkwithsysroot");
1644 } else {
1645 try zig_args.append("-iframework");
1646 }
1647 try zig_args.append(dir);
1648 try zig_args.append("-F");
1649 try zig_args.append(dir);
1650 }
1651
1652 var it = self.frameworks.iterator();
1653 while (it.next()) |entry| {
1654 const name = entry.key_ptr.*;
1655 const info = entry.value_ptr.*;
1656 if (info.needed) {
1657 zig_args.append("-needed_framework") catch unreachable;
1658 } else if (info.weak) {
1659 zig_args.append("-weak_framework") catch unreachable;
1660 } else {
1661 zig_args.append("-framework") catch unreachable;
1662 }
1663 zig_args.append(name) catch unreachable;
1664 }
1665 } else {
1666 if (self.framework_dirs.items.len > 0) {
1667 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1668 }
1669
1670 if (self.frameworks.count() > 0) {
1671 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1672 }
1673 }
1674
1675 if (builder.sysroot) |sysroot| {
1676 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1677 }
1678
1679 for (builder.search_prefixes.items) |search_prefix| {
1680 try zig_args.append("-L");
1681 try zig_args.append(builder.pathJoin(&.{
1682 search_prefix, "lib",
1683 }));
1684 try zig_args.append("-I");
1685 try zig_args.append(builder.pathJoin(&.{
1686 search_prefix, "include",
1687 }));
1688 }
1689
1690 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1691 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1692 try addFlag(&zig_args, "build-id", self.build_id);
1693
1694 if (self.override_lib_dir) |dir| {
1695 try zig_args.append("--zig-lib-dir");
1696 try zig_args.append(builder.pathFromRoot(dir));
1697 } else if (builder.override_lib_dir) |dir| {
1698 try zig_args.append("--zig-lib-dir");
1699 try zig_args.append(builder.pathFromRoot(dir));
1700 }
1701
1702 if (self.main_pkg_path) |dir| {
1703 try zig_args.append("--main-pkg-path");
1704 try zig_args.append(builder.pathFromRoot(dir));
1705 }
1706
1707 try addFlag(&zig_args, "PIC", self.force_pic);
1708 try addFlag(&zig_args, "PIE", self.pie);
1709 try addFlag(&zig_args, "lto", self.want_lto);
1710
1711 if (self.subsystem) |subsystem| {
1712 try zig_args.append("--subsystem");
1713 try zig_args.append(switch (subsystem) {
1714 .Console => "console",
1715 .Windows => "windows",
1716 .Posix => "posix",
1717 .Native => "native",
1718 .EfiApplication => "efi_application",
1719 .EfiBootServiceDriver => "efi_boot_service_driver",
1720 .EfiRom => "efi_rom",
1721 .EfiRuntimeDriver => "efi_runtime_driver",
1722 });
1723 }
1724
1725 try zig_args.append("--enable-cache");
1726
1727 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1728 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1729 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1730 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1731 var args_length: usize = 0;
1732 for (zig_args.items) |arg| {
1733 args_length += arg.len + 1; // +1 to account for null terminator
1734 }
1735 if (args_length >= 30 * 1024) {
1736 const args_dir = try fs.path.join(
1737 builder.allocator,
1738 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1739 );
1740 try std.fs.cwd().makePath(args_dir);
1741
1742 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1743 defer args_arena.deinit();
1744
1745 const args_to_escape = zig_args.items[2..];
1746 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1747
1748 arg_blk: for (args_to_escape) |arg| {
1749 for (arg) |c, arg_idx| {
1750 if (c == '\\' or c == '"') {
1751 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1752 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1753 const writer = escaped.writer();
1754 writer.writeAll(arg[0..arg_idx]) catch unreachable;
1755 for (arg[arg_idx..]) |to_escape| {
1756 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1757 try writer.writeByte(to_escape);
1758 }
1759 escaped_args.appendAssumeCapacity(escaped.items);
1760 continue :arg_blk;
1761 }
1762 }
1763 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1764 }
1765
1766 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1767 // other zig build commands running in parallel.
1768 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1769 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1770
1771 var args_hash: [Sha256.digest_length]u8 = undefined;
1772 Sha256.hash(args, &args_hash, .{});
1773 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1774 _ = try std.fmt.bufPrint(
1775 &args_hex_hash,
1776 "{s}",
1777 .{std.fmt.fmtSliceHexLower(&args_hash)},
1778 );
1779
1780 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1781 try std.fs.cwd().writeFile(args_file, args);
1782
1783 zig_args.shrinkRetainingCapacity(2);
1784 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1785 }
1786
1787 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1788 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1789
1790 if (self.output_dir) |output_dir| {
1791 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1792 defer src_dir.close();
1793
1794 // Create the output directory if it doesn't exist.
1795 try std.fs.cwd().makePath(output_dir);
1796
1797 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1798 defer dest_dir.close();
1799
1800 var it = src_dir.iterate();
1801 while (try it.next()) |entry| {
1802 // The compiler can put these files into the same directory, but we don't
1803 // want to copy them over.
1804 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1805 mem.eql(u8, entry.name, "libs.txt") or
1806 mem.eql(u8, entry.name, "builtin.zig") or
1807 mem.eql(u8, entry.name, "zld.id") or
1808 mem.eql(u8, entry.name, "lld.id")) continue;
1809
1810 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1811 }
1812 } else {
1813 self.output_dir = build_output_dir;
1814 }
1815
1816 // This will ensure all output filenames will now have the output_dir available!
1817 self.computeOutFileNames();
1818
1819 // Update generated files
1820 if (self.output_dir != null) {
1821 self.output_path_source.path = builder.pathJoin(
1822 &.{ self.output_dir.?, self.out_filename },
1823 );
1824
1825 if (self.emit_h) {
1826 self.output_h_path_source.path = builder.pathJoin(
1827 &.{ self.output_dir.?, self.out_h_filename },
1828 );
1829 }
1830
1831 if (self.target.isWindows() or self.target.isUefi()) {
1832 self.output_pdb_path_source.path = builder.pathJoin(
1833 &.{ self.output_dir.?, self.out_pdb_filename },
1834 );
1835 }
1836 }
1837
1838 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1839 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1840 }
1841}
1842
1843fn isLibCLibrary(name: []const u8) bool {
1844 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1845 for (libc_libraries) |libc_lib_name| {
1846 if (mem.eql(u8, name, libc_lib_name))
1847 return true;
1848 }
1849 return false;
1850}
1851
1852fn isLibCppLibrary(name: []const u8) bool {
1853 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1854 for (libcpp_libraries) |libcpp_lib_name| {
1855 if (mem.eql(u8, name, libcpp_lib_name))
1856 return true;
1857 }
1858 return false;
1859}
1860
1861/// Returned slice must be freed by the caller.
1862fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1863 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1864 defer allocator.free(appdata_path);
1865
1866 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1867 defer allocator.free(path_file);
1868
1869 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1870 defer file.close();
1871
1872 const size = @intCast(usize, try file.getEndPos());
1873 const vcpkg_path = try allocator.alloc(u8, size);
1874 const size_read = try file.read(vcpkg_path);
1875 std.debug.assert(size == size_read);
1876
1877 return vcpkg_path;
1878}
1879
1880pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
1881 const out_dir = fs.path.dirname(output_path) orelse ".";
1882 const out_basename = fs.path.basename(output_path);
1883 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1884 const major_only_path = fs.path.join(
1885 allocator,
1886 &[_][]const u8{ out_dir, filename_major_only },
1887 ) catch unreachable;
1888 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1889 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1890 return err;
1891 };
1892 // sym link for libfoo.so to libfoo.so.1
1893 const name_only_path = fs.path.join(
1894 allocator,
1895 &[_][]const u8{ out_dir, filename_name_only },
1896 ) catch unreachable;
1897 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1898 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1899 return err;
1900 };
1901}
1902
1903fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1904 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1905 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1906 errdefer list.deinit();
1907 var line_it = mem.tokenize(u8, stdout, "\r\n");
1908 while (line_it.next()) |line| {
1909 if (mem.trim(u8, line, " \t").len == 0) continue;
1910 var tok_it = mem.tokenize(u8, line, " \t");
1911 try list.append(PkgConfigPkg{
1912 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1913 .desc = tok_it.rest(),
1914 });
1915 }
1916 return list.toOwnedSlice();
1917}
1918
1919fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
1920 if (self.pkg_config_pkg_list) |res| {
1921 return res;
1922 }
1923 var code: u8 = undefined;
1924 if (execPkgConfigList(self, &code)) |list| {
1925 self.pkg_config_pkg_list = list;
1926 return list;
1927 } else |err| {
1928 const result = switch (err) {
1929 error.ProcessTerminated => error.PkgConfigCrashed,
1930 error.ExecNotSupported => error.PkgConfigFailed,
1931 error.ExitCodeFailure => error.PkgConfigFailed,
1932 error.FileNotFound => error.PkgConfigNotInstalled,
1933 error.InvalidName => error.PkgConfigNotInstalled,
1934 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
1935 error.ChildExecFailed => error.PkgConfigFailed,
1936 else => return err,
1937 };
1938 self.pkg_config_pkg_list = result;
1939 return result;
1940 }
1941}
1942
1943test "addPackage" {
1944 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1945
1946 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1947 defer arena.deinit();
1948
1949 var builder = try std.Build.create(
1950 arena.allocator(),
1951 "test",
1952 "test",
1953 "test",
1954 "test",
1955 );
1956 defer builder.destroy();
1957
1958 const pkg_dep = Pkg{
1959 .name = "pkg_dep",
1960 .source = .{ .path = "/not/a/pkg_dep.zig" },
1961 };
1962 const pkg_top = Pkg{
1963 .name = "pkg_dep",
1964 .source = .{ .path = "/not/a/pkg_top.zig" },
1965 .dependencies = &[_]Pkg{pkg_dep},
1966 };
1967
1968 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
1969 exe.addPackage(pkg_top);
1970
1971 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
1972
1973 const dupe = exe.packages.items[0];
1974 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1975}
1976
1977fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
1978 const cond = opt orelse return;
1979 try args.ensureUnusedCapacity(1);
1980 if (cond) {
1981 args.appendAssumeCapacity("-f" ++ name);
1982 } else {
1983 args.appendAssumeCapacity("-fno-" ++ name);
1984 }
1985}
1986
1987const TransitiveDeps = struct {
1988 link_objects: ArrayList(LinkObject),
1989 seen_system_libs: StringHashMap(void),
1990 seen_steps: std.AutoHashMap(*const Step, void),
1991 is_linking_libcpp: bool,
1992 is_linking_libc: bool,
1993 frameworks: *StringHashMap(FrameworkLinkInfo),
1994
1995 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
1996 try td.link_objects.ensureUnusedCapacity(link_objects.len);
1997
1998 for (link_objects) |link_object| {
1999 try td.link_objects.append(link_object);
2000 switch (link_object) {
2001 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2002 else => {},
2003 }
2004 }
2005 }
2006
2007 fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
2008 // Inherit dependency on libc and libc++
2009 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2010 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2011
2012 // Inherit dependencies on darwin frameworks
2013 if (!dyn) {
2014 var it = other.frameworks.iterator();
2015 while (it.next()) |framework| {
2016 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2017 }
2018 }
2019
2020 // Inherit dependencies on system libraries and static libraries.
2021 for (other.link_objects.items) |other_link_object| {
2022 switch (other_link_object) {
2023 .system_lib => |system_lib| {
2024 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2025 continue;
2026
2027 if (dyn)
2028 continue;
2029
2030 try td.link_objects.append(other_link_object);
2031 },
2032 .other_step => |inner_other| {
2033 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2034 continue;
2035
2036 if (!dyn)
2037 try td.link_objects.append(other_link_object);
2038
2039 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2040 },
2041 else => continue,
2042 }
2043 }
2044 }
2045};
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+363
......@@ -0,0 +1,363 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const fs = std.fs;
4const Step = std.Build.Step;
5const GeneratedFile = std.Build.GeneratedFile;
6const LibExeObjStep = std.Build.LibExeObjStep;
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 unreachable;
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 const out = self.contents.writer();
38 switch (T) {
39 []const []const u8 => {
40 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
41 for (value) |slice| {
42 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
43 }
44 out.writeAll("};\n") catch unreachable;
45 return;
46 },
47 [:0]const u8 => {
48 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
49 return;
50 },
51 []const u8 => {
52 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
53 return;
54 },
55 ?[:0]const u8 => {
56 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
57 if (value) |payload| {
58 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
59 } else {
60 out.writeAll("null;\n") catch unreachable;
61 }
62 return;
63 },
64 ?[]const u8 => {
65 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
66 if (value) |payload| {
67 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
68 } else {
69 out.writeAll("null;\n") catch unreachable;
70 }
71 return;
72 },
73 std.builtin.Version => {
74 out.print(
75 \\pub const {}: @import("std").builtin.Version = .{{
76 \\ .major = {d},
77 \\ .minor = {d},
78 \\ .patch = {d},
79 \\}};
80 \\
81 , .{
82 std.zig.fmtId(name),
83
84 value.major,
85 value.minor,
86 value.patch,
87 }) catch unreachable;
88 return;
89 },
90 std.SemanticVersion => {
91 out.print(
92 \\pub const {}: @import("std").SemanticVersion = .{{
93 \\ .major = {d},
94 \\ .minor = {d},
95 \\ .patch = {d},
96 \\
97 , .{
98 std.zig.fmtId(name),
99
100 value.major,
101 value.minor,
102 value.patch,
103 }) catch unreachable;
104 if (value.pre) |some| {
105 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
106 }
107 if (value.build) |some| {
108 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
109 }
110 out.writeAll("};\n") catch unreachable;
111 return;
112 },
113 else => {},
114 }
115 switch (@typeInfo(T)) {
116 .Enum => |enum_info| {
117 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
118 inline for (enum_info.fields) |field| {
119 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
120 }
121 out.writeAll("};\n") catch unreachable;
122 out.print("pub const {}: {s} = {s}.{s};\n", .{
123 std.zig.fmtId(name),
124 std.zig.fmtId(@typeName(T)),
125 std.zig.fmtId(@typeName(T)),
126 std.zig.fmtId(@tagName(value)),
127 }) catch unreachable;
128 return;
129 },
130 else => {},
131 }
132 out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable;
133 printLiteral(out, value, 0) catch unreachable;
134 out.writeAll(";\n") catch unreachable;
135}
136
137// TODO: non-recursive?
138fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
139 const T = @TypeOf(val);
140 switch (@typeInfo(T)) {
141 .Array => {
142 try out.print("{s} {{\n", .{@typeName(T)});
143 for (val) |item| {
144 try out.writeByteNTimes(' ', indent + 4);
145 try printLiteral(out, item, indent + 4);
146 try out.writeAll(",\n");
147 }
148 try out.writeByteNTimes(' ', indent);
149 try out.writeAll("}");
150 },
151 .Pointer => |p| {
152 if (p.size != .Slice) {
153 @compileError("Non-slice pointers are not yet supported in build options");
154 }
155 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
156 for (val) |item| {
157 try out.writeByteNTimes(' ', indent + 4);
158 try printLiteral(out, item, indent + 4);
159 try out.writeAll(",\n");
160 }
161 try out.writeByteNTimes(' ', indent);
162 try out.writeAll("}");
163 },
164 .Optional => {
165 if (val) |inner| {
166 return printLiteral(out, inner, indent);
167 } else {
168 return out.writeAll("null");
169 }
170 },
171 .Void,
172 .Bool,
173 .Int,
174 .ComptimeInt,
175 .Float,
176 .Null,
177 => try out.print("{any}", .{val}),
178 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
179 }
180}
181
182/// The value is the path in the cache dir.
183/// Adds a dependency automatically.
184pub fn addOptionFileSource(
185 self: *OptionsStep,
186 name: []const u8,
187 source: FileSource,
188) void {
189 self.file_source_args.append(.{
190 .name = name,
191 .source = source.dupe(self.builder),
192 }) catch unreachable;
193 source.addStepDependencies(&self.step);
194}
195
196/// The value is the path in the cache dir.
197/// Adds a dependency automatically.
198pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
199 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
200 self.step.dependOn(&artifact.step);
201}
202
203pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg {
204 return .{ .name = package_name, .source = self.getSource() };
205}
206
207pub fn getSource(self: *OptionsStep) FileSource {
208 return .{ .generated = &self.generated_file };
209}
210
211fn make(step: *Step) !void {
212 const self = @fieldParentPtr(OptionsStep, "step", step);
213
214 for (self.artifact_args.items) |item| {
215 self.addOption(
216 []const u8,
217 item.name,
218 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
219 );
220 }
221
222 for (self.file_source_args.items) |item| {
223 self.addOption(
224 []const u8,
225 item.name,
226 item.source.getPath(self.builder),
227 );
228 }
229
230 const options_directory = self.builder.pathFromRoot(
231 try fs.path.join(
232 self.builder.allocator,
233 &[_][]const u8{ self.builder.cache_root, "options" },
234 ),
235 );
236
237 try fs.cwd().makePath(options_directory);
238
239 const options_file = try fs.path.join(
240 self.builder.allocator,
241 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
242 );
243
244 try fs.cwd().writeFile(options_file, self.contents.items);
245
246 self.generated_file.path = options_file;
247}
248
249fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
250 // This implementation is copied from `WriteFileStep.make`
251
252 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
253
254 // Random bytes to make OptionsStep unique. Refresh this with
255 // new random bytes when OptionsStep implementation is modified
256 // in a non-backwards-compatible way.
257 hash.update("yL0Ya4KkmcCjBlP8");
258 hash.update(self.contents.items);
259
260 var digest: [48]u8 = undefined;
261 hash.final(&digest);
262 var hash_basename: [64]u8 = undefined;
263 _ = fs.base64_encoder.encode(&hash_basename, &digest);
264 return hash_basename;
265}
266
267const OptionArtifactArg = struct {
268 name: []const u8,
269 artifact: *LibExeObjStep,
270};
271
272const OptionFileSourceArg = struct {
273 name: []const u8,
274 source: FileSource,
275};
276
277test "OptionsStep" {
278 if (builtin.os.tag == .wasi) return error.SkipZigTest;
279
280 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
281 defer arena.deinit();
282 var builder = try std.Build.create(
283 arena.allocator(),
284 "test",
285 "test",
286 "test",
287 "test",
288 );
289 defer builder.destroy();
290
291 const options = builder.addOptions();
292
293 // TODO this regressed at some point
294 //const KeywordEnum = enum {
295 // @"0.8.1",
296 //};
297
298 const nested_array = [2][2]u16{
299 [2]u16{ 300, 200 },
300 [2]u16{ 300, 200 },
301 };
302 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
303
304 options.addOption(usize, "option1", 1);
305 options.addOption(?usize, "option2", null);
306 options.addOption(?usize, "option3", 3);
307 options.addOption(comptime_int, "option4", 4);
308 options.addOption([]const u8, "string", "zigisthebest");
309 options.addOption(?[]const u8, "optional_string", null);
310 options.addOption([2][2]u16, "nested_array", nested_array);
311 options.addOption([]const []const u16, "nested_slice", nested_slice);
312 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
313 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
314 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
315
316 try std.testing.expectEqualStrings(
317 \\pub const option1: usize = 1;
318 \\pub const option2: ?usize = null;
319 \\pub const option3: ?usize = 3;
320 \\pub const option4: comptime_int = 4;
321 \\pub const string: []const u8 = "zigisthebest";
322 \\pub const optional_string: ?[]const u8 = null;
323 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
324 \\ [2]u16 {
325 \\ 300,
326 \\ 200,
327 \\ },
328 \\ [2]u16 {
329 \\ 300,
330 \\ 200,
331 \\ },
332 \\};
333 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
334 \\ &[_]u16 {
335 \\ 300,
336 \\ 200,
337 \\ },
338 \\ &[_]u16 {
339 \\ 300,
340 \\ 200,
341 \\ },
342 \\};
343 //\\pub const KeywordEnum = enum {
344 //\\ @"0.8.1",
345 //\\};
346 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
347 \\pub const version: @import("std").builtin.Version = .{
348 \\ .major = 0,
349 \\ .minor = 1,
350 \\ .patch = 2,
351 \\};
352 \\pub const semantic_version: @import("std").SemanticVersion = .{
353 \\ .major = 0,
354 \\ .minor = 1,
355 \\ .patch = 2,
356 \\ .pre = "foo",
357 \\ .build = "bar",
358 \\};
359 \\
360 , options.contents.items);
361
362 _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
363}
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 LibExeObjStep = std.Build.LibExeObjStep;
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: *LibExeObjStep,
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 unreachable;
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: *LibExeObjStep) void {
70 self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
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 unreachable;
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 unreachable;
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 unreachable;
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 unreachable;
111 } else {
112 env_map.put(key, builder.dupePath(search_path)) catch unreachable;
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 unreachable;
128 env_map.* = process.getEnvMap(allocator) catch unreachable;
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 unreachable;
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 = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
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 = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
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: *LibExeObjStep) 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: *LibExeObjStep) 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 lib_exe_obj,
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 .lib_exe_obj => Build.LibExeObjStep,
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 unreachable,
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 unreachable;
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 LibExeObjStep = std.Build.LibExeObjStep;
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 unreachable;
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: ?LibExeObjStep.Linkage = null,
55};
56
57/// Creates a step to build an executable from the translated source.
58pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *LibExeObjStep {
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 unreachable;
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 unreachable;
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 unreachable;
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 = fs.path.join(
133 self.builder.allocator,
134 &[_][]const u8{ self.output_dir.?, self.out_basename },
135 ) catch unreachable;
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 unreachable;
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 = fs.path.join(
110 self.builder.allocator,
111 &[_][]const u8{ self.output_dir, node.data.basename },
112 ) catch unreachable;
113 }
114 }
115}
lib/std/build.zig deleted-1863
......@@ -1,1863 +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 /// zig lib dir
77 override_lib_dir: ?[]const u8,
78 vcpkg_root: VcpkgRoot = .unattempted,
79 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
80 args: ?[][]const u8 = null,
81 debug_log_scopes: []const []const u8 = &.{},
82 debug_compile_errors: bool = false,
83
84 /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
85 enable_darling: bool = false,
86 /// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
87 enable_qemu: bool = false,
88 /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
89 enable_rosetta: bool = false,
90 /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
91 enable_wasmtime: bool = false,
92 /// Use system Wine installation to run cross compiled Windows build artifacts.
93 enable_wine: bool = false,
94 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
95 /// this will be the directory $glibc-build-dir/install/glibcs
96 /// Given the example of the aarch64 target, this is the directory
97 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
98 glibc_runtimes_dir: ?[]const u8 = null,
99
100 /// Information about the native target. Computed before build() is invoked.
101 host: NativeTargetInfo,
102
103 dep_prefix: []const u8 = "",
104
105 pub const ExecError = error{
106 ReadFailure,
107 ExitCodeFailure,
108 ProcessTerminated,
109 ExecNotSupported,
110 } || std.ChildProcess.SpawnError;
111
112 pub const PkgConfigError = error{
113 PkgConfigCrashed,
114 PkgConfigFailed,
115 PkgConfigNotInstalled,
116 PkgConfigInvalidOutput,
117 };
118
119 pub const PkgConfigPkg = struct {
120 name: []const u8,
121 desc: []const u8,
122 };
123
124 pub const CStd = enum {
125 C89,
126 C99,
127 C11,
128 };
129
130 const UserInputOptionsMap = StringHashMap(UserInputOption);
131 const AvailableOptionsMap = StringHashMap(AvailableOption);
132
133 const AvailableOption = struct {
134 name: []const u8,
135 type_id: TypeId,
136 description: []const u8,
137 /// If the `type_id` is `enum` this provides the list of enum options
138 enum_options: ?[]const []const u8,
139 };
140
141 const UserInputOption = struct {
142 name: []const u8,
143 value: UserValue,
144 used: bool,
145 };
146
147 const UserValue = union(enum) {
148 flag: void,
149 scalar: []const u8,
150 list: ArrayList([]const u8),
151 map: StringHashMap(*const UserValue),
152 };
153
154 const TypeId = enum {
155 bool,
156 int,
157 float,
158 @"enum",
159 string,
160 list,
161 };
162
163 const TopLevelStep = struct {
164 pub const base_id = .top_level;
165
166 step: Step,
167 description: []const u8,
168 };
169
170 pub const DirList = struct {
171 lib_dir: ?[]const u8 = null,
172 exe_dir: ?[]const u8 = null,
173 include_dir: ?[]const u8 = null,
174 };
175
176 pub fn create(
177 allocator: Allocator,
178 zig_exe: []const u8,
179 build_root: []const u8,
180 cache_root: []const u8,
181 global_cache_root: []const u8,
182 ) !*Builder {
183 const env_map = try allocator.create(EnvMap);
184 env_map.* = try process.getEnvMap(allocator);
185
186 const host = try NativeTargetInfo.detect(.{});
187
188 const self = try allocator.create(Builder);
189 self.* = Builder{
190 .zig_exe = zig_exe,
191 .build_root = build_root,
192 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
193 .global_cache_root = global_cache_root,
194 .verbose = false,
195 .verbose_link = false,
196 .verbose_cc = false,
197 .verbose_air = false,
198 .verbose_llvm_ir = false,
199 .verbose_cimport = false,
200 .verbose_llvm_cpu_features = false,
201 .prominent_compile_errors = false,
202 .invalid_user_input = false,
203 .allocator = allocator,
204 .user_input_options = UserInputOptionsMap.init(allocator),
205 .available_options_map = AvailableOptionsMap.init(allocator),
206 .available_options_list = ArrayList(AvailableOption).init(allocator),
207 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
208 .default_step = undefined,
209 .env_map = env_map,
210 .search_prefixes = ArrayList([]const u8).init(allocator),
211 .install_prefix = undefined,
212 .lib_dir = undefined,
213 .exe_dir = undefined,
214 .h_dir = undefined,
215 .dest_dir = env_map.get("DESTDIR"),
216 .installed_files = ArrayList(InstalledFile).init(allocator),
217 .install_tls = TopLevelStep{
218 .step = Step.initNoOp(.top_level, "install", allocator),
219 .description = "Copy build artifacts to prefix path",
220 },
221 .uninstall_tls = TopLevelStep{
222 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
223 .description = "Remove build artifacts from prefix path",
224 },
225 .override_lib_dir = null,
226 .install_path = undefined,
227 .args = null,
228 .host = host,
229 };
230 try self.top_level_steps.append(&self.install_tls);
231 try self.top_level_steps.append(&self.uninstall_tls);
232 self.default_step = &self.install_tls.step;
233 return self;
234 }
235
236 fn createChild(
237 parent: *Builder,
238 dep_name: []const u8,
239 build_root: []const u8,
240 args: anytype,
241 ) !*Builder {
242 const child = try createChildOnly(parent, dep_name, build_root);
243 try applyArgs(child, args);
244 return child;
245 }
246
247 fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
248 const allocator = parent.allocator;
249 const child = try allocator.create(Builder);
250 child.* = .{
251 .allocator = allocator,
252 .install_tls = .{
253 .step = Step.initNoOp(.top_level, "install", allocator),
254 .description = "Copy build artifacts to prefix path",
255 },
256 .uninstall_tls = .{
257 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
258 .description = "Remove build artifacts from prefix path",
259 },
260 .user_input_options = UserInputOptionsMap.init(allocator),
261 .available_options_map = AvailableOptionsMap.init(allocator),
262 .available_options_list = ArrayList(AvailableOption).init(allocator),
263 .verbose = parent.verbose,
264 .verbose_link = parent.verbose_link,
265 .verbose_cc = parent.verbose_cc,
266 .verbose_air = parent.verbose_air,
267 .verbose_llvm_ir = parent.verbose_llvm_ir,
268 .verbose_cimport = parent.verbose_cimport,
269 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
270 .prominent_compile_errors = parent.prominent_compile_errors,
271 .color = parent.color,
272 .reference_trace = parent.reference_trace,
273 .invalid_user_input = false,
274 .zig_exe = parent.zig_exe,
275 .default_step = undefined,
276 .env_map = parent.env_map,
277 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
278 .install_prefix = undefined,
279 .dest_dir = parent.dest_dir,
280 .lib_dir = parent.lib_dir,
281 .exe_dir = parent.exe_dir,
282 .h_dir = parent.h_dir,
283 .install_path = parent.install_path,
284 .sysroot = parent.sysroot,
285 .search_prefixes = ArrayList([]const u8).init(allocator),
286 .libc_file = parent.libc_file,
287 .installed_files = ArrayList(InstalledFile).init(allocator),
288 .build_root = build_root,
289 .cache_root = parent.cache_root,
290 .global_cache_root = parent.global_cache_root,
291 .override_lib_dir = parent.override_lib_dir,
292 .debug_log_scopes = parent.debug_log_scopes,
293 .debug_compile_errors = parent.debug_compile_errors,
294 .enable_darling = parent.enable_darling,
295 .enable_qemu = parent.enable_qemu,
296 .enable_rosetta = parent.enable_rosetta,
297 .enable_wasmtime = parent.enable_wasmtime,
298 .enable_wine = parent.enable_wine,
299 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
300 .host = parent.host,
301 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
302 };
303 try child.top_level_steps.append(&child.install_tls);
304 try child.top_level_steps.append(&child.uninstall_tls);
305 child.default_step = &child.install_tls.step;
306 return child;
307 }
308
309 fn applyArgs(b: *Builder, args: anytype) !void {
310 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
311 const v = @field(args, field.name);
312 const T = @TypeOf(v);
313 switch (T) {
314 CrossTarget => {
315 try b.user_input_options.put(field.name, .{
316 .name = field.name,
317 .value = .{ .scalar = try v.zigTriple(b.allocator) },
318 .used = false,
319 });
320 try b.user_input_options.put("cpu", .{
321 .name = "cpu",
322 .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
323 .used = false,
324 });
325 },
326 []const u8 => {
327 try b.user_input_options.put(field.name, .{
328 .name = field.name,
329 .value = .{ .scalar = v },
330 .used = false,
331 });
332 },
333 else => switch (@typeInfo(T)) {
334 .Bool => {
335 try b.user_input_options.put(field.name, .{
336 .name = field.name,
337 .value = .{ .scalar = if (v) "true" else "false" },
338 .used = false,
339 });
340 },
341 .Enum => {
342 try b.user_input_options.put(field.name, .{
343 .name = field.name,
344 .value = .{ .scalar = @tagName(v) },
345 .used = false,
346 });
347 },
348 .Int => {
349 try b.user_input_options.put(field.name, .{
350 .name = field.name,
351 .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
352 .used = false,
353 });
354 },
355 else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
356 },
357 }
358 }
359 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
360 // Random bytes to make unique. Refresh this with new random bytes when
361 // implementation is modified in a non-backwards-compatible way.
362 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
363 hash.update(b.dep_prefix);
364 // TODO additionally update the hash with `args`.
365
366 var digest: [16]u8 = undefined;
367 hash.final(&digest);
368 var hash_basename: [digest.len * 2]u8 = undefined;
369 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
370 unreachable;
371
372 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
373 b.resolveInstallPrefix(install_prefix, .{});
374 }
375
376 pub fn destroy(self: *Builder) void {
377 self.env_map.deinit();
378 self.top_level_steps.deinit();
379 self.allocator.destroy(self);
380 }
381
382 /// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
383 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void {
384 if (self.dest_dir) |dest_dir| {
385 self.install_prefix = install_prefix orelse "/usr";
386 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
387 } else {
388 self.install_prefix = install_prefix orelse
389 (self.pathJoin(&.{ self.build_root, "zig-out" }));
390 self.install_path = self.install_prefix;
391 }
392
393 var lib_list = [_][]const u8{ self.install_path, "lib" };
394 var exe_list = [_][]const u8{ self.install_path, "bin" };
395 var h_list = [_][]const u8{ self.install_path, "include" };
396
397 if (dir_list.lib_dir) |dir| {
398 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
399 lib_list[1] = dir;
400 }
401
402 if (dir_list.exe_dir) |dir| {
403 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
404 exe_list[1] = dir;
405 }
406
407 if (dir_list.include_dir) |dir| {
408 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
409 h_list[1] = dir;
410 }
411
412 self.lib_dir = self.pathJoin(&lib_list);
413 self.exe_dir = self.pathJoin(&exe_list);
414 self.h_dir = self.pathJoin(&h_list);
415 }
416
417 pub fn addOptions(self: *Builder) *OptionsStep {
418 return OptionsStep.create(self);
419 }
420
421 pub const ExecutableOptions = struct {
422 name: []const u8,
423 root_source_file: ?FileSource = null,
424 version: ?std.builtin.Version = null,
425 target: CrossTarget = .{},
426 optimize: std.builtin.Mode = .Debug,
427 linkage: ?LibExeObjStep.Linkage = null,
428 };
429
430 pub fn addExecutable(b: *Builder, options: ExecutableOptions) *LibExeObjStep {
431 return LibExeObjStep.create(b, .{
432 .name = options.name,
433 .root_source_file = options.root_source_file,
434 .version = options.version,
435 .target = options.target,
436 .optimize = options.optimize,
437 .kind = .exe,
438 .linkage = options.linkage,
439 });
440 }
441
442 pub const ObjectOptions = struct {
443 name: []const u8,
444 root_source_file: ?FileSource = null,
445 target: CrossTarget,
446 optimize: std.builtin.Mode,
447 };
448
449 pub fn addObject(b: *Builder, options: ObjectOptions) *LibExeObjStep {
450 return LibExeObjStep.create(b, .{
451 .name = options.name,
452 .root_source_file = options.root_source_file,
453 .target = options.target,
454 .optimize = options.optimize,
455 .kind = .obj,
456 });
457 }
458
459 pub const SharedLibraryOptions = struct {
460 name: []const u8,
461 root_source_file: ?FileSource = null,
462 version: ?std.builtin.Version = null,
463 target: CrossTarget,
464 optimize: std.builtin.Mode,
465 };
466
467 pub fn addSharedLibrary(b: *Builder, options: SharedLibraryOptions) *LibExeObjStep {
468 return LibExeObjStep.create(b, .{
469 .name = options.name,
470 .root_source_file = options.root_source_file,
471 .kind = .lib,
472 .linkage = .dynamic,
473 .version = options.version,
474 .target = options.target,
475 .optimize = options.optimize,
476 });
477 }
478
479 pub const StaticLibraryOptions = struct {
480 name: []const u8,
481 root_source_file: ?FileSource = null,
482 target: CrossTarget,
483 optimize: std.builtin.Mode,
484 version: ?std.builtin.Version = null,
485 };
486
487 pub fn addStaticLibrary(b: *Builder, options: StaticLibraryOptions) *LibExeObjStep {
488 return LibExeObjStep.create(b, .{
489 .name = options.name,
490 .root_source_file = options.root_source_file,
491 .kind = .lib,
492 .linkage = .static,
493 .version = options.version,
494 .target = options.target,
495 .optimize = options.optimize,
496 });
497 }
498
499 pub const TestOptions = struct {
500 name: []const u8 = "test",
501 kind: LibExeObjStep.Kind = .@"test",
502 root_source_file: FileSource,
503 target: CrossTarget = .{},
504 optimize: std.builtin.Mode = .Debug,
505 version: ?std.builtin.Version = null,
506 };
507
508 pub fn addTest(b: *Builder, options: TestOptions) *LibExeObjStep {
509 return LibExeObjStep.create(b, .{
510 .name = options.name,
511 .kind = options.kind,
512 .root_source_file = options.root_source_file,
513 .target = options.target,
514 .optimize = options.optimize,
515 });
516 }
517
518 pub const AssemblyOptions = struct {
519 name: []const u8,
520 source_file: FileSource,
521 target: CrossTarget,
522 optimize: std.builtin.Mode,
523 };
524
525 pub fn addAssembly(b: *Builder, options: AssemblyOptions) *LibExeObjStep {
526 const obj_step = LibExeObjStep.create(b, .{
527 .name = options.name,
528 .root_source_file = null,
529 .target = options.target,
530 .optimize = options.optimize,
531 });
532 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
533 return obj_step;
534 }
535
536 /// Initializes a RunStep with argv, which must at least have the path to the
537 /// executable. More command line arguments can be added with `addArg`,
538 /// `addArgs`, and `addArtifactArg`.
539 /// Be careful using this function, as it introduces a system dependency.
540 /// To run an executable built with zig build, see `LibExeObjStep.run`.
541 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
542 assert(argv.len >= 1);
543 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
544 run_step.addArgs(argv);
545 return run_step;
546 }
547
548 pub fn addConfigHeader(
549 b: *Builder,
550 source: FileSource,
551 style: ConfigHeaderStep.Style,
552 values: anytype,
553 ) *ConfigHeaderStep {
554 const config_header_step = ConfigHeaderStep.create(b, source, style);
555 config_header_step.addValues(values);
556 return config_header_step;
557 }
558
559 /// Allocator.dupe without the need to handle out of memory.
560 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
561 return self.allocator.dupe(u8, bytes) catch unreachable;
562 }
563
564 /// Duplicates an array of strings without the need to handle out of memory.
565 pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
566 const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
567 for (strings) |s, i| {
568 array[i] = self.dupe(s);
569 }
570 return array;
571 }
572
573 /// Duplicates a path and converts all slashes to the OS's canonical path separator.
574 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
575 const the_copy = self.dupe(bytes);
576 for (the_copy) |*byte| {
577 switch (byte.*) {
578 '/', '\\' => byte.* = fs.path.sep,
579 else => {},
580 }
581 }
582 return the_copy;
583 }
584
585 /// Duplicates a package recursively.
586 pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
587 var the_copy = Pkg{
588 .name = self.dupe(package.name),
589 .source = package.source.dupe(self),
590 };
591
592 if (package.dependencies) |dependencies| {
593 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
594 the_copy.dependencies = new_dependencies;
595
596 for (dependencies) |dep_package, i| {
597 new_dependencies[i] = self.dupePkg(dep_package);
598 }
599 }
600 return the_copy;
601 }
602
603 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
604 const write_file_step = self.addWriteFiles();
605 write_file_step.add(file_path, data);
606 return write_file_step;
607 }
608
609 pub fn addWriteFiles(self: *Builder) *WriteFileStep {
610 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
611 write_file_step.* = WriteFileStep.init(self);
612 return write_file_step;
613 }
614
615 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
616 const data = self.fmt(format, args);
617 const log_step = self.allocator.create(LogStep) catch unreachable;
618 log_step.* = LogStep.init(self, data);
619 return log_step;
620 }
621
622 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
623 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
624 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
625 return remove_dir_step;
626 }
627
628 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
629 return FmtStep.create(self, paths);
630 }
631
632 pub fn addTranslateC(self: *Builder, options: TranslateCStep.Options) *TranslateCStep {
633 return TranslateCStep.create(self, options);
634 }
635
636 pub fn make(self: *Builder, step_names: []const []const u8) !void {
637 try self.makePath(self.cache_root);
638
639 var wanted_steps = ArrayList(*Step).init(self.allocator);
640 defer wanted_steps.deinit();
641
642 if (step_names.len == 0) {
643 try wanted_steps.append(self.default_step);
644 } else {
645 for (step_names) |step_name| {
646 const s = try self.getTopLevelStepByName(step_name);
647 try wanted_steps.append(s);
648 }
649 }
650
651 for (wanted_steps.items) |s| {
652 try self.makeOneStep(s);
653 }
654 }
655
656 pub fn getInstallStep(self: *Builder) *Step {
657 return &self.install_tls.step;
658 }
659
660 pub fn getUninstallStep(self: *Builder) *Step {
661 return &self.uninstall_tls.step;
662 }
663
664 fn makeUninstall(uninstall_step: *Step) anyerror!void {
665 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
666 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
667
668 for (self.installed_files.items) |installed_file| {
669 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
670 if (self.verbose) {
671 log.info("rm {s}", .{full_path});
672 }
673 fs.cwd().deleteTree(full_path) catch {};
674 }
675
676 // TODO remove empty directories
677 }
678
679 fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
680 if (s.loop_flag) {
681 log.err("Dependency loop detected:\n {s}", .{s.name});
682 return error.DependencyLoopDetected;
683 }
684 s.loop_flag = true;
685
686 for (s.dependencies.items) |dep| {
687 self.makeOneStep(dep) catch |err| {
688 if (err == error.DependencyLoopDetected) {
689 log.err(" {s}", .{s.name});
690 }
691 return err;
692 };
693 }
694
695 s.loop_flag = false;
696
697 try s.make();
698 }
699
700 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
701 for (self.top_level_steps.items) |top_level_step| {
702 if (mem.eql(u8, top_level_step.step.name, name)) {
703 return &top_level_step.step;
704 }
705 }
706 log.err("Cannot run step '{s}' because it does not exist", .{name});
707 return error.InvalidStepName;
708 }
709
710 pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
711 const name = self.dupe(name_raw);
712 const description = self.dupe(description_raw);
713 const type_id = comptime typeToEnum(T);
714 const enum_options = if (type_id == .@"enum") blk: {
715 const fields = comptime std.meta.fields(T);
716 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
717
718 inline for (fields) |field| {
719 options.appendAssumeCapacity(field.name);
720 }
721
722 break :blk options.toOwnedSlice() catch unreachable;
723 } else null;
724 const available_option = AvailableOption{
725 .name = name,
726 .type_id = type_id,
727 .description = description,
728 .enum_options = enum_options,
729 };
730 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
731 panic("Option '{s}' declared twice", .{name});
732 }
733 self.available_options_list.append(available_option) catch unreachable;
734
735 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
736 option_ptr.used = true;
737 switch (type_id) {
738 .bool => switch (option_ptr.value) {
739 .flag => return true,
740 .scalar => |s| {
741 if (mem.eql(u8, s, "true")) {
742 return true;
743 } else if (mem.eql(u8, s, "false")) {
744 return false;
745 } else {
746 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
747 self.markInvalidUserInput();
748 return null;
749 }
750 },
751 .list, .map => {
752 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
753 name, @tagName(option_ptr.value),
754 });
755 self.markInvalidUserInput();
756 return null;
757 },
758 },
759 .int => switch (option_ptr.value) {
760 .flag, .list, .map => {
761 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
762 name, @tagName(option_ptr.value),
763 });
764 self.markInvalidUserInput();
765 return null;
766 },
767 .scalar => |s| {
768 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
769 error.Overflow => {
770 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
771 self.markInvalidUserInput();
772 return null;
773 },
774 else => {
775 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
776 self.markInvalidUserInput();
777 return null;
778 },
779 };
780 return n;
781 },
782 },
783 .float => switch (option_ptr.value) {
784 .flag, .map, .list => {
785 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
786 name, @tagName(option_ptr.value),
787 });
788 self.markInvalidUserInput();
789 return null;
790 },
791 .scalar => |s| {
792 const n = std.fmt.parseFloat(T, s) catch {
793 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
794 self.markInvalidUserInput();
795 return null;
796 };
797 return n;
798 },
799 },
800 .@"enum" => switch (option_ptr.value) {
801 .flag, .map, .list => {
802 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
803 name, @tagName(option_ptr.value),
804 });
805 self.markInvalidUserInput();
806 return null;
807 },
808 .scalar => |s| {
809 if (std.meta.stringToEnum(T, s)) |enum_lit| {
810 return enum_lit;
811 } else {
812 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
813 self.markInvalidUserInput();
814 return null;
815 }
816 },
817 },
818 .string => switch (option_ptr.value) {
819 .flag, .list, .map => {
820 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
821 name, @tagName(option_ptr.value),
822 });
823 self.markInvalidUserInput();
824 return null;
825 },
826 .scalar => |s| return s,
827 },
828 .list => switch (option_ptr.value) {
829 .flag, .map => {
830 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
831 name, @tagName(option_ptr.value),
832 });
833 self.markInvalidUserInput();
834 return null;
835 },
836 .scalar => |s| {
837 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
838 },
839 .list => |lst| return lst.items,
840 },
841 }
842 }
843
844 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
845 const step_info = self.allocator.create(TopLevelStep) catch unreachable;
846 step_info.* = TopLevelStep{
847 .step = Step.initNoOp(.top_level, name, self.allocator),
848 .description = self.dupe(description),
849 };
850 self.top_level_steps.append(step_info) catch unreachable;
851 return &step_info.step;
852 }
853
854 pub const StandardOptimizeOptionOptions = struct {
855 preferred_optimize_mode: ?std.builtin.Mode = null,
856 };
857
858 pub fn standardOptimizeOption(self: *Builder, options: StandardOptimizeOptionOptions) std.builtin.Mode {
859 if (options.preferred_optimize_mode) |mode| {
860 if (self.option(bool, "release", "optimize for end users") orelse false) {
861 return mode;
862 } else {
863 return .Debug;
864 }
865 } else {
866 return self.option(
867 std.builtin.Mode,
868 "optimize",
869 "prioritize performance, safety, or binary size (-O flag)",
870 ) orelse .Debug;
871 }
872 }
873
874 pub const StandardTargetOptionsArgs = struct {
875 whitelist: ?[]const CrossTarget = null,
876
877 default_target: CrossTarget = CrossTarget{},
878 };
879
880 /// Exposes standard `zig build` options for choosing a target.
881 pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
882 const maybe_triple = self.option(
883 []const u8,
884 "target",
885 "The CPU architecture, OS, and ABI to build for",
886 );
887 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
888
889 if (maybe_triple == null and mcpu == null) {
890 return args.default_target;
891 }
892
893 const triple = maybe_triple orelse "native";
894
895 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
896 const selected_target = CrossTarget.parse(.{
897 .arch_os_abi = triple,
898 .cpu_features = mcpu,
899 .diagnostics = &diags,
900 }) catch |err| switch (err) {
901 error.UnknownCpuModel => {
902 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
903 diags.cpu_name.?,
904 @tagName(diags.arch.?),
905 });
906 for (diags.arch.?.allCpuModels()) |cpu| {
907 log.err(" {s}", .{cpu.name});
908 }
909 self.markInvalidUserInput();
910 return args.default_target;
911 },
912 error.UnknownCpuFeature => {
913 log.err(
914 \\Unknown CPU feature: '{s}'
915 \\Available CPU features for architecture '{s}':
916 \\
917 , .{
918 diags.unknown_feature_name.?,
919 @tagName(diags.arch.?),
920 });
921 for (diags.arch.?.allFeaturesList()) |feature| {
922 log.err(" {s}: {s}", .{ feature.name, feature.description });
923 }
924 self.markInvalidUserInput();
925 return args.default_target;
926 },
927 error.UnknownOperatingSystem => {
928 log.err(
929 \\Unknown OS: '{s}'
930 \\Available operating systems:
931 \\
932 , .{diags.os_name.?});
933 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
934 log.err(" {s}", .{field.name});
935 }
936 self.markInvalidUserInput();
937 return args.default_target;
938 },
939 else => |e| {
940 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
941 self.markInvalidUserInput();
942 return args.default_target;
943 },
944 };
945
946 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
947
948 if (args.whitelist) |list| whitelist_check: {
949 // Make sure it's a match of one of the list.
950 var mismatch_triple = true;
951 var mismatch_cpu_features = true;
952 var whitelist_item = CrossTarget{};
953 for (list) |t| {
954 mismatch_cpu_features = true;
955 mismatch_triple = true;
956
957 const t_triple = t.zigTriple(self.allocator) catch unreachable;
958 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
959 mismatch_triple = false;
960 whitelist_item = t;
961 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
962 mismatch_cpu_features = false;
963 break :whitelist_check;
964 } else {
965 break;
966 }
967 }
968 }
969 if (mismatch_triple) {
970 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
971 selected_canonicalized_triple,
972 });
973 for (list) |t| {
974 const t_triple = t.zigTriple(self.allocator) catch unreachable;
975 log.err(" {s}", .{t_triple});
976 }
977 } else {
978 assert(mismatch_cpu_features);
979 const whitelist_cpu = whitelist_item.getCpu();
980 const selected_cpu = selected_target.getCpu();
981 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
982 selected_cpu.model.name,
983 });
984 log.err(" Supported feature Set: ", .{});
985 const all_features = whitelist_cpu.arch.allFeaturesList();
986 var populated_cpu_features = whitelist_cpu.model.features;
987 populated_cpu_features.populateDependencies(all_features);
988 for (all_features) |feature, i_usize| {
989 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
990 const in_cpu_set = populated_cpu_features.isEnabled(i);
991 if (in_cpu_set) {
992 log.err("{s} ", .{feature.name});
993 }
994 }
995 log.err(" Remove: ", .{});
996 for (all_features) |feature, i_usize| {
997 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
998 const in_cpu_set = populated_cpu_features.isEnabled(i);
999 const in_actual_set = selected_cpu.features.isEnabled(i);
1000 if (in_actual_set and !in_cpu_set) {
1001 log.err("{s} ", .{feature.name});
1002 }
1003 }
1004 }
1005 self.markInvalidUserInput();
1006 return args.default_target;
1007 }
1008
1009 return selected_target;
1010 }
1011
1012 pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
1013 const name = self.dupe(name_raw);
1014 const value = self.dupe(value_raw);
1015 const gop = try self.user_input_options.getOrPut(name);
1016 if (!gop.found_existing) {
1017 gop.value_ptr.* = UserInputOption{
1018 .name = name,
1019 .value = .{ .scalar = value },
1020 .used = false,
1021 };
1022 return false;
1023 }
1024
1025 // option already exists
1026 switch (gop.value_ptr.value) {
1027 .scalar => |s| {
1028 // turn it into a list
1029 var list = ArrayList([]const u8).init(self.allocator);
1030 list.append(s) catch unreachable;
1031 list.append(value) catch unreachable;
1032 self.user_input_options.put(name, .{
1033 .name = name,
1034 .value = .{ .list = list },
1035 .used = false,
1036 }) catch unreachable;
1037 },
1038 .list => |*list| {
1039 // append to the list
1040 list.append(value) catch unreachable;
1041 self.user_input_options.put(name, .{
1042 .name = name,
1043 .value = .{ .list = list.* },
1044 .used = false,
1045 }) catch unreachable;
1046 },
1047 .flag => {
1048 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1049 return true;
1050 },
1051 .map => |*map| {
1052 _ = map;
1053 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
1054 return true;
1055 },
1056 }
1057 return false;
1058 }
1059
1060 pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
1061 const name = self.dupe(name_raw);
1062 const gop = try self.user_input_options.getOrPut(name);
1063 if (!gop.found_existing) {
1064 gop.value_ptr.* = .{
1065 .name = name,
1066 .value = .{ .flag = {} },
1067 .used = false,
1068 };
1069 return false;
1070 }
1071
1072 // option already exists
1073 switch (gop.value_ptr.value) {
1074 .scalar => |s| {
1075 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1076 return true;
1077 },
1078 .list, .map => {
1079 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1080 return true;
1081 },
1082 .flag => {},
1083 }
1084 return false;
1085 }
1086
1087 fn typeToEnum(comptime T: type) TypeId {
1088 return switch (@typeInfo(T)) {
1089 .Int => .int,
1090 .Float => .float,
1091 .Bool => .bool,
1092 .Enum => .@"enum",
1093 else => switch (T) {
1094 []const u8 => .string,
1095 []const []const u8 => .list,
1096 else => @compileError("Unsupported type: " ++ @typeName(T)),
1097 },
1098 };
1099 }
1100
1101 fn markInvalidUserInput(self: *Builder) void {
1102 self.invalid_user_input = true;
1103 }
1104
1105 pub fn validateUserInputDidItFail(self: *Builder) bool {
1106 // make sure all args are used
1107 var it = self.user_input_options.iterator();
1108 while (it.next()) |entry| {
1109 if (!entry.value_ptr.used) {
1110 log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
1111 self.markInvalidUserInput();
1112 }
1113 }
1114
1115 return self.invalid_user_input;
1116 }
1117
1118 pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
1119 return self.spawnChildEnvMap(null, self.env_map, argv);
1120 }
1121
1122 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1123 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1124 for (argv) |arg| {
1125 std.debug.print("{s} ", .{arg});
1126 }
1127 std.debug.print("\n", .{});
1128 }
1129
1130 pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1131 if (self.verbose) {
1132 printCmd(cwd, argv);
1133 }
1134
1135 if (!std.process.can_spawn)
1136 return error.ExecNotSupported;
1137
1138 var child = std.ChildProcess.init(argv, self.allocator);
1139 child.cwd = cwd;
1140 child.env_map = env_map;
1141
1142 const term = child.spawnAndWait() catch |err| {
1143 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1144 return err;
1145 };
1146
1147 switch (term) {
1148 .Exited => |code| {
1149 if (code != 0) {
1150 log.err("The following command exited with error code {}:", .{code});
1151 printCmd(cwd, argv);
1152 return error.UncleanExit;
1153 }
1154 },
1155 else => {
1156 log.err("The following command terminated unexpectedly:", .{});
1157 printCmd(cwd, argv);
1158
1159 return error.UncleanExit;
1160 },
1161 }
1162 }
1163
1164 pub fn makePath(self: *Builder, path: []const u8) !void {
1165 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1166 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1167 return err;
1168 };
1169 }
1170
1171 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
1172 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1173 }
1174
1175 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
1176 return InstallArtifactStep.create(self, artifact);
1177 }
1178
1179 ///`dest_rel_path` is relative to prefix path
1180 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1181 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1182 }
1183
1184 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
1185 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1186 }
1187
1188 ///`dest_rel_path` is relative to bin path
1189 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1190 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1191 }
1192
1193 ///`dest_rel_path` is relative to lib path
1194 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1195 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1196 }
1197
1198 /// Output format (BIN vs Intel HEX) determined by filename
1199 pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1200 const raw = self.addInstallRaw(artifact, dest_filename, options);
1201 self.getInstallStep().dependOn(&raw.step);
1202 return raw;
1203 }
1204
1205 ///`dest_rel_path` is relative to install prefix path
1206 pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1207 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1208 }
1209
1210 ///`dest_rel_path` is relative to bin path
1211 pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1212 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1213 }
1214
1215 ///`dest_rel_path` is relative to lib path
1216 pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1217 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1218 }
1219
1220 pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1221 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1222 }
1223
1224 pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1225 return InstallRawStep.create(self, artifact, dest_filename, options);
1226 }
1227
1228 pub fn addInstallFileWithDir(
1229 self: *Builder,
1230 source: FileSource,
1231 install_dir: InstallDir,
1232 dest_rel_path: []const u8,
1233 ) *InstallFileStep {
1234 if (dest_rel_path.len == 0) {
1235 panic("dest_rel_path must be non-empty", .{});
1236 }
1237 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
1238 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1239 return install_step;
1240 }
1241
1242 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
1243 const install_step = self.allocator.create(InstallDirStep) catch unreachable;
1244 install_step.* = InstallDirStep.init(self, options);
1245 return install_step;
1246 }
1247
1248 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
1249 const file = InstalledFile{
1250 .dir = dir,
1251 .path = dest_rel_path,
1252 };
1253 self.installed_files.append(file.dupe(self)) catch unreachable;
1254 }
1255
1256 pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
1257 if (self.verbose) {
1258 log.info("cp {s} {s} ", .{ source_path, dest_path });
1259 }
1260 const cwd = fs.cwd();
1261 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1262 if (self.verbose) switch (prev_status) {
1263 .stale => log.info("# installed", .{}),
1264 .fresh => log.info("# up-to-date", .{}),
1265 };
1266 }
1267
1268 pub fn truncateFile(self: *Builder, dest_path: []const u8) !void {
1269 if (self.verbose) {
1270 log.info("truncate {s}", .{dest_path});
1271 }
1272 const cwd = fs.cwd();
1273 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1274 error.FileNotFound => blk: {
1275 if (fs.path.dirname(dest_path)) |dirname| {
1276 try cwd.makePath(dirname);
1277 }
1278 break :blk try cwd.createFile(dest_path, .{});
1279 },
1280 else => |e| return e,
1281 };
1282 src_file.close();
1283 }
1284
1285 pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
1286 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
1287 }
1288
1289 /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable`
1290 pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 {
1291 return fs.path.join(self.allocator, paths) catch unreachable;
1292 }
1293
1294 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
1295 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
1296 }
1297
1298 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1299 // TODO report error for ambiguous situations
1300 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1301 for (self.search_prefixes.items) |search_prefix| {
1302 for (names) |name| {
1303 if (fs.path.isAbsolute(name)) {
1304 return name;
1305 }
1306 const full_path = self.pathJoin(&.{
1307 search_prefix,
1308 "bin",
1309 self.fmt("{s}{s}", .{ name, exe_extension }),
1310 });
1311 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1312 }
1313 }
1314 if (self.env_map.get("PATH")) |PATH| {
1315 for (names) |name| {
1316 if (fs.path.isAbsolute(name)) {
1317 return name;
1318 }
1319 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1320 while (it.next()) |path| {
1321 const full_path = self.pathJoin(&.{
1322 path,
1323 self.fmt("{s}{s}", .{ name, exe_extension }),
1324 });
1325 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1326 }
1327 }
1328 }
1329 for (names) |name| {
1330 if (fs.path.isAbsolute(name)) {
1331 return name;
1332 }
1333 for (paths) |path| {
1334 const full_path = self.pathJoin(&.{
1335 path,
1336 self.fmt("{s}{s}", .{ name, exe_extension }),
1337 });
1338 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1339 }
1340 }
1341 return error.FileNotFound;
1342 }
1343
1344 pub fn execAllowFail(
1345 self: *Builder,
1346 argv: []const []const u8,
1347 out_code: *u8,
1348 stderr_behavior: std.ChildProcess.StdIo,
1349 ) ExecError![]u8 {
1350 assert(argv.len != 0);
1351
1352 if (!std.process.can_spawn)
1353 return error.ExecNotSupported;
1354
1355 const max_output_size = 400 * 1024;
1356 var child = std.ChildProcess.init(argv, self.allocator);
1357 child.stdin_behavior = .Ignore;
1358 child.stdout_behavior = .Pipe;
1359 child.stderr_behavior = stderr_behavior;
1360 child.env_map = self.env_map;
1361
1362 try child.spawn();
1363
1364 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1365 return error.ReadFailure;
1366 };
1367 errdefer self.allocator.free(stdout);
1368
1369 const term = try child.wait();
1370 switch (term) {
1371 .Exited => |code| {
1372 if (code != 0) {
1373 out_code.* = @truncate(u8, code);
1374 return error.ExitCodeFailure;
1375 }
1376 return stdout;
1377 },
1378 .Signal, .Stopped, .Unknown => |code| {
1379 out_code.* = @truncate(u8, code);
1380 return error.ProcessTerminated;
1381 },
1382 }
1383 }
1384
1385 pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1386 assert(argv.len != 0);
1387
1388 if (self.verbose) {
1389 printCmd(null, argv);
1390 }
1391
1392 if (!std.process.can_spawn) {
1393 if (src_step) |s| log.err("{s}...", .{s.name});
1394 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1395 printCmd(null, argv);
1396 std.os.abort();
1397 }
1398
1399 var code: u8 = undefined;
1400 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1401 error.ExecNotSupported => {
1402 if (src_step) |s| log.err("{s}...", .{s.name});
1403 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1404 printCmd(null, argv);
1405 std.os.abort();
1406 },
1407 error.FileNotFound => {
1408 if (src_step) |s| log.err("{s}...", .{s.name});
1409 log.err("Unable to spawn the following command: file not found", .{});
1410 printCmd(null, argv);
1411 std.os.exit(@truncate(u8, code));
1412 },
1413 error.ExitCodeFailure => {
1414 if (src_step) |s| log.err("{s}...", .{s.name});
1415 if (self.prominent_compile_errors) {
1416 log.err("The step exited with error code {d}", .{code});
1417 } else {
1418 log.err("The following command exited with error code {d}:", .{code});
1419 printCmd(null, argv);
1420 }
1421
1422 std.os.exit(@truncate(u8, code));
1423 },
1424 error.ProcessTerminated => {
1425 if (src_step) |s| log.err("{s}...", .{s.name});
1426 log.err("The following command terminated unexpectedly:", .{});
1427 printCmd(null, argv);
1428 std.os.exit(@truncate(u8, code));
1429 },
1430 else => |e| return e,
1431 };
1432 }
1433
1434 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
1435 return self.execFromStep(argv, null);
1436 }
1437
1438 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
1439 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
1440 }
1441
1442 pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1443 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1444 const base_dir = switch (dir) {
1445 .prefix => self.install_path,
1446 .bin => self.exe_dir,
1447 .lib => self.lib_dir,
1448 .header => self.h_dir,
1449 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1450 };
1451 return fs.path.resolve(
1452 self.allocator,
1453 &[_][]const u8{ base_dir, dest_rel_path },
1454 ) catch unreachable;
1455 }
1456
1457 pub const Dependency = struct {
1458 builder: *Builder,
1459
1460 pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1461 var found: ?*LibExeObjStep = null;
1462 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1463 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1464 if (mem.eql(u8, inst.artifact.name, name)) {
1465 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1466 found = inst.artifact;
1467 }
1468 }
1469 return found orelse {
1470 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1471 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1472 log.info("available artifact: '{s}'", .{inst.artifact.name});
1473 }
1474 panic("unable to find artifact '{s}'", .{name});
1475 };
1476 }
1477 };
1478
1479 pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
1480 const build_runner = @import("root");
1481 const deps = build_runner.dependencies;
1482
1483 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1484 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1485 mem.endsWith(u8, decl.name, name) and
1486 decl.name.len == b.dep_prefix.len + name.len)
1487 {
1488 const build_zig = @field(deps.imports, decl.name);
1489 const build_root = @field(deps.build_root, decl.name);
1490 return dependencyInner(b, name, build_root, build_zig, args);
1491 }
1492 }
1493
1494 const full_path = b.pathFromRoot("build.zig.ini");
1495 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1496 std.process.exit(1);
1497 }
1498
1499 fn dependencyInner(
1500 b: *Builder,
1501 name: []const u8,
1502 build_root: []const u8,
1503 comptime build_zig: type,
1504 args: anytype,
1505 ) *Dependency {
1506 const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1507 sub_builder.runBuild(build_zig) catch unreachable;
1508
1509 if (sub_builder.validateUserInputDidItFail()) {
1510 std.debug.dumpCurrentStackTrace(@returnAddress());
1511 }
1512
1513 const dep = b.allocator.create(Dependency) catch unreachable;
1514 dep.* = .{ .builder = sub_builder };
1515 return dep;
1516 }
1517
1518 pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
1519 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1520 .Void => build_zig.build(b),
1521 .ErrorUnion => try build_zig.build(b),
1522 else => @compileError("expected return type of build to be 'void' or '!void'"),
1523 }
1524 }
1525};
1526
1527test "builder.findProgram compiles" {
1528 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1529
1530 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1531 defer arena.deinit();
1532
1533 const builder = try Builder.create(
1534 arena.allocator(),
1535 "zig",
1536 "zig-cache",
1537 "zig-cache",
1538 "zig-cache",
1539 );
1540 defer builder.destroy();
1541 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1542}
1543
1544pub const Pkg = struct {
1545 name: []const u8,
1546 source: FileSource,
1547 dependencies: ?[]const Pkg = null,
1548};
1549
1550/// A file that is generated by a build step.
1551/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1552pub const GeneratedFile = struct {
1553 /// The step that generates the file
1554 step: *Step,
1555
1556 /// The path to the generated file. Must be either absolute or relative to the build root.
1557 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1558 path: ?[]const u8 = null,
1559
1560 pub fn getPath(self: GeneratedFile) []const u8 {
1561 return self.path orelse std.debug.panic(
1562 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1563 .{self.step.name},
1564 );
1565 }
1566};
1567
1568/// A file source is a reference to an existing or future file.
1569///
1570pub const FileSource = union(enum) {
1571 /// A plain file path, relative to build root or absolute.
1572 path: []const u8,
1573
1574 /// A file that is generated by an interface. Those files usually are
1575 /// not available until built by a build step.
1576 generated: *const GeneratedFile,
1577
1578 /// Returns a new file source that will have a relative path to the build root guaranteed.
1579 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1580 pub fn relative(path: []const u8) FileSource {
1581 std.debug.assert(!std.fs.path.isAbsolute(path));
1582 return FileSource{ .path = path };
1583 }
1584
1585 /// Returns a string that can be shown to represent the file source.
1586 /// Either returns the path or `"generated"`.
1587 pub fn getDisplayName(self: FileSource) []const u8 {
1588 return switch (self) {
1589 .path => self.path,
1590 .generated => "generated",
1591 };
1592 }
1593
1594 /// Adds dependencies this file source implies to the given step.
1595 pub fn addStepDependencies(self: FileSource, step: *Step) void {
1596 switch (self) {
1597 .path => {},
1598 .generated => |gen| step.dependOn(gen.step),
1599 }
1600 }
1601
1602 /// Should only be called during make(), returns a path relative to the build root or absolute.
1603 pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
1604 const path = switch (self) {
1605 .path => |p| builder.pathFromRoot(p),
1606 .generated => |gen| gen.getPath(),
1607 };
1608 return path;
1609 }
1610
1611 /// Duplicates the file source for a given builder.
1612 pub fn dupe(self: FileSource, b: *Builder) FileSource {
1613 return switch (self) {
1614 .path => |p| .{ .path = b.dupePath(p) },
1615 .generated => |gen| .{ .generated = gen },
1616 };
1617 }
1618};
1619
1620/// Allocates a new string for assigning a value to a named macro.
1621/// If the value is omitted, it is set to 1.
1622/// `name` and `value` need not live longer than the function call.
1623pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1624 var macro = allocator.alloc(
1625 u8,
1626 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1627 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1628 mem.copy(u8, macro, name);
1629 if (value) |value_slice| {
1630 macro[name.len] = '=';
1631 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1632 }
1633 return macro;
1634}
1635
1636/// deprecated: use `InstallDirStep.Options`
1637pub const InstallDirectoryOptions = InstallDirStep.Options;
1638
1639pub const Step = struct {
1640 id: Id,
1641 name: []const u8,
1642 makeFn: MakeFn,
1643 dependencies: ArrayList(*Step),
1644 loop_flag: bool,
1645 done_flag: bool,
1646
1647 const MakeFn = *const fn (self: *Step) anyerror!void;
1648
1649 pub const Id = enum {
1650 top_level,
1651 lib_exe_obj,
1652 install_artifact,
1653 install_file,
1654 install_dir,
1655 log,
1656 remove_dir,
1657 fmt,
1658 translate_c,
1659 write_file,
1660 run,
1661 emulatable_run,
1662 check_file,
1663 check_object,
1664 config_header,
1665 install_raw,
1666 options,
1667 custom,
1668
1669 pub fn Type(comptime id: Id) type {
1670 return switch (id) {
1671 .top_level => Builder.TopLevelStep,
1672 .lib_exe_obj => LibExeObjStep,
1673 .install_artifact => InstallArtifactStep,
1674 .install_file => InstallFileStep,
1675 .install_dir => InstallDirStep,
1676 .log => LogStep,
1677 .remove_dir => RemoveDirStep,
1678 .fmt => FmtStep,
1679 .translate_c => TranslateCStep,
1680 .write_file => WriteFileStep,
1681 .run => RunStep,
1682 .emulatable_run => EmulatableRunStep,
1683 .check_file => CheckFileStep,
1684 .check_object => CheckObjectStep,
1685 .config_header => ConfigHeaderStep,
1686 .install_raw => InstallRawStep,
1687 .options => OptionsStep,
1688 .custom => @compileError("no type available for custom step"),
1689 };
1690 }
1691 };
1692
1693 pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step {
1694 return Step{
1695 .id = id,
1696 .name = allocator.dupe(u8, name) catch unreachable,
1697 .makeFn = makeFn,
1698 .dependencies = ArrayList(*Step).init(allocator),
1699 .loop_flag = false,
1700 .done_flag = false,
1701 };
1702 }
1703 pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
1704 return init(id, name, allocator, makeNoOp);
1705 }
1706
1707 pub fn make(self: *Step) !void {
1708 if (self.done_flag) return;
1709
1710 try self.makeFn(self);
1711 self.done_flag = true;
1712 }
1713
1714 pub fn dependOn(self: *Step, other: *Step) void {
1715 self.dependencies.append(other) catch unreachable;
1716 }
1717
1718 fn makeNoOp(self: *Step) anyerror!void {
1719 _ = self;
1720 }
1721
1722 pub fn cast(step: *Step, comptime T: type) ?*T {
1723 if (step.id == T.base_id) {
1724 return @fieldParentPtr(T, "step", step);
1725 }
1726 return null;
1727 }
1728};
1729
1730pub const VcpkgRoot = union(VcpkgRootStatus) {
1731 unattempted: void,
1732 not_found: void,
1733 found: []const u8,
1734};
1735
1736pub const VcpkgRootStatus = enum {
1737 unattempted,
1738 not_found,
1739 found,
1740};
1741
1742pub const InstallDir = union(enum) {
1743 prefix: void,
1744 lib: void,
1745 bin: void,
1746 header: void,
1747 /// A path relative to the prefix
1748 custom: []const u8,
1749
1750 /// Duplicates the install directory including the path if set to custom.
1751 pub fn dupe(self: InstallDir, builder: *Builder) InstallDir {
1752 if (self == .custom) {
1753 // Written with this temporary to avoid RLS problems
1754 const duped_path = builder.dupe(self.custom);
1755 return .{ .custom = duped_path };
1756 } else {
1757 return self;
1758 }
1759 }
1760};
1761
1762pub const InstalledFile = struct {
1763 dir: InstallDir,
1764 path: []const u8,
1765
1766 /// Duplicates the installed file path and directory.
1767 pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
1768 return .{
1769 .dir = self.dir.dupe(builder),
1770 .path = builder.dupe(self.path),
1771 };
1772 }
1773};
1774
1775pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1776 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1777 const all_features = cpu.arch.allFeaturesList();
1778 var populated_cpu_features = cpu.model.features;
1779 populated_cpu_features.populateDependencies(all_features);
1780
1781 if (populated_cpu_features.eql(cpu.features)) {
1782 // The CPU name alone is sufficient.
1783 return cpu.model.name;
1784 } else {
1785 var mcpu_buffer = ArrayList(u8).init(allocator);
1786 try mcpu_buffer.appendSlice(cpu.model.name);
1787
1788 for (all_features) |feature, i_usize| {
1789 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1790 const in_cpu_set = populated_cpu_features.isEnabled(i);
1791 const in_actual_set = cpu.features.isEnabled(i);
1792 if (in_cpu_set and !in_actual_set) {
1793 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1794 } else if (!in_cpu_set and in_actual_set) {
1795 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1796 }
1797 }
1798
1799 return try mcpu_buffer.toOwnedSlice();
1800 }
1801}
1802
1803test "dupePkg()" {
1804 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1805
1806 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1807 defer arena.deinit();
1808 var builder = try Builder.create(
1809 arena.allocator(),
1810 "test",
1811 "test",
1812 "test",
1813 "test",
1814 );
1815 defer builder.destroy();
1816
1817 var pkg_dep = Pkg{
1818 .name = "pkg_dep",
1819 .source = .{ .path = "/not/a/pkg_dep.zig" },
1820 };
1821 var pkg_top = Pkg{
1822 .name = "pkg_top",
1823 .source = .{ .path = "/not/a/pkg_top.zig" },
1824 .dependencies = &[_]Pkg{pkg_dep},
1825 };
1826 const dupe = builder.dupePkg(pkg_top);
1827
1828 const original_deps = pkg_top.dependencies.?;
1829 const dupe_deps = dupe.dependencies.?;
1830
1831 // probably the same top level package details
1832 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1833
1834 // probably the same dependencies
1835 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1836 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1837
1838 // could segfault otherwise if pointers in duplicated package's fields are
1839 // the same as those in stack allocated package's fields
1840 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1841 try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
1842 try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr);
1843 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1844 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1845}
1846
1847test {
1848 _ = CheckFileStep;
1849 _ = CheckObjectStep;
1850 _ = EmulatableRunStep;
1851 _ = FmtStep;
1852 _ = InstallArtifactStep;
1853 _ = InstallDirStep;
1854 _ = InstallFileStep;
1855 _ = InstallRawStep;
1856 _ = LibExeObjStep;
1857 _ = LogStep;
1858 _ = OptionsStep;
1859 _ = RemoveDirStep;
1860 _ = RunStep;
1861 _ = TranslateCStep;
1862 _ = WriteFileStep;
1863}
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-2047
......@@ -1,2047 +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,
40target_info: NativeTargetInfo,
41optimize: std.builtin.Mode,
42linker_script: ?FileSource = null,
43version_script: ?[]const u8 = null,
44out_filename: []const u8,
45linkage: ?Linkage = null,
46version: ?std.builtin.Version,
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 Options = struct {
275 name: []const u8,
276 root_source_file: ?FileSource = null,
277 target: CrossTarget,
278 optimize: std.builtin.Mode,
279 kind: Kind,
280 linkage: ?Linkage = null,
281 version: ?std.builtin.Version = null,
282};
283
284pub const Kind = enum {
285 exe,
286 lib,
287 obj,
288 @"test",
289 test_exe,
290};
291
292pub const Linkage = enum { dynamic, static };
293
294pub const EmitOption = union(enum) {
295 default: void,
296 no_emit: void,
297 emit: void,
298 emit_to: []const u8,
299
300 fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 {
301 return switch (self) {
302 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
303 .default => null,
304 .emit => b.fmt("-f{s}", .{arg_name}),
305 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
306 };
307 }
308};
309
310pub fn create(builder: *Builder, options: Options) *LibExeObjStep {
311 const name = builder.dupe(options.name);
312 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
313 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
314 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
315 }
316
317 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
318 self.* = LibExeObjStep{
319 .strip = null,
320 .unwind_tables = null,
321 .builder = builder,
322 .verbose_link = false,
323 .verbose_cc = false,
324 .optimize = options.optimize,
325 .target = options.target,
326 .linkage = options.linkage,
327 .kind = options.kind,
328 .root_src = root_src,
329 .name = name,
330 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
331 .step = Step.init(base_id, name, builder.allocator, make),
332 .version = options.version,
333 .out_filename = undefined,
334 .out_h_filename = builder.fmt("{s}.h", .{name}),
335 .out_lib_filename = undefined,
336 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
337 .major_only_filename = null,
338 .name_only_filename = null,
339 .packages = ArrayList(Pkg).init(builder.allocator),
340 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
341 .link_objects = ArrayList(LinkObject).init(builder.allocator),
342 .c_macros = ArrayList([]const u8).init(builder.allocator),
343 .lib_paths = ArrayList([]const u8).init(builder.allocator),
344 .rpaths = ArrayList([]const u8).init(builder.allocator),
345 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
346 .installed_headers = ArrayList(*std.build.Step).init(builder.allocator),
347 .object_src = undefined,
348 .c_std = Builder.CStd.C99,
349 .override_lib_dir = null,
350 .main_pkg_path = null,
351 .exec_cmd_args = null,
352 .name_prefix = "",
353 .filter = null,
354 .test_runner = null,
355 .disable_stack_probing = false,
356 .disable_sanitize_c = false,
357 .sanitize_thread = false,
358 .rdynamic = false,
359 .output_dir = null,
360 .override_dest_dir = null,
361 .installed_path = null,
362 .install_step = null,
363
364 .output_path_source = GeneratedFile{ .step = &self.step },
365 .output_lib_path_source = GeneratedFile{ .step = &self.step },
366 .output_h_path_source = GeneratedFile{ .step = &self.step },
367 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
368
369 .target_info = undefined, // populated in computeOutFileNames
370 };
371 self.computeOutFileNames();
372 if (root_src) |rs| rs.addStepDependencies(&self.step);
373 return self;
374}
375
376fn computeOutFileNames(self: *LibExeObjStep) void {
377 self.target_info = NativeTargetInfo.detect(self.target) catch
378 unreachable;
379
380 const target = self.target_info.target;
381
382 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
383 .root_name = self.name,
384 .target = target,
385 .output_mode = switch (self.kind) {
386 .lib => .Lib,
387 .obj => .Obj,
388 .exe, .@"test", .test_exe => .Exe,
389 },
390 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
391 .dynamic => .Dynamic,
392 .static => .Static,
393 }) else null,
394 .version = self.version,
395 }) catch unreachable;
396
397 if (self.kind == .lib) {
398 if (self.linkage != null and self.linkage.? == .static) {
399 self.out_lib_filename = self.out_filename;
400 } else if (self.version) |version| {
401 if (target.isDarwin()) {
402 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
403 self.name,
404 version.major,
405 });
406 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
407 self.out_lib_filename = self.out_filename;
408 } else if (target.os.tag == .windows) {
409 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
410 } else {
411 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
412 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
413 self.out_lib_filename = self.out_filename;
414 }
415 } else {
416 if (target.isDarwin()) {
417 self.out_lib_filename = self.out_filename;
418 } else if (target.os.tag == .windows) {
419 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
420 } else {
421 self.out_lib_filename = self.out_filename;
422 }
423 }
424 if (self.output_dir != null) {
425 self.output_lib_path_source.path = self.builder.pathJoin(
426 &.{ self.output_dir.?, self.out_lib_filename },
427 );
428 }
429 }
430}
431
432pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
433 self.output_dir = self.builder.dupePath(dir);
434}
435
436pub fn install(self: *LibExeObjStep) void {
437 self.builder.installArtifact(self);
438}
439
440pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
441 return self.builder.installRaw(self, dest_filename, options);
442}
443
444pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
445 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
446 a.builder.getInstallStep().dependOn(&install_file.step);
447 a.installed_headers.append(&install_file.step) catch unreachable;
448}
449
450pub fn installHeadersDirectory(
451 a: *LibExeObjStep,
452 src_dir_path: []const u8,
453 dest_rel_path: []const u8,
454) void {
455 return installHeadersDirectoryOptions(a, .{
456 .source_dir = src_dir_path,
457 .install_dir = .header,
458 .install_subdir = dest_rel_path,
459 });
460}
461
462pub fn installHeadersDirectoryOptions(
463 a: *LibExeObjStep,
464 options: std.build.InstallDirStep.Options,
465) void {
466 const install_dir = a.builder.addInstallDirectory(options);
467 a.builder.getInstallStep().dependOn(&install_dir.step);
468 a.installed_headers.append(&install_dir.step) catch unreachable;
469}
470
471pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
472 assert(l.kind == .lib);
473 const install_step = a.builder.getInstallStep();
474 // Copy each element from installed_headers, modifying the builder
475 // to be the new parent's builder.
476 for (l.installed_headers.items) |step| {
477 const step_copy = switch (step.id) {
478 inline .install_file, .install_dir => |id| blk: {
479 const T = id.Type();
480 const ptr = a.builder.allocator.create(T) catch unreachable;
481 ptr.* = step.cast(T).?.*;
482 ptr.override_source_builder = ptr.builder;
483 ptr.builder = a.builder;
484 break :blk &ptr.step;
485 },
486 else => unreachable,
487 };
488 a.installed_headers.append(step_copy) catch unreachable;
489 install_step.dependOn(step_copy);
490 }
491 a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
492}
493
494/// Creates a `RunStep` with an executable built with `addExecutable`.
495/// Add command line arguments with `addArg`.
496pub fn run(exe: *LibExeObjStep) *RunStep {
497 assert(exe.kind == .exe or exe.kind == .test_exe);
498
499 // It doesn't have to be native. We catch that if you actually try to run it.
500 // Consider that this is declarative; the run step may not be run unless a user
501 // option is supplied.
502 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
503 run_step.addArtifactArg(exe);
504
505 if (exe.kind == .test_exe) {
506 run_step.addArg(exe.builder.zig_exe);
507 }
508
509 if (exe.vcpkg_bin_path) |path| {
510 run_step.addPathDir(path);
511 }
512
513 return run_step;
514}
515
516/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
517/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
518/// When a binary cannot be ran through emulation or the option is disabled, a warning
519/// will be printed and the binary will *NOT* be ran.
520pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
521 assert(exe.kind == .exe or exe.kind == .test_exe);
522
523 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
524 if (exe.vcpkg_bin_path) |path| {
525 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
526 }
527 return run_step;
528}
529
530pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
531 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
532}
533
534pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
535 self.linker_script = source.dupe(self.builder);
536 source.addStepDependencies(&self.step);
537}
538
539pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
540 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
541}
542
543pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
544 self.frameworks.put(self.builder.dupe(framework_name), .{
545 .needed = true,
546 }) catch unreachable;
547}
548
549pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
550 self.frameworks.put(self.builder.dupe(framework_name), .{
551 .weak = true,
552 }) catch unreachable;
553}
554
555/// Returns whether the library, executable, or object depends on a particular system library.
556pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
557 if (isLibCLibrary(name)) {
558 return self.is_linking_libc;
559 }
560 if (isLibCppLibrary(name)) {
561 return self.is_linking_libcpp;
562 }
563 for (self.link_objects.items) |link_object| {
564 switch (link_object) {
565 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
566 else => continue,
567 }
568 }
569 return false;
570}
571
572pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
573 assert(lib.kind == .lib);
574 self.linkLibraryOrObject(lib);
575}
576
577pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
578 return self.kind == .lib and self.linkage == Linkage.dynamic;
579}
580
581pub fn isStaticLibrary(self: *LibExeObjStep) bool {
582 return self.kind == .lib and self.linkage != Linkage.dynamic;
583}
584
585pub fn producesPdbFile(self: *LibExeObjStep) bool {
586 if (!self.target.isWindows() and !self.target.isUefi()) return false;
587 if (self.target.getObjectFormat() == .c) return false;
588 if (self.strip == true) return false;
589 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
590}
591
592pub fn linkLibC(self: *LibExeObjStep) void {
593 self.is_linking_libc = true;
594}
595
596pub fn linkLibCpp(self: *LibExeObjStep) void {
597 self.is_linking_libcpp = true;
598}
599
600/// If the value is omitted, it is set to 1.
601/// `name` and `value` need not live longer than the function call.
602pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
603 const macro = std.build.constructCMacro(self.builder.allocator, name, value);
604 self.c_macros.append(macro) catch unreachable;
605}
606
607/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
608pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
609 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
610}
611
612/// This one has no integration with anything, it just puts -lname on the command line.
613/// Prefer to use `linkSystemLibrary` instead.
614pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
615 self.link_objects.append(.{
616 .system_lib = .{
617 .name = self.builder.dupe(name),
618 .needed = false,
619 .weak = false,
620 .use_pkg_config = .no,
621 },
622 }) catch unreachable;
623}
624
625/// This one has no integration with anything, it just puts -needed-lname on the command line.
626/// Prefer to use `linkSystemLibraryNeeded` instead.
627pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
628 self.link_objects.append(.{
629 .system_lib = .{
630 .name = self.builder.dupe(name),
631 .needed = true,
632 .weak = false,
633 .use_pkg_config = .no,
634 },
635 }) catch unreachable;
636}
637
638/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
639/// command line. Prefer to use `linkSystemLibraryWeak` instead.
640pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
641 self.link_objects.append(.{
642 .system_lib = .{
643 .name = self.builder.dupe(name),
644 .needed = false,
645 .weak = true,
646 .use_pkg_config = .no,
647 },
648 }) catch unreachable;
649}
650
651/// This links against a system library, exclusively using pkg-config to find the library.
652/// Prefer to use `linkSystemLibrary` instead.
653pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
654 self.link_objects.append(.{
655 .system_lib = .{
656 .name = self.builder.dupe(lib_name),
657 .needed = false,
658 .weak = false,
659 .use_pkg_config = .force,
660 },
661 }) catch unreachable;
662}
663
664/// This links against a system library, exclusively using pkg-config to find the library.
665/// Prefer to use `linkSystemLibraryNeeded` instead.
666pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
667 self.link_objects.append(.{
668 .system_lib = .{
669 .name = self.builder.dupe(lib_name),
670 .needed = true,
671 .weak = false,
672 .use_pkg_config = .force,
673 },
674 }) catch unreachable;
675}
676
677/// Run pkg-config for the given library name and parse the output, returning the arguments
678/// that should be passed to zig to link the given library.
679pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
680 const pkg_name = match: {
681 // First we have to map the library name to pkg config name. Unfortunately,
682 // there are several examples where this is not straightforward:
683 // -lSDL2 -> pkg-config sdl2
684 // -lgdk-3 -> pkg-config gdk-3.0
685 // -latk-1.0 -> pkg-config atk
686 const pkgs = try getPkgConfigList(self.builder);
687
688 // Exact match means instant winner.
689 for (pkgs) |pkg| {
690 if (mem.eql(u8, pkg.name, lib_name)) {
691 break :match pkg.name;
692 }
693 }
694
695 // Next we'll try ignoring case.
696 for (pkgs) |pkg| {
697 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
698 break :match pkg.name;
699 }
700 }
701
702 // Now try appending ".0".
703 for (pkgs) |pkg| {
704 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
705 if (pos != 0) continue;
706 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
707 break :match pkg.name;
708 }
709 }
710 }
711
712 // Trimming "-1.0".
713 if (mem.endsWith(u8, lib_name, "-1.0")) {
714 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
715 for (pkgs) |pkg| {
716 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
717 break :match pkg.name;
718 }
719 }
720 }
721
722 return error.PackageNotFound;
723 };
724
725 var code: u8 = undefined;
726 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
727 "pkg-config",
728 pkg_name,
729 "--cflags",
730 "--libs",
731 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
732 error.ProcessTerminated => return error.PkgConfigCrashed,
733 error.ExecNotSupported => return error.PkgConfigFailed,
734 error.ExitCodeFailure => return error.PkgConfigFailed,
735 error.FileNotFound => return error.PkgConfigNotInstalled,
736 error.ChildExecFailed => return error.PkgConfigFailed,
737 else => return err,
738 };
739
740 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
741 defer zig_args.deinit();
742
743 var it = mem.tokenize(u8, stdout, " \r\n\t");
744 while (it.next()) |tok| {
745 if (mem.eql(u8, tok, "-I")) {
746 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
747 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
748 } else if (mem.startsWith(u8, tok, "-I")) {
749 try zig_args.append(tok);
750 } else if (mem.eql(u8, tok, "-L")) {
751 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
752 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
753 } else if (mem.startsWith(u8, tok, "-L")) {
754 try zig_args.append(tok);
755 } else if (mem.eql(u8, tok, "-l")) {
756 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
757 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
758 } else if (mem.startsWith(u8, tok, "-l")) {
759 try zig_args.append(tok);
760 } else if (mem.eql(u8, tok, "-D")) {
761 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
762 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
763 } else if (mem.startsWith(u8, tok, "-D")) {
764 try zig_args.append(tok);
765 } else if (self.builder.verbose) {
766 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
767 }
768 }
769
770 return zig_args.toOwnedSlice();
771}
772
773pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
774 self.linkSystemLibraryInner(name, .{});
775}
776
777pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
778 self.linkSystemLibraryInner(name, .{ .needed = true });
779}
780
781pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
782 self.linkSystemLibraryInner(name, .{ .weak = true });
783}
784
785fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
786 needed: bool = false,
787 weak: bool = false,
788}) void {
789 if (isLibCLibrary(name)) {
790 self.linkLibC();
791 return;
792 }
793 if (isLibCppLibrary(name)) {
794 self.linkLibCpp();
795 return;
796 }
797
798 self.link_objects.append(.{
799 .system_lib = .{
800 .name = self.builder.dupe(name),
801 .needed = opts.needed,
802 .weak = opts.weak,
803 .use_pkg_config = .yes,
804 },
805 }) catch unreachable;
806}
807
808pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
809 assert(self.kind == .@"test" or self.kind == .test_exe);
810 self.name_prefix = self.builder.dupe(text);
811}
812
813pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
814 assert(self.kind == .@"test" or self.kind == .test_exe);
815 self.filter = if (text) |t| self.builder.dupe(t) else null;
816}
817
818pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
819 assert(self.kind == .@"test" or self.kind == .test_exe);
820 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
821}
822
823/// Handy when you have many C/C++ source files and want them all to have the same flags.
824pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
825 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
826
827 const files_copy = self.builder.dupeStrings(files);
828 const flags_copy = self.builder.dupeStrings(flags);
829
830 c_source_files.* = .{
831 .files = files_copy,
832 .flags = flags_copy,
833 };
834 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
835}
836
837pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
838 self.addCSourceFileSource(.{
839 .args = flags,
840 .source = .{ .path = file },
841 });
842}
843
844pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
845 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
846 c_source_file.* = source.dupe(self.builder);
847 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
848 source.source.addStepDependencies(&self.step);
849}
850
851pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
852 self.verbose_link = value;
853}
854
855pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
856 self.verbose_cc = value;
857}
858
859pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
860 self.override_lib_dir = self.builder.dupePath(dir_path);
861}
862
863pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
864 self.main_pkg_path = self.builder.dupePath(dir_path);
865}
866
867pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
868 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
869}
870
871/// Returns the generated executable, library or object file.
872/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
873pub fn getOutputSource(self: *LibExeObjStep) FileSource {
874 return FileSource{ .generated = &self.output_path_source };
875}
876
877/// Returns the generated import library. This function can only be called for libraries.
878pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
879 assert(self.kind == .lib);
880 return FileSource{ .generated = &self.output_lib_path_source };
881}
882
883/// Returns the generated header file.
884/// This function can only be called for libraries or object files which have `emit_h` set.
885pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
886 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
887 assert(self.emit_h);
888 return FileSource{ .generated = &self.output_h_path_source };
889}
890
891/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
892pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
893 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
894 assert(self.target.isWindows() or self.target.isUefi());
895 return FileSource{ .generated = &self.output_pdb_path_source };
896}
897
898pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
899 self.link_objects.append(.{
900 .assembly_file = .{ .path = self.builder.dupe(path) },
901 }) catch unreachable;
902}
903
904pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
905 const source_duped = source.dupe(self.builder);
906 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
907 source_duped.addStepDependencies(&self.step);
908}
909
910pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
911 self.addObjectFileSource(.{ .path = source_file });
912}
913
914pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
915 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
916 source.addStepDependencies(&self.step);
917}
918
919pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
920 assert(obj.kind == .obj);
921 self.linkLibraryOrObject(obj);
922}
923
924pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
925pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
926pub const addLibPath = @compileError("deprecated, use addLibraryPath");
927pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
928
929pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
930 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
931}
932
933pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
934 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
935}
936
937pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
938 self.step.dependOn(&config_header.step);
939 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
940}
941
942pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
943 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
944}
945
946pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
947 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
948}
949
950pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
951 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
952}
953
954pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
955 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
956 self.addRecursiveBuildDeps(package);
957}
958
959pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
960 self.addPackage(options.getPackage(package_name));
961}
962
963fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
964 package.source.addStepDependencies(&self.step);
965 if (package.dependencies) |deps| {
966 for (deps) |dep| {
967 self.addRecursiveBuildDeps(dep);
968 }
969 }
970}
971
972pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
973 self.addPackage(Pkg{
974 .name = self.builder.dupe(name),
975 .source = .{ .path = self.builder.dupe(pkg_index_path) },
976 });
977}
978
979/// If Vcpkg was found on the system, it will be added to include and lib
980/// paths for the specified target.
981pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
982 // Ideally in the Unattempted case we would call the function recursively
983 // after findVcpkgRoot and have only one switch statement, but the compiler
984 // cannot resolve the error set.
985 switch (self.builder.vcpkg_root) {
986 .unattempted => {
987 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
988 VcpkgRoot{ .found = root }
989 else
990 .not_found;
991 },
992 .not_found => return error.VcpkgNotFound,
993 .found => {},
994 }
995
996 switch (self.builder.vcpkg_root) {
997 .unattempted => unreachable,
998 .not_found => return error.VcpkgNotFound,
999 .found => |root| {
1000 const allocator = self.builder.allocator;
1001 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1002 defer self.builder.allocator.free(triplet);
1003
1004 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1005 errdefer allocator.free(include_path);
1006 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1007
1008 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1009 try self.lib_paths.append(lib_path);
1010
1011 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1012 },
1013 }
1014}
1015
1016pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
1017 assert(self.kind == .@"test");
1018 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
1019 for (args) |arg, i| {
1020 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1021 }
1022 self.exec_cmd_args = duped_args;
1023}
1024
1025fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
1026 self.step.dependOn(&other.step);
1027 self.link_objects.append(.{ .other_step = other }) catch unreachable;
1028 self.include_dirs.append(.{ .other_step = other }) catch unreachable;
1029}
1030
1031fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1032 const builder = self.builder;
1033
1034 try zig_args.append("--pkg-begin");
1035 try zig_args.append(pkg.name);
1036 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1037
1038 if (pkg.dependencies) |dependencies| {
1039 for (dependencies) |sub_pkg| {
1040 try self.makePackageCmd(sub_pkg, zig_args);
1041 }
1042 }
1043
1044 try zig_args.append("--pkg-end");
1045}
1046
1047fn make(step: *Step) !void {
1048 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1049 const builder = self.builder;
1050
1051 if (self.root_src == null and self.link_objects.items.len == 0) {
1052 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1053 return error.NeedAnObject;
1054 }
1055
1056 var zig_args = ArrayList([]const u8).init(builder.allocator);
1057 defer zig_args.deinit();
1058
1059 zig_args.append(builder.zig_exe) catch unreachable;
1060
1061 const cmd = switch (self.kind) {
1062 .lib => "build-lib",
1063 .exe => "build-exe",
1064 .obj => "build-obj",
1065 .@"test" => "test",
1066 .test_exe => "test",
1067 };
1068 zig_args.append(cmd) catch unreachable;
1069
1070 if (builder.color != .auto) {
1071 try zig_args.append("--color");
1072 try zig_args.append(@tagName(builder.color));
1073 }
1074
1075 if (builder.reference_trace) |some| {
1076 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1077 }
1078
1079 try addFlag(&zig_args, "LLVM", self.use_llvm);
1080 try addFlag(&zig_args, "LLD", self.use_lld);
1081
1082 if (self.target.ofmt) |ofmt| {
1083 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1084 }
1085
1086 if (self.entry_symbol_name) |entry| {
1087 try zig_args.append("--entry");
1088 try zig_args.append(entry);
1089 }
1090
1091 if (self.stack_size) |stack_size| {
1092 try zig_args.append("--stack");
1093 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1094 }
1095
1096 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1097
1098 // We will add link objects from transitive dependencies, but we want to keep
1099 // all link objects in the same order provided.
1100 // This array is used to keep self.link_objects immutable.
1101 var transitive_deps: TransitiveDeps = .{
1102 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1103 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1104 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1105 .is_linking_libcpp = self.is_linking_libcpp,
1106 .is_linking_libc = self.is_linking_libc,
1107 .frameworks = &self.frameworks,
1108 };
1109
1110 try transitive_deps.seen_steps.put(&self.step, {});
1111 try transitive_deps.add(self.link_objects.items);
1112
1113 var prev_has_extra_flags = false;
1114
1115 for (transitive_deps.link_objects.items) |link_object| {
1116 switch (link_object) {
1117 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1118
1119 .other_step => |other| switch (other.kind) {
1120 .exe => @panic("Cannot link with an executable build artifact"),
1121 .test_exe => @panic("Cannot link with an executable build artifact"),
1122 .@"test" => @panic("Cannot link with a test"),
1123 .obj => {
1124 try zig_args.append(other.getOutputSource().getPath(builder));
1125 },
1126 .lib => l: {
1127 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1128 // Avoid putting a static library inside a static library.
1129 break :l;
1130 }
1131
1132 const full_path_lib = other.getOutputLibSource().getPath(builder);
1133 try zig_args.append(full_path_lib);
1134
1135 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1136 if (fs.path.dirname(full_path_lib)) |dirname| {
1137 try zig_args.append("-rpath");
1138 try zig_args.append(dirname);
1139 }
1140 }
1141 },
1142 },
1143
1144 .system_lib => |system_lib| {
1145 const prefix: []const u8 = prefix: {
1146 if (system_lib.needed) break :prefix "-needed-l";
1147 if (system_lib.weak) {
1148 if (self.target.isDarwin()) break :prefix "-weak-l";
1149 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1150 }
1151 break :prefix "-l";
1152 };
1153 switch (system_lib.use_pkg_config) {
1154 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1155 .yes, .force => {
1156 if (self.runPkgConfig(system_lib.name)) |args| {
1157 try zig_args.appendSlice(args);
1158 } else |err| switch (err) {
1159 error.PkgConfigInvalidOutput,
1160 error.PkgConfigCrashed,
1161 error.PkgConfigFailed,
1162 error.PkgConfigNotInstalled,
1163 error.PackageNotFound,
1164 => switch (system_lib.use_pkg_config) {
1165 .yes => {
1166 // pkg-config failed, so fall back to linking the library
1167 // by name directly.
1168 try zig_args.append(builder.fmt("{s}{s}", .{
1169 prefix,
1170 system_lib.name,
1171 }));
1172 },
1173 .force => {
1174 panic("pkg-config failed for library {s}", .{system_lib.name});
1175 },
1176 .no => unreachable,
1177 },
1178
1179 else => |e| return e,
1180 }
1181 },
1182 }
1183 },
1184
1185 .assembly_file => |asm_file| {
1186 if (prev_has_extra_flags) {
1187 try zig_args.append("-extra-cflags");
1188 try zig_args.append("--");
1189 prev_has_extra_flags = false;
1190 }
1191 try zig_args.append(asm_file.getPath(builder));
1192 },
1193
1194 .c_source_file => |c_source_file| {
1195 if (c_source_file.args.len == 0) {
1196 if (prev_has_extra_flags) {
1197 try zig_args.append("-cflags");
1198 try zig_args.append("--");
1199 prev_has_extra_flags = false;
1200 }
1201 } else {
1202 try zig_args.append("-cflags");
1203 for (c_source_file.args) |arg| {
1204 try zig_args.append(arg);
1205 }
1206 try zig_args.append("--");
1207 }
1208 try zig_args.append(c_source_file.source.getPath(builder));
1209 },
1210
1211 .c_source_files => |c_source_files| {
1212 if (c_source_files.flags.len == 0) {
1213 if (prev_has_extra_flags) {
1214 try zig_args.append("-cflags");
1215 try zig_args.append("--");
1216 prev_has_extra_flags = false;
1217 }
1218 } else {
1219 try zig_args.append("-cflags");
1220 for (c_source_files.flags) |flag| {
1221 try zig_args.append(flag);
1222 }
1223 try zig_args.append("--");
1224 }
1225 for (c_source_files.files) |file| {
1226 try zig_args.append(builder.pathFromRoot(file));
1227 }
1228 },
1229 }
1230 }
1231
1232 if (transitive_deps.is_linking_libcpp) {
1233 try zig_args.append("-lc++");
1234 }
1235
1236 if (transitive_deps.is_linking_libc) {
1237 try zig_args.append("-lc");
1238 }
1239
1240 if (self.image_base) |image_base| {
1241 try zig_args.append("--image-base");
1242 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1243 }
1244
1245 if (self.filter) |filter| {
1246 try zig_args.append("--test-filter");
1247 try zig_args.append(filter);
1248 }
1249
1250 if (self.test_evented_io) {
1251 try zig_args.append("--test-evented-io");
1252 }
1253
1254 if (self.name_prefix.len != 0) {
1255 try zig_args.append("--test-name-prefix");
1256 try zig_args.append(self.name_prefix);
1257 }
1258
1259 if (self.test_runner) |test_runner| {
1260 try zig_args.append("--test-runner");
1261 try zig_args.append(builder.pathFromRoot(test_runner));
1262 }
1263
1264 for (builder.debug_log_scopes) |log_scope| {
1265 try zig_args.append("--debug-log");
1266 try zig_args.append(log_scope);
1267 }
1268
1269 if (builder.debug_compile_errors) {
1270 try zig_args.append("--debug-compile-errors");
1271 }
1272
1273 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
1274 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
1275 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
1276 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
1277 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
1278 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
1279
1280 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1281 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1282 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1283 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1284 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1285 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1286 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1287
1288 if (self.emit_h) try zig_args.append("-femit-h");
1289
1290 try addFlag(&zig_args, "strip", self.strip);
1291 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1292
1293 switch (self.compress_debug_sections) {
1294 .none => {},
1295 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1296 }
1297
1298 if (self.link_eh_frame_hdr) {
1299 try zig_args.append("--eh-frame-hdr");
1300 }
1301 if (self.link_emit_relocs) {
1302 try zig_args.append("--emit-relocs");
1303 }
1304 if (self.link_function_sections) {
1305 try zig_args.append("-ffunction-sections");
1306 }
1307 if (self.link_gc_sections) |x| {
1308 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1309 }
1310 if (self.linker_allow_shlib_undefined) |x| {
1311 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1312 }
1313 if (self.link_z_notext) {
1314 try zig_args.append("-z");
1315 try zig_args.append("notext");
1316 }
1317 if (!self.link_z_relro) {
1318 try zig_args.append("-z");
1319 try zig_args.append("norelro");
1320 }
1321 if (self.link_z_lazy) {
1322 try zig_args.append("-z");
1323 try zig_args.append("lazy");
1324 }
1325 if (self.link_z_common_page_size) |size| {
1326 try zig_args.append("-z");
1327 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1328 }
1329 if (self.link_z_max_page_size) |size| {
1330 try zig_args.append("-z");
1331 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1332 }
1333
1334 if (self.libc_file) |libc_file| {
1335 try zig_args.append("--libc");
1336 try zig_args.append(libc_file.getPath(builder));
1337 } else if (builder.libc_file) |libc_file| {
1338 try zig_args.append("--libc");
1339 try zig_args.append(libc_file);
1340 }
1341
1342 switch (self.optimize) {
1343 .Debug => {}, // Skip since it's the default.
1344 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable,
1345 }
1346
1347 try zig_args.append("--cache-dir");
1348 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1349
1350 try zig_args.append("--global-cache-dir");
1351 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1352
1353 zig_args.append("--name") catch unreachable;
1354 zig_args.append(self.name) catch unreachable;
1355
1356 if (self.linkage) |some| switch (some) {
1357 .dynamic => try zig_args.append("-dynamic"),
1358 .static => try zig_args.append("-static"),
1359 };
1360 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1361 if (self.version) |version| {
1362 zig_args.append("--version") catch unreachable;
1363 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
1364 }
1365
1366 if (self.target.isDarwin()) {
1367 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1368 self.target.libPrefix(),
1369 self.name,
1370 self.target.dynamicLibSuffix(),
1371 });
1372 try zig_args.append("-install_name");
1373 try zig_args.append(install_name);
1374 }
1375 }
1376
1377 if (self.entitlements) |entitlements| {
1378 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1379 }
1380 if (self.pagezero_size) |pagezero_size| {
1381 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1382 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1383 }
1384 if (self.search_strategy) |strat| switch (strat) {
1385 .paths_first => try zig_args.append("-search_paths_first"),
1386 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1387 };
1388 if (self.headerpad_size) |headerpad_size| {
1389 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1390 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1391 }
1392 if (self.headerpad_max_install_names) {
1393 try zig_args.append("-headerpad_max_install_names");
1394 }
1395 if (self.dead_strip_dylibs) {
1396 try zig_args.append("-dead_strip_dylibs");
1397 }
1398
1399 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1400 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1401 if (self.disable_stack_probing) {
1402 try zig_args.append("-fno-stack-check");
1403 }
1404 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1405 if (self.red_zone) |red_zone| {
1406 if (red_zone) {
1407 try zig_args.append("-mred-zone");
1408 } else {
1409 try zig_args.append("-mno-red-zone");
1410 }
1411 }
1412 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1413 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1414
1415 if (self.disable_sanitize_c) {
1416 try zig_args.append("-fno-sanitize-c");
1417 }
1418 if (self.sanitize_thread) {
1419 try zig_args.append("-fsanitize-thread");
1420 }
1421 if (self.rdynamic) {
1422 try zig_args.append("-rdynamic");
1423 }
1424 if (self.import_memory) {
1425 try zig_args.append("--import-memory");
1426 }
1427 if (self.import_symbols) {
1428 try zig_args.append("--import-symbols");
1429 }
1430 if (self.import_table) {
1431 try zig_args.append("--import-table");
1432 }
1433 if (self.export_table) {
1434 try zig_args.append("--export-table");
1435 }
1436 if (self.initial_memory) |initial_memory| {
1437 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1438 }
1439 if (self.max_memory) |max_memory| {
1440 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1441 }
1442 if (self.shared_memory) {
1443 try zig_args.append("--shared-memory");
1444 }
1445 if (self.global_base) |global_base| {
1446 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1447 }
1448
1449 if (self.code_model != .default) {
1450 try zig_args.append("-mcmodel");
1451 try zig_args.append(@tagName(self.code_model));
1452 }
1453 if (self.wasi_exec_model) |model| {
1454 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1455 }
1456 for (self.export_symbol_names) |symbol_name| {
1457 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1458 }
1459
1460 if (!self.target.isNative()) {
1461 try zig_args.appendSlice(&.{
1462 "-target", try self.target.zigTriple(builder.allocator),
1463 "-mcpu", try build.serializeCpu(builder.allocator, self.target.getCpu()),
1464 });
1465
1466 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1467 try zig_args.append("--dynamic-linker");
1468 try zig_args.append(dynamic_linker);
1469 }
1470 }
1471
1472 if (self.linker_script) |linker_script| {
1473 try zig_args.append("--script");
1474 try zig_args.append(linker_script.getPath(builder));
1475 }
1476
1477 if (self.version_script) |version_script| {
1478 try zig_args.append("--version-script");
1479 try zig_args.append(builder.pathFromRoot(version_script));
1480 }
1481
1482 if (self.kind == .@"test") {
1483 if (self.exec_cmd_args) |exec_cmd_args| {
1484 for (exec_cmd_args) |cmd_arg| {
1485 if (cmd_arg) |arg| {
1486 try zig_args.append("--test-cmd");
1487 try zig_args.append(arg);
1488 } else {
1489 try zig_args.append("--test-cmd-bin");
1490 }
1491 }
1492 } else {
1493 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1494
1495 switch (builder.host.getExternalExecutor(self.target_info, .{
1496 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1497 .link_libc = transitive_deps.is_linking_libc,
1498 })) {
1499 .native => {},
1500 .bad_dl, .bad_os_or_cpu => {
1501 try zig_args.append("--test-no-exec");
1502 },
1503 .rosetta => if (builder.enable_rosetta) {
1504 try zig_args.append("--test-cmd-bin");
1505 } else {
1506 try zig_args.append("--test-no-exec");
1507 },
1508 .qemu => |bin_name| ok: {
1509 if (builder.enable_qemu) qemu: {
1510 const glibc_dir_arg = if (need_cross_glibc)
1511 builder.glibc_runtimes_dir orelse break :qemu
1512 else
1513 null;
1514 try zig_args.append("--test-cmd");
1515 try zig_args.append(bin_name);
1516 if (glibc_dir_arg) |dir| {
1517 // TODO look into making this a call to `linuxTriple`. This
1518 // needs the directory to be called "i686" rather than
1519 // "x86" which is why we do it manually here.
1520 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1521 const cpu_arch = self.target.getCpuArch();
1522 const os_tag = self.target.getOsTag();
1523 const abi = self.target.getAbi();
1524 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1525 "i686"
1526 else
1527 @tagName(cpu_arch);
1528 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1529 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1530 });
1531
1532 try zig_args.append("--test-cmd");
1533 try zig_args.append("-L");
1534 try zig_args.append("--test-cmd");
1535 try zig_args.append(full_dir);
1536 }
1537 try zig_args.append("--test-cmd-bin");
1538 break :ok;
1539 }
1540 try zig_args.append("--test-no-exec");
1541 },
1542 .wine => |bin_name| if (builder.enable_wine) {
1543 try zig_args.append("--test-cmd");
1544 try zig_args.append(bin_name);
1545 try zig_args.append("--test-cmd-bin");
1546 } else {
1547 try zig_args.append("--test-no-exec");
1548 },
1549 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1550 try zig_args.append("--test-cmd");
1551 try zig_args.append(bin_name);
1552 try zig_args.append("--test-cmd");
1553 try zig_args.append("--dir=.");
1554 try zig_args.append("--test-cmd-bin");
1555 } else {
1556 try zig_args.append("--test-no-exec");
1557 },
1558 .darling => |bin_name| if (builder.enable_darling) {
1559 try zig_args.append("--test-cmd");
1560 try zig_args.append(bin_name);
1561 try zig_args.append("--test-cmd-bin");
1562 } else {
1563 try zig_args.append("--test-no-exec");
1564 },
1565 }
1566 }
1567 } else if (self.kind == .test_exe) {
1568 try zig_args.append("--test-no-exec");
1569 }
1570
1571 for (self.packages.items) |pkg| {
1572 try self.makePackageCmd(pkg, &zig_args);
1573 }
1574
1575 for (self.include_dirs.items) |include_dir| {
1576 switch (include_dir) {
1577 .raw_path => |include_path| {
1578 try zig_args.append("-I");
1579 try zig_args.append(builder.pathFromRoot(include_path));
1580 },
1581 .raw_path_system => |include_path| {
1582 if (builder.sysroot != null) {
1583 try zig_args.append("-iwithsysroot");
1584 } else {
1585 try zig_args.append("-isystem");
1586 }
1587
1588 const resolved_include_path = builder.pathFromRoot(include_path);
1589
1590 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1591 // We need to check for disk designator and strip it out from dir path so
1592 // that zig/clang can concat resolved_include_path with sysroot.
1593 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1594
1595 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1596 break :blk resolved_include_path[where + disk_designator.len ..];
1597 }
1598
1599 break :blk resolved_include_path;
1600 } else resolved_include_path;
1601
1602 try zig_args.append(common_include_path);
1603 },
1604 .other_step => |other| {
1605 if (other.emit_h) {
1606 const h_path = other.getOutputHSource().getPath(builder);
1607 try zig_args.append("-isystem");
1608 try zig_args.append(fs.path.dirname(h_path).?);
1609 }
1610 if (other.installed_headers.items.len > 0) {
1611 for (other.installed_headers.items) |install_step| {
1612 try install_step.make();
1613 }
1614 try zig_args.append("-I");
1615 try zig_args.append(builder.pathJoin(&.{
1616 other.builder.install_prefix, "include",
1617 }));
1618 }
1619 },
1620 .config_header_step => |config_header| {
1621 try zig_args.append("-I");
1622 try zig_args.append(config_header.output_dir);
1623 },
1624 }
1625 }
1626
1627 for (self.lib_paths.items) |lib_path| {
1628 try zig_args.append("-L");
1629 try zig_args.append(lib_path);
1630 }
1631
1632 for (self.rpaths.items) |rpath| {
1633 try zig_args.append("-rpath");
1634 try zig_args.append(rpath);
1635 }
1636
1637 for (self.c_macros.items) |c_macro| {
1638 try zig_args.append("-D");
1639 try zig_args.append(c_macro);
1640 }
1641
1642 if (self.target.isDarwin()) {
1643 for (self.framework_dirs.items) |dir| {
1644 if (builder.sysroot != null) {
1645 try zig_args.append("-iframeworkwithsysroot");
1646 } else {
1647 try zig_args.append("-iframework");
1648 }
1649 try zig_args.append(dir);
1650 try zig_args.append("-F");
1651 try zig_args.append(dir);
1652 }
1653
1654 var it = self.frameworks.iterator();
1655 while (it.next()) |entry| {
1656 const name = entry.key_ptr.*;
1657 const info = entry.value_ptr.*;
1658 if (info.needed) {
1659 zig_args.append("-needed_framework") catch unreachable;
1660 } else if (info.weak) {
1661 zig_args.append("-weak_framework") catch unreachable;
1662 } else {
1663 zig_args.append("-framework") catch unreachable;
1664 }
1665 zig_args.append(name) catch unreachable;
1666 }
1667 } else {
1668 if (self.framework_dirs.items.len > 0) {
1669 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1670 }
1671
1672 if (self.frameworks.count() > 0) {
1673 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1674 }
1675 }
1676
1677 if (builder.sysroot) |sysroot| {
1678 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1679 }
1680
1681 for (builder.search_prefixes.items) |search_prefix| {
1682 try zig_args.append("-L");
1683 try zig_args.append(builder.pathJoin(&.{
1684 search_prefix, "lib",
1685 }));
1686 try zig_args.append("-I");
1687 try zig_args.append(builder.pathJoin(&.{
1688 search_prefix, "include",
1689 }));
1690 }
1691
1692 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1693 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1694 try addFlag(&zig_args, "build-id", self.build_id);
1695
1696 if (self.override_lib_dir) |dir| {
1697 try zig_args.append("--zig-lib-dir");
1698 try zig_args.append(builder.pathFromRoot(dir));
1699 } else if (builder.override_lib_dir) |dir| {
1700 try zig_args.append("--zig-lib-dir");
1701 try zig_args.append(builder.pathFromRoot(dir));
1702 }
1703
1704 if (self.main_pkg_path) |dir| {
1705 try zig_args.append("--main-pkg-path");
1706 try zig_args.append(builder.pathFromRoot(dir));
1707 }
1708
1709 try addFlag(&zig_args, "PIC", self.force_pic);
1710 try addFlag(&zig_args, "PIE", self.pie);
1711 try addFlag(&zig_args, "lto", self.want_lto);
1712
1713 if (self.subsystem) |subsystem| {
1714 try zig_args.append("--subsystem");
1715 try zig_args.append(switch (subsystem) {
1716 .Console => "console",
1717 .Windows => "windows",
1718 .Posix => "posix",
1719 .Native => "native",
1720 .EfiApplication => "efi_application",
1721 .EfiBootServiceDriver => "efi_boot_service_driver",
1722 .EfiRom => "efi_rom",
1723 .EfiRuntimeDriver => "efi_runtime_driver",
1724 });
1725 }
1726
1727 try zig_args.append("--enable-cache");
1728
1729 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1730 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1731 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1732 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1733 var args_length: usize = 0;
1734 for (zig_args.items) |arg| {
1735 args_length += arg.len + 1; // +1 to account for null terminator
1736 }
1737 if (args_length >= 30 * 1024) {
1738 const args_dir = try fs.path.join(
1739 builder.allocator,
1740 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1741 );
1742 try std.fs.cwd().makePath(args_dir);
1743
1744 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1745 defer args_arena.deinit();
1746
1747 const args_to_escape = zig_args.items[2..];
1748 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1749
1750 arg_blk: for (args_to_escape) |arg| {
1751 for (arg) |c, arg_idx| {
1752 if (c == '\\' or c == '"') {
1753 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1754 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1755 const writer = escaped.writer();
1756 writer.writeAll(arg[0..arg_idx]) catch unreachable;
1757 for (arg[arg_idx..]) |to_escape| {
1758 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1759 try writer.writeByte(to_escape);
1760 }
1761 escaped_args.appendAssumeCapacity(escaped.items);
1762 continue :arg_blk;
1763 }
1764 }
1765 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1766 }
1767
1768 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1769 // other zig build commands running in parallel.
1770 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1771 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1772
1773 var args_hash: [Sha256.digest_length]u8 = undefined;
1774 Sha256.hash(args, &args_hash, .{});
1775 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1776 _ = try std.fmt.bufPrint(
1777 &args_hex_hash,
1778 "{s}",
1779 .{std.fmt.fmtSliceHexLower(&args_hash)},
1780 );
1781
1782 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1783 try std.fs.cwd().writeFile(args_file, args);
1784
1785 zig_args.shrinkRetainingCapacity(2);
1786 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1787 }
1788
1789 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1790 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1791
1792 if (self.output_dir) |output_dir| {
1793 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1794 defer src_dir.close();
1795
1796 // Create the output directory if it doesn't exist.
1797 try std.fs.cwd().makePath(output_dir);
1798
1799 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1800 defer dest_dir.close();
1801
1802 var it = src_dir.iterate();
1803 while (try it.next()) |entry| {
1804 // The compiler can put these files into the same directory, but we don't
1805 // want to copy them over.
1806 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1807 mem.eql(u8, entry.name, "libs.txt") or
1808 mem.eql(u8, entry.name, "builtin.zig") or
1809 mem.eql(u8, entry.name, "zld.id") or
1810 mem.eql(u8, entry.name, "lld.id")) continue;
1811
1812 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1813 }
1814 } else {
1815 self.output_dir = build_output_dir;
1816 }
1817
1818 // This will ensure all output filenames will now have the output_dir available!
1819 self.computeOutFileNames();
1820
1821 // Update generated files
1822 if (self.output_dir != null) {
1823 self.output_path_source.path = builder.pathJoin(
1824 &.{ self.output_dir.?, self.out_filename },
1825 );
1826
1827 if (self.emit_h) {
1828 self.output_h_path_source.path = builder.pathJoin(
1829 &.{ self.output_dir.?, self.out_h_filename },
1830 );
1831 }
1832
1833 if (self.target.isWindows() or self.target.isUefi()) {
1834 self.output_pdb_path_source.path = builder.pathJoin(
1835 &.{ self.output_dir.?, self.out_pdb_filename },
1836 );
1837 }
1838 }
1839
1840 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1841 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1842 }
1843}
1844
1845fn isLibCLibrary(name: []const u8) bool {
1846 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1847 for (libc_libraries) |libc_lib_name| {
1848 if (mem.eql(u8, name, libc_lib_name))
1849 return true;
1850 }
1851 return false;
1852}
1853
1854fn isLibCppLibrary(name: []const u8) bool {
1855 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1856 for (libcpp_libraries) |libcpp_lib_name| {
1857 if (mem.eql(u8, name, libcpp_lib_name))
1858 return true;
1859 }
1860 return false;
1861}
1862
1863/// Returned slice must be freed by the caller.
1864fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1865 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1866 defer allocator.free(appdata_path);
1867
1868 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1869 defer allocator.free(path_file);
1870
1871 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1872 defer file.close();
1873
1874 const size = @intCast(usize, try file.getEndPos());
1875 const vcpkg_path = try allocator.alloc(u8, size);
1876 const size_read = try file.read(vcpkg_path);
1877 std.debug.assert(size == size_read);
1878
1879 return vcpkg_path;
1880}
1881
1882pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !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 = fs.path.join(
1887 allocator,
1888 &[_][]const u8{ out_dir, filename_major_only },
1889 ) catch unreachable;
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 = fs.path.join(
1896 allocator,
1897 &[_][]const u8{ out_dir, filename_name_only },
1898 ) catch unreachable;
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: *Builder, 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: *Builder) ![]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 var builder = try Builder.create(
1952 arena.allocator(),
1953 "test",
1954 "test",
1955 "test",
1956 "test",
1957 );
1958 defer builder.destroy();
1959
1960 const pkg_dep = Pkg{
1961 .name = "pkg_dep",
1962 .source = .{ .path = "/not/a/pkg_dep.zig" },
1963 };
1964 const pkg_top = Pkg{
1965 .name = "pkg_dep",
1966 .source = .{ .path = "/not/a/pkg_top.zig" },
1967 .dependencies = &[_]Pkg{pkg_dep},
1968 };
1969
1970 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
1971 exe.addPackage(pkg_top);
1972
1973 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
1974
1975 const dupe = exe.packages.items[0];
1976 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1977}
1978
1979fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
1980 const cond = opt orelse return;
1981 try args.ensureUnusedCapacity(1);
1982 if (cond) {
1983 args.appendAssumeCapacity("-f" ++ name);
1984 } else {
1985 args.appendAssumeCapacity("-fno-" ++ name);
1986 }
1987}
1988
1989const TransitiveDeps = struct {
1990 link_objects: ArrayList(LinkObject),
1991 seen_system_libs: StringHashMap(void),
1992 seen_steps: std.AutoHashMap(*const Step, void),
1993 is_linking_libcpp: bool,
1994 is_linking_libc: bool,
1995 frameworks: *StringHashMap(FrameworkLinkInfo),
1996
1997 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
1998 try td.link_objects.ensureUnusedCapacity(link_objects.len);
1999
2000 for (link_objects) |link_object| {
2001 try td.link_objects.append(link_object);
2002 switch (link_object) {
2003 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2004 else => {},
2005 }
2006 }
2007 }
2008
2009 fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
2010 // Inherit dependency on libc and libc++
2011 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2012 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2013
2014 // Inherit dependencies on darwin frameworks
2015 if (!dyn) {
2016 var it = other.frameworks.iterator();
2017 while (it.next()) |framework| {
2018 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2019 }
2020 }
2021
2022 // Inherit dependencies on system libraries and static libraries.
2023 for (other.link_objects.items) |other_link_object| {
2024 switch (other_link_object) {
2025 .system_lib => |system_lib| {
2026 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2027 continue;
2028
2029 if (dyn)
2030 continue;
2031
2032 try td.link_objects.append(other_link_object);
2033 },
2034 .other_step => |inner_other| {
2035 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2036 continue;
2037
2038 if (!dyn)
2039 try td.link_objects.append(other_link_object);
2040
2041 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2042 },
2043 else => continue,
2044 }
2045 }
2046 }
2047};
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-138
......@@ -1,138 +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,
23optimize: std.builtin.OptimizeMode,
24output_file: build.GeneratedFile,
25
26pub const Options = struct {
27 source_file: build.FileSource,
28 target: CrossTarget,
29 optimize: std.builtin.OptimizeMode,
30};
31
32pub fn create(builder: *Builder, options: Options) *TranslateCStep {
33 const self = builder.allocator.create(TranslateCStep) catch unreachable;
34 const source = options.source_file.dupe(builder);
35 self.* = TranslateCStep{
36 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
37 .builder = builder,
38 .source = source,
39 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
40 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
41 .output_dir = null,
42 .out_basename = undefined,
43 .target = options.target,
44 .optimize = options.optimize,
45 .output_file = build.GeneratedFile{ .step = &self.step },
46 };
47 source.addStepDependencies(&self.step);
48 return self;
49}
50
51pub const AddExecutableOptions = struct {
52 name: ?[]const u8 = null,
53 version: ?std.builtin.Version = null,
54 target: ?CrossTarget = null,
55 optimize: ?std.builtin.Mode = null,
56 linkage: ?LibExeObjStep.Linkage = null,
57};
58
59/// Creates a step to build an executable from the translated source.
60pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *LibExeObjStep {
61 return self.builder.addExecutable(.{
62 .root_source_file = .{ .generated = &self.output_file },
63 .name = options.name orelse "translated_c",
64 .version = options.version,
65 .target = options.target orelse self.target,
66 .optimize = options.optimize orelse self.optimize,
67 .linkage = options.linkage,
68 });
69}
70
71pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
72 self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
73}
74
75pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
76 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
77}
78
79/// If the value is omitted, it is set to 1.
80/// `name` and `value` need not live longer than the function call.
81pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
82 const macro = build.constructCMacro(self.builder.allocator, name, value);
83 self.c_macros.append(macro) catch unreachable;
84}
85
86/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
87pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
88 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
89}
90
91fn make(step: *Step) !void {
92 const self = @fieldParentPtr(TranslateCStep, "step", step);
93
94 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
95 try argv_list.append(self.builder.zig_exe);
96 try argv_list.append("translate-c");
97 try argv_list.append("-lc");
98
99 try argv_list.append("--enable-cache");
100
101 if (!self.target.isNative()) {
102 try argv_list.append("-target");
103 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
104 }
105
106 switch (self.optimize) {
107 .Debug => {}, // Skip since it's the default.
108 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
109 }
110
111 for (self.include_dirs.items) |include_dir| {
112 try argv_list.append("-I");
113 try argv_list.append(include_dir);
114 }
115
116 for (self.c_macros.items) |c_macro| {
117 try argv_list.append("-D");
118 try argv_list.append(c_macro);
119 }
120
121 try argv_list.append(self.source.getPath(self.builder));
122
123 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
124 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
125
126 self.out_basename = fs.path.basename(output_path);
127 if (self.output_dir) |output_dir| {
128 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
129 try self.builder.updateFile(output_path, full_dest);
130 } else {
131 self.output_dir = fs.path.dirname(output_path).?;
132 }
133
134 self.output_file.path = fs.path.join(
135 self.builder.allocator,
136 &[_][]const u8{ self.output_dir.?, self.out_basename },
137 ) catch unreachable;
138}
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/std.zig+7-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,12 @@ pub const wasm = @import("wasm.zig");
9696pub const zig = @import("zig.zig");
9797pub const start = @import("start.zig");
9898
99///// Deprecated. Use `std.Build` instead.
100//pub const build = struct {
101// /// Deprecated. Use `std.Build` instead.
102// pub const Builder = Build;
103//};
104
99105const root = @import("root");
100106const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
101107
test/link/bss/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55 const test_step = b.step("test", "Test");
66
test/link/common_symbols/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55
66 const lib_a = b.addStaticLibrary(.{
test/link/common_symbols_alignment/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55 const target = b.standardTargetOptions(.{});
66
test/link/interdependent_static_c_libs/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55 const target = b.standardTargetOptions(.{});
66
test/link/macho/bugs/13056/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
test/link/macho/bugs/13457/build.zig+2-3
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87
test/link/macho/dead_strip/build.zig+3-4
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87
......@@ -37,7 +36,7 @@ pub fn build(b: *Builder) void {
3736 }
3837}
3938
40fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
39fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
4140 const exe = b.addExecutable(.{
4241 .name = "test",
4342 .optimize = optimize,
test/link/macho/dead_strip_dylibs/build.zig+3-4
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76
87 const test_step = b.step("test", "Test the program");
......@@ -36,7 +35,7 @@ pub fn build(b: *Builder) void {
3635 }
3736}
3837
39fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
38fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
4039 const exe = b.addExecutable(.{
4140 .name = "test",
4241 .optimize = optimize,
test/link/macho/dylib/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
test/link/macho/empty/build.zig+2-3
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
......@@ -17,7 +16,7 @@ pub fn build(b: *Builder) void {
1716 exe.addCSourceFile("empty.c", &[0][]const u8{});
1817 exe.linkLibC();
1918
20 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
19 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
2120 run_cmd.expectStdOutEqual("Hello!\n");
2221 test_step.dependOn(&run_cmd.step);
2322}
test/link/macho/entry/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test");
test/link/macho/headerpad/build.zig+3-4
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
3const LibExeObjectStep = std.Build.LibExeObjStep;
54
6pub fn build(b: *Builder) void {
5pub fn build(b: *std.Build) void {
76 const optimize = b.standardOptimizeOption(.{});
87
98 const test_step = b.step("test", "Test");
......@@ -94,7 +93,7 @@ pub fn build(b: *Builder) void {
9493 }
9594}
9695
97fn simpleExe(b: *Builder, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
96fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
9897 const exe = b.addExecutable(.{
9998 .name = "main",
10099 .optimize = optimize,
test/link/macho/linksection/build.zig+1-1
......@@ -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 optimize = b.standardOptimizeOption(.{});
55 const target = std.zig.CrossTarget{ .os_tag = .macos };
66
test/link/macho/needed_framework/build.zig+2-3
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76
87 const test_step = b.step("test", "Test the program");
test/link/macho/needed_library/build.zig+2-3
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87
test/link/macho/objc/build.zig+2-3
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test the program");
......@@ -18,6 +17,6 @@ pub fn build(b: *Builder) void {
1817 // populate paths to the sysroot here.
1918 exe.linkFramework("Foundation");
2019
21 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
20 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
2221 test_step.dependOn(&run_cmd.step);
2322}
test/link/macho/objcpp/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test the program");
test/link/macho/pagezero/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
test/link/macho/search_strategy/build.zig+6-7
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87
......@@ -29,14 +28,14 @@ pub fn build(b: *Builder) void {
2928 const exe = createScenario(b, optimize, target);
3029 exe.search_strategy = .paths_first;
3130
32 const run = std.build.EmulatableRunStep.create(b, "run", exe);
31 const run = std.Build.EmulatableRunStep.create(b, "run", exe);
3332 run.cwd = b.pathFromRoot(".");
3433 run.expectStdOutEqual("Hello world");
3534 test_step.dependOn(&run.step);
3635 }
3736}
3837
39fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
38fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
4039 const static = b.addStaticLibrary(.{
4140 .name = "a",
4241 .optimize = optimize,
......@@ -44,7 +43,7 @@ fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.z
4443 });
4544 static.addCSourceFile("a.c", &.{});
4645 static.linkLibC();
47 static.override_dest_dir = std.build.InstallDir{
46 static.override_dest_dir = std.Build.InstallDir{
4847 .custom = "static",
4948 };
5049 static.install();
......@@ -57,7 +56,7 @@ fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.z
5756 });
5857 dylib.addCSourceFile("a.c", &.{});
5958 dylib.linkLibC();
60 dylib.override_dest_dir = std.build.InstallDir{
59 dylib.override_dest_dir = std.Build.InstallDir{
6160 .custom = "dynamic",
6261 };
6362 dylib.install();
test/link/macho/stack_size/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
test/link/macho/strict_validation/build.zig+2-3
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
3const LibExeObjectStep = std.Build.LibExeObjStep;
54
6pub fn build(b: *Builder) void {
5pub fn build(b: *std.Build) void {
76 const optimize = b.standardOptimizeOption(.{});
87 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
98
test/link/macho/tls/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
test/link/macho/unwind_info/build.zig+5-6
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
3const LibExeObjectStep = std.Build.LibExeObjStep;
54
6pub fn build(b: *Builder) void {
5pub fn build(b: *std.Build) void {
76 const optimize = b.standardOptimizeOption(.{});
87 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
98
......@@ -14,8 +13,8 @@ pub fn build(b: *Builder) void {
1413}
1514
1615fn testUnwindInfo(
17 b: *Builder,
18 test_step: *std.build.Step,
16 b: *std.Build,
17 test_step: *std.Build.Step,
1918 optimize: std.builtin.OptimizeMode,
2019 target: std.zig.CrossTarget,
2120 dead_strip: bool,
......@@ -52,7 +51,7 @@ fn testUnwindInfo(
5251 test_step.dependOn(&run_cmd.step);
5352}
5453
55fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
54fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
5655 const exe = b.addExecutable(.{
5756 .name = "test",
5857 .optimize = optimize,
test/link/macho/uuid/build.zig+5-6
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const test_step = b.step("test", "Test");
76 test_step.dependOn(b.getInstallStep());
87
......@@ -27,8 +26,8 @@ pub fn build(b: *Builder) void {
2726}
2827
2928fn testUuid(
30 b: *Builder,
31 test_step: *std.build.Step,
29 b: *std.Build,
30 test_step: *std.Build.Step,
3231 optimize: std.builtin.OptimizeMode,
3332 target: std.zig.CrossTarget,
3433 comptime exp: []const u8,
......@@ -52,7 +51,7 @@ fn testUuid(
5251 }
5352}
5453
55fn simpleDylib(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
54fn simpleDylib(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
5655 const dylib = b.addSharedLibrary(.{
5756 .name = "test",
5857 .version = .{ .major = 1, .minor = 0 },
test/link/macho/weak_framework/build.zig+2-3
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76
87 const test_step = b.step("test", "Test the program");
test/link/macho/weak_library/build.zig+2-3
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
2const LibExeObjectStep = std.Build.LibExeObjStep;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const optimize = b.standardOptimizeOption(.{});
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87
test/link/static_lib_as_system_lib/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target = b.standardTargetOptions(.{});
76
test/link/wasm/archive/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/link/wasm/basic-features/build.zig+1-1
......@@ -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 // Library with explicitly set cpu features
55 const lib = b.addSharedLibrary(.{
66 .name = "lib",
test/link/wasm/bss/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/link/wasm/export-data/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/link/wasm/export/build.zig+1-1
......@@ -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 optimize = b.standardOptimizeOption(.{});
55
66 const no_export = b.addSharedLibrary(.{
test/link/wasm/extern-mangle/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/link/wasm/extern/build.zig+1-1
......@@ -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 exe = b.addExecutable(.{
55 .name = "extern",
66 .root_source_file = .{ .path = "main.zig" },
test/link/wasm/function-table/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test");
test/link/wasm/infer-features/build.zig+1-1
......@@ -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 optimize = b.standardOptimizeOption(.{});
55
66 // Wasm Object file which we will use to infer the features from
test/link/wasm/producers/build.zig+1-2
......@@ -1,8 +1,7 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43
5pub fn build(b: *Builder) void {
4pub fn build(b: *std.Build) void {
65 const test_step = b.step("test", "Test");
76 test_step.dependOn(b.getInstallStep());
87
test/link/wasm/segments/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/link/wasm/stack_pointer/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/link/wasm/type/build.zig+1-2
......@@ -1,7 +1,6 @@
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
test/src/compare_output.zig+2-3
......@@ -1,7 +1,6 @@
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;
......@@ -9,8 +8,8 @@ const fs = std.fs;
98const 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,
1615 optimize_modes: []const OptimizeMode,
test/src/run_translated_c.zig+2-3
......@@ -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,
test/src/translate_c.zig+2-3
......@@ -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
test/standalone/brace_expansion/build.zig+2-2
......@@ -1,6 +1,6 @@
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 main = b.addTest(.{
55 .root_source_file = .{ .path = "main.zig" },
66 .optimize = b.standardOptimizeOption(.{}),
test/standalone/c_compiler/build.zig+2-3
......@@ -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,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
13pub fn build(b: *std.Build) void {
1514 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
test/standalone/emit_asm_and_bin/build.zig+2-2
......@@ -1,6 +1,6 @@
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 main = b.addTest(.{
55 .root_source_file = .{ .path = "main.zig" },
66 .optimize = b.standardOptimizeOption(.{}),
test/standalone/empty_env/build.zig+2-2
......@@ -1,6 +1,6 @@
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 main = b.addExecutable(.{
55 .name = "main",
66 .root_source_file = .{ .path = "main.zig" },
test/standalone/global_linkage/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55
66 const obj1 = b.addStaticLibrary(.{
test/standalone/install_raw_hex/build.zig+2-2
......@@ -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 },
test/standalone/issue_11595/build.zig+2-3
......@@ -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,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
13pub fn build(b: *std.Build) void {
1514 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
test/standalone/issue_12588/build.zig+1-2
......@@ -1,7 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
65 const target = b.standardTargetOptions(.{});
76
test/standalone/issue_12706/build.zig+2-3
......@@ -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,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
13pub fn build(b: *std.Build) void {
1514 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
test/standalone/issue_13030/build.zig+1-2
......@@ -1,9 +1,8 @@
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 {
5pub fn build(b: *std.Build) void {
76 const optimize = b.standardOptimizeOption(.{});
87 const target = b.standardTargetOptions(.{});
98
test/standalone/issue_339/build.zig+2-2
......@@ -1,6 +1,6 @@
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 obj = b.addObject(.{
55 .name = "test",
66 .root_source_file = .{ .path = "test.zig" },
test/standalone/issue_5825/build.zig+2-2
......@@ -1,6 +1,6 @@
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,
test/standalone/issue_7030/build.zig+2-2
......@@ -1,6 +1,6 @@
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 exe = b.addExecutable(.{
55 .name = "issue_7030",
66 .root_source_file = .{ .path = "main.zig" },
test/standalone/issue_794/build.zig+2-2
......@@ -1,6 +1,6 @@
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 test_artifact = b.addTest(.{
55 .root_source_file = .{ .path = "main.zig" },
66 });
test/standalone/issue_8550/build.zig+1-1
......@@ -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,
test/standalone/issue_9812/build.zig+1-1
......@@ -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 optimize = b.standardOptimizeOption(.{});
55 const zip_add = b.addTest(.{
66 .root_source_file = .{ .path = "main.zig" },
test/standalone/load_dynamic_library/build.zig+2-2
......@@ -1,6 +1,6 @@
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 = b.standardTargetOptions(.{});
55 const optimize = b.standardOptimizeOption(.{});
66
test/standalone/main_pkg_path/build.zig+2-2
......@@ -1,6 +1,6 @@
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 test_exe = b.addTest(.{
55 .root_source_file = .{ .path = "a/test.zig" },
66 });
test/standalone/mix_c_files/build.zig+2-3
......@@ -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,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
13pub fn build(b: *std.Build) void {
1514 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
test/standalone/mix_o_files/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55
66 const obj = b.addObject(.{
test/standalone/options/build.zig+1-1
......@@ -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 = b.standardTargetOptions(.{});
55 const optimize = b.standardOptimizeOption(.{});
66
test/standalone/pie/build.zig+2-2
......@@ -1,6 +1,6 @@
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 main = b.addTest(.{
55 .root_source_file = .{ .path = "main.zig" },
66 .optimize = b.standardOptimizeOption(.{}),
test/standalone/pkg_import/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55
66 const exe = b.addExecutable(.{
test/standalone/shared_library/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55 const target = b.standardTargetOptions(.{});
66 const lib = b.addSharedLibrary(.{
test/standalone/static_c_lib/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55
66 const foo = b.addStaticLibrary(.{
test/standalone/test_runner_path/build.zig+2-2
......@@ -1,6 +1,6 @@
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 test_exe = b.addTest(.{
55 .root_source_file = .{ .path = "test.zig" },
66 .kind = .test_exe,
test/standalone/use_alias/build.zig+2-2
......@@ -1,6 +1,6 @@
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 main = b.addTest(.{
55 .root_source_file = .{ .path = "main.zig" },
66 .optimize = b.standardOptimizeOption(.{}),
test/standalone/windows_spawn/build.zig+2-2
......@@ -1,6 +1,6 @@
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 optimize = b.standardOptimizeOption(.{});
55
66 const hello = b.addExecutable(.{
test/tests.zig+30-30
......@@ -1,7 +1,6 @@
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;
......@@ -9,9 +8,10 @@ const mem = std.mem;
98const fmt = std.fmt;
109const ArrayList = std.ArrayList;
1110const OptimizeMode = std.builtin.OptimizeMode;
12const LibExeObjStep = build.LibExeObjStep;
11const LibExeObjStep = std.Build.LibExeObjStep;
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");
......@@ -462,7 +462,7 @@ 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, optimize_modes: []const OptimizeMode) *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,
......@@ -477,7 +477,7 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, optimi
477477 return cases.step;
478478}
479479
480pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *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,
......@@ -493,7 +493,7 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, optimize_
493493}
494494
495495pub fn addStandaloneTests(
496 b: *build.Builder,
496 b: *std.Build,
497497 test_filter: ?[]const u8,
498498 optimize_modes: []const OptimizeMode,
499499 skip_non_native: bool,
......@@ -506,7 +506,7 @@ 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,
......@@ -532,13 +532,13 @@ pub fn addStandaloneTests(
532532}
533533
534534pub fn addLinkTests(
535 b: *build.Builder,
535 b: *std.Build,
536536 test_filter: ?[]const u8,
537537 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,
......@@ -556,7 +556,7 @@ pub fn addLinkTests(
556556 return cases.step;
557557}
558558
559pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *build.Step {
559pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
560560 _ = test_filter;
561561 _ = optimize_modes;
562562 const step = b.step("test-cli", "Test the command line interface");
......@@ -577,7 +577,7 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes:
577577 return step;
578578}
579579
580pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *build.Step {
580pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
581581 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
582582 cases.* = CompareOutputContext{
583583 .b = b,
......@@ -592,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, opti
592592 return cases.step;
593593}
594594
595pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
595pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
596596 const cases = b.allocator.create(TranslateCContext) catch unreachable;
597597 cases.* = TranslateCContext{
598598 .b = b,
......@@ -607,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
607607}
608608
609609pub fn addRunTranslatedCTests(
610 b: *build.Builder,
610 b: *std.Build,
611611 test_filter: ?[]const u8,
612612 target: std.zig.CrossTarget,
613) *build.Step {
613) *Step {
614614 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
615615 cases.* = .{
616616 .b = b,
......@@ -625,7 +625,7 @@ pub fn addRunTranslatedCTests(
625625 return cases.step;
626626}
627627
628pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
628pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {
629629 const cases = b.allocator.create(GenHContext) catch unreachable;
630630 cases.* = GenHContext{
631631 .b = b,
......@@ -640,7 +640,7 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
640640}
641641
642642pub fn addPkgTests(
643 b: *build.Builder,
643 b: *std.Build,
644644 test_filter: ?[]const u8,
645645 root_src: []const u8,
646646 name: []const u8,
......@@ -651,7 +651,7 @@ pub fn addPkgTests(
651651 skip_libc: bool,
652652 skip_stage1: bool,
653653 skip_stage2: bool,
654) *build.Step {
654) *Step {
655655 const step = b.step(b.fmt("test-{s}", .{name}), desc);
656656
657657 for (test_targets) |test_target| {
......@@ -742,8 +742,8 @@ pub fn addPkgTests(
742742}
743743
744744pub const StackTracesContext = struct {
745 b: *build.Builder,
746 step: *build.Step,
745 b: *std.Build,
746 step: *Step,
747747 test_index: usize,
748748 test_filter: ?[]const u8,
749749 optimize_modes: []const OptimizeMode,
......@@ -840,7 +840,7 @@ pub const StackTracesContext = struct {
840840 const RunAndCompareStep = struct {
841841 pub const base_id = .custom;
842842
843 step: build.Step,
843 step: Step,
844844 context: *StackTracesContext,
845845 exe: *LibExeObjStep,
846846 name: []const u8,
......@@ -858,7 +858,7 @@ pub const StackTracesContext = struct {
858858 const allocator = context.b.allocator;
859859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
860860 ptr.* = RunAndCompareStep{
861 .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
861 .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
862862 .context = context,
863863 .exe = exe,
864864 .name = name,
......@@ -871,7 +871,7 @@ pub const StackTracesContext = struct {
871871 return ptr;
872872 }
873873
874 fn make(step: *build.Step) !void {
874 fn make(step: *Step) !void {
875875 const self = @fieldParentPtr(RunAndCompareStep, "step", step);
876876 const b = self.context.b;
877877
......@@ -1014,8 +1014,8 @@ pub const StackTracesContext = struct {
10141014};
10151015
10161016pub const StandaloneContext = struct {
1017 b: *build.Builder,
1018 step: *build.Step,
1017 b: *std.Build,
1018 step: *Step,
10191019 test_index: usize,
10201020 test_filter: ?[]const u8,
10211021 optimize_modes: []const OptimizeMode,
......@@ -1150,8 +1150,8 @@ pub const StandaloneContext = struct {
11501150};
11511151
11521152pub const GenHContext = struct {
1153 b: *build.Builder,
1154 step: *build.Step,
1153 b: *std.Build,
1154 step: *Step,
11551155 test_index: usize,
11561156 test_filter: ?[]const u8,
11571157
......@@ -1178,7 +1178,7 @@ pub const GenHContext = struct {
11781178 };
11791179
11801180 const GenHCmpOutputStep = struct {
1181 step: build.Step,
1181 step: Step,
11821182 context: *GenHContext,
11831183 obj: *LibExeObjStep,
11841184 name: []const u8,
......@@ -1194,7 +1194,7 @@ pub const GenHContext = struct {
11941194 const allocator = context.b.allocator;
11951195 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
11961196 ptr.* = GenHCmpOutputStep{
1197 .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make),
1197 .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make),
11981198 .context = context,
11991199 .obj = obj,
12001200 .name = name,
......@@ -1206,7 +1206,7 @@ pub const GenHContext = struct {
12061206 return ptr;
12071207 }
12081208
1209 fn make(step: *build.Step) !void {
1209 fn make(step: *Step) !void {
12101210 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
12111211 const b = self.context.b;
12121212
......@@ -1348,7 +1348,7 @@ const c_abi_targets = [_]CrossTarget{
13481348 },
13491349};
13501350
1351pub 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 {
13521352 const step = b.step("test-c-abi", "Run the C ABI tests");
13531353
13541354 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };