From 73cf7b64291ed8b5dcb4cb52df103be08f15a347 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Jan 2023 21:39:43 -0700
Subject: update build.zig API usage
---
build.zig | 72 +++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 42 insertions(+), 30 deletions(-)
(limited to 'build.zig')
diff --git a/build.zig b/build.zig
index 3a7468243f..98da9f31ee 100644
--- a/build.zig
+++ b/build.zig
@@ -23,7 +23,7 @@ pub fn build(b: *Builder) !void {
}
break :t b.standardTargetOptions(.{ .default_target = default_target });
};
- const mode: std.builtin.Mode = if (release) switch (target.getCpuArch()) {
+ const optimize: std.builtin.OptimizeMode = if (release) switch (target.getCpuArch()) {
.wasm32 => .ReleaseSmall,
else => .ReleaseFast,
} else .Debug;
@@ -33,7 +33,12 @@ pub fn build(b: *Builder) !void {
const test_step = b.step("test", "Run all the tests");
- const docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
+ const docgen_exe = b.addExecutable(.{
+ .name = "docgen",
+ .root_source_file = .{ .path = "doc/docgen.zig" },
+ .target = .{},
+ .optimize = .Debug,
+ });
docgen_exe.single_threaded = single_threaded;
const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);
@@ -53,10 +58,12 @@ pub fn build(b: *Builder) !void {
const docs_step = b.step("docs", "Build documentation");
docs_step.dependOn(&docgen_cmd.step);
- const test_cases = b.addTest("src/test.zig");
+ const test_cases = b.addTest(.{
+ .root_source_file = .{ .path = "src/test.zig" },
+ .optimize = optimize,
+ });
test_cases.main_pkg_path = ".";
test_cases.stack_size = stack_size;
- test_cases.setBuildMode(mode);
test_cases.single_threaded = single_threaded;
const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
@@ -149,17 +156,15 @@ pub fn build(b: *Builder) !void {
const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {
if (strip == true) break :blk @as(u32, 0);
- if (mode != .Debug) break :blk 0;
+ if (optimize != .Debug) break :blk 0;
break :blk 4;
};
- const exe = addCompilerStep(b);
+ const exe = addCompilerStep(b, optimize, target);
exe.strip = strip;
exe.sanitize_thread = sanitize_thread;
exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
exe.install();
- exe.setBuildMode(mode);
- exe.setTarget(target);
const compile_step = b.step("compile", "Build the self-hosted compiler");
compile_step.dependOn(&exe.step);
@@ -195,7 +200,7 @@ pub fn build(b: *Builder) !void {
test_cases.linkLibC();
}
- const is_debug = mode == .Debug;
+ const is_debug = optimize == .Debug;
const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
@@ -360,25 +365,25 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(test_cases_step);
}
- var chosen_modes: [4]builtin.Mode = undefined;
+ var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
var chosen_mode_index: usize = 0;
if (!skip_debug) {
- chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
+ chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug;
chosen_mode_index += 1;
}
if (!skip_release_safe) {
- chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
+ chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe;
chosen_mode_index += 1;
}
if (!skip_release_fast) {
- chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast;
+ chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast;
chosen_mode_index += 1;
}
if (!skip_release_small) {
- chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall;
+ chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall;
chosen_mode_index += 1;
}
- const modes = chosen_modes[0..chosen_mode_index];
+ const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
// run stage1 `zig fmt` on this build.zig file just to make sure it works
test_step.dependOn(&fmt_build_zig.step);
@@ -391,7 +396,7 @@ pub fn build(b: *Builder) !void {
"test/behavior.zig",
"behavior",
"Run the behavior tests",
- modes,
+ optimization_modes,
skip_single_threaded,
skip_non_native,
skip_libc,
@@ -405,7 +410,7 @@ pub fn build(b: *Builder) !void {
"lib/compiler_rt.zig",
"compiler-rt",
"Run the compiler_rt tests",
- modes,
+ optimization_modes,
true, // skip_single_threaded
skip_non_native,
true, // skip_libc
@@ -419,7 +424,7 @@ pub fn build(b: *Builder) !void {
"lib/c.zig",
"universal-libc",
"Run the universal libc tests",
- modes,
+ optimization_modes,
true, // skip_single_threaded
skip_non_native,
true, // skip_libc
@@ -427,11 +432,11 @@ pub fn build(b: *Builder) !void {
skip_stage2_tests or true, // TODO get these all passing
));
- test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
+ test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
test_step.dependOn(tests.addStandaloneTests(
b,
test_filter,
- modes,
+ optimization_modes,
skip_non_native,
enable_macos_sdk,
target,
@@ -444,10 +449,10 @@ pub fn build(b: *Builder) !void {
enable_symlinks_windows,
));
test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
- test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
- test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
- test_step.dependOn(tests.addCliTests(b, test_filter, modes));
- test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
+ test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
+ test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
+ test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
+ test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
if (!skip_run_translated_c) {
test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
@@ -461,7 +466,7 @@ pub fn build(b: *Builder) !void {
"lib/std/std.zig",
"std",
"Run the standard library tests",
- modes,
+ optimization_modes,
skip_single_threaded,
skip_non_native,
skip_libc,
@@ -481,9 +486,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
};
target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));
- const exe = addCompilerStep(b);
- exe.setBuildMode(.ReleaseSmall);
- exe.setTarget(target);
+ const exe = addCompilerStep(b, .ReleaseSmall, target);
const exe_options = b.addOptions();
exe.addOptions("build_options", exe_options);
@@ -510,8 +513,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
update_zig1_step.dependOn(&run_opt.step);
}
-fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep {
- const exe = b.addExecutable("zig", "src/main.zig");
+fn addCompilerStep(
+ b: *Builder,
+ optimize: std.builtin.OptimizeMode,
+ target: std.zig.CrossTarget,
+) *std.build.LibExeObjStep {
+ const exe = b.addExecutable(.{
+ .name = "zig",
+ .root_source_file = .{ .path = "src/main.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
exe.stack_size = stack_size;
return exe;
}
--
cgit v1.2.3
From 36e2d992dd8c45ca89a51d508c6c413cff5ad2cd Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 31 Jan 2023 00:19:51 -0700
Subject: combine std.build and std.build.Builder into std.Build
I've been wanting to do this for along time.
---
build.zig | 31 +-
doc/langref.html.in | 20 +-
lib/build_runner.zig | 9 +-
lib/init-exe/build.zig | 2 +-
lib/init-lib/build.zig | 2 +-
lib/std/Build.zig | 1771 +++++++++++++++++++
lib/std/Build/CheckFileStep.zig | 51 +
lib/std/Build/CheckObjectStep.zig | 1024 +++++++++++
lib/std/Build/ConfigHeaderStep.zig | 287 +++
lib/std/Build/EmulatableRunStep.zig | 213 +++
lib/std/Build/FmtStep.zig | 32 +
lib/std/Build/InstallArtifactStep.zig | 85 +
lib/std/Build/InstallDirStep.zig | 93 +
lib/std/Build/InstallFileStep.zig | 40 +
lib/std/Build/InstallRawStep.zig | 110 ++
lib/std/Build/LibExeObjStep.zig | 2045 +++++++++++++++++++++
lib/std/Build/LogStep.zig | 23 +
lib/std/Build/OptionsStep.zig | 363 ++++
lib/std/Build/RemoveDirStep.zig | 29 +
lib/std/Build/RunStep.zig | 376 ++++
lib/std/Build/Step.zig | 97 +
lib/std/Build/TranslateCStep.zig | 136 ++
lib/std/Build/WriteFileStep.zig | 115 ++
lib/std/build.zig | 1863 --------------------
lib/std/build/CheckFileStep.zig | 53 -
lib/std/build/CheckObjectStep.zig | 1026 -----------
lib/std/build/ConfigHeaderStep.zig | 288 ---
lib/std/build/EmulatableRunStep.zig | 215 ---
lib/std/build/FmtStep.zig | 37 -
lib/std/build/InstallArtifactStep.zig | 88 -
lib/std/build/InstallDirStep.zig | 95 -
lib/std/build/InstallFileStep.zig | 42 -
lib/std/build/InstallRawStep.zig | 106 --
lib/std/build/LibExeObjStep.zig | 2047 ----------------------
lib/std/build/LogStep.zig | 25 -
lib/std/build/OptionsStep.zig | 365 ----
lib/std/build/RemoveDirStep.zig | 31 -
lib/std/build/RunStep.zig | 378 ----
lib/std/build/TranslateCStep.zig | 138 --
lib/std/build/WriteFileStep.zig | 117 --
lib/std/std.zig | 8 +-
test/link/bss/build.zig | 4 +-
test/link/common_symbols/build.zig | 4 +-
test/link/common_symbols_alignment/build.zig | 4 +-
test/link/interdependent_static_c_libs/build.zig | 4 +-
test/link/macho/bugs/13056/build.zig | 3 +-
test/link/macho/bugs/13457/build.zig | 5 +-
test/link/macho/dead_strip/build.zig | 7 +-
test/link/macho/dead_strip_dylibs/build.zig | 7 +-
test/link/macho/dylib/build.zig | 3 +-
test/link/macho/empty/build.zig | 5 +-
test/link/macho/entry/build.zig | 3 +-
test/link/macho/headerpad/build.zig | 7 +-
test/link/macho/linksection/build.zig | 2 +-
test/link/macho/needed_framework/build.zig | 5 +-
test/link/macho/needed_library/build.zig | 5 +-
test/link/macho/objc/build.zig | 5 +-
test/link/macho/objcpp/build.zig | 3 +-
test/link/macho/pagezero/build.zig | 3 +-
test/link/macho/search_strategy/build.zig | 13 +-
test/link/macho/stack_size/build.zig | 3 +-
test/link/macho/strict_validation/build.zig | 5 +-
test/link/macho/tls/build.zig | 3 +-
test/link/macho/unwind_info/build.zig | 11 +-
test/link/macho/uuid/build.zig | 11 +-
test/link/macho/weak_framework/build.zig | 5 +-
test/link/macho/weak_library/build.zig | 5 +-
test/link/static_lib_as_system_lib/build.zig | 3 +-
test/link/wasm/archive/build.zig | 3 +-
test/link/wasm/basic-features/build.zig | 2 +-
test/link/wasm/bss/build.zig | 3 +-
test/link/wasm/export-data/build.zig | 3 +-
test/link/wasm/export/build.zig | 2 +-
test/link/wasm/extern-mangle/build.zig | 3 +-
test/link/wasm/extern/build.zig | 2 +-
test/link/wasm/function-table/build.zig | 3 +-
test/link/wasm/infer-features/build.zig | 2 +-
test/link/wasm/producers/build.zig | 3 +-
test/link/wasm/segments/build.zig | 3 +-
test/link/wasm/stack_pointer/build.zig | 3 +-
test/link/wasm/type/build.zig | 3 +-
test/src/compare_output.zig | 5 +-
test/src/run_translated_c.zig | 5 +-
test/src/translate_c.zig | 5 +-
test/standalone/brace_expansion/build.zig | 4 +-
test/standalone/c_compiler/build.zig | 5 +-
test/standalone/emit_asm_and_bin/build.zig | 4 +-
test/standalone/empty_env/build.zig | 4 +-
test/standalone/global_linkage/build.zig | 4 +-
test/standalone/install_raw_hex/build.zig | 4 +-
test/standalone/issue_11595/build.zig | 5 +-
test/standalone/issue_12588/build.zig | 3 +-
test/standalone/issue_12706/build.zig | 5 +-
test/standalone/issue_13030/build.zig | 3 +-
test/standalone/issue_339/build.zig | 4 +-
test/standalone/issue_5825/build.zig | 4 +-
test/standalone/issue_7030/build.zig | 4 +-
test/standalone/issue_794/build.zig | 4 +-
test/standalone/issue_8550/build.zig | 2 +-
test/standalone/issue_9812/build.zig | 2 +-
test/standalone/load_dynamic_library/build.zig | 4 +-
test/standalone/main_pkg_path/build.zig | 4 +-
test/standalone/mix_c_files/build.zig | 5 +-
test/standalone/mix_o_files/build.zig | 4 +-
test/standalone/options/build.zig | 2 +-
test/standalone/pie/build.zig | 4 +-
test/standalone/pkg_import/build.zig | 4 +-
test/standalone/shared_library/build.zig | 4 +-
test/standalone/static_c_lib/build.zig | 4 +-
test/standalone/test_runner_path/build.zig | 4 +-
test/standalone/use_alias/build.zig | 4 +-
test/standalone/windows_spawn/build.zig | 4 +-
test/tests.zig | 60 +-
113 files changed, 7086 insertions(+), 7146 deletions(-)
create mode 100644 lib/std/Build.zig
create mode 100644 lib/std/Build/CheckFileStep.zig
create mode 100644 lib/std/Build/CheckObjectStep.zig
create mode 100644 lib/std/Build/ConfigHeaderStep.zig
create mode 100644 lib/std/Build/EmulatableRunStep.zig
create mode 100644 lib/std/Build/FmtStep.zig
create mode 100644 lib/std/Build/InstallArtifactStep.zig
create mode 100644 lib/std/Build/InstallDirStep.zig
create mode 100644 lib/std/Build/InstallFileStep.zig
create mode 100644 lib/std/Build/InstallRawStep.zig
create mode 100644 lib/std/Build/LibExeObjStep.zig
create mode 100644 lib/std/Build/LogStep.zig
create mode 100644 lib/std/Build/OptionsStep.zig
create mode 100644 lib/std/Build/RemoveDirStep.zig
create mode 100644 lib/std/Build/RunStep.zig
create mode 100644 lib/std/Build/Step.zig
create mode 100644 lib/std/Build/TranslateCStep.zig
create mode 100644 lib/std/Build/WriteFileStep.zig
delete mode 100644 lib/std/build.zig
delete mode 100644 lib/std/build/CheckFileStep.zig
delete mode 100644 lib/std/build/CheckObjectStep.zig
delete mode 100644 lib/std/build/ConfigHeaderStep.zig
delete mode 100644 lib/std/build/EmulatableRunStep.zig
delete mode 100644 lib/std/build/FmtStep.zig
delete mode 100644 lib/std/build/InstallArtifactStep.zig
delete mode 100644 lib/std/build/InstallDirStep.zig
delete mode 100644 lib/std/build/InstallFileStep.zig
delete mode 100644 lib/std/build/InstallRawStep.zig
delete mode 100644 lib/std/build/LibExeObjStep.zig
delete mode 100644 lib/std/build/LogStep.zig
delete mode 100644 lib/std/build/OptionsStep.zig
delete mode 100644 lib/std/build/RemoveDirStep.zig
delete mode 100644 lib/std/build/RunStep.zig
delete mode 100644 lib/std/build/TranslateCStep.zig
delete mode 100644 lib/std/build/WriteFileStep.zig
(limited to 'build.zig')
diff --git a/build.zig b/build.zig
index 98da9f31ee..6b6c384fc4 100644
--- a/build.zig
+++ b/build.zig
@@ -1,19 +1,18 @@
const std = @import("std");
const builtin = std.builtin;
-const Builder = std.build.Builder;
const tests = @import("test/tests.zig");
const BufMap = std.BufMap;
const mem = std.mem;
const ArrayList = std.ArrayList;
const io = std.io;
const fs = std.fs;
-const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
+const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
const assert = std.debug.assert;
const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 };
const stack_size = 32 * 1024 * 1024;
-pub fn build(b: *Builder) !void {
+pub fn build(b: *std.Build) !void {
const release = b.option(bool, "release", "Build in release mode") orelse false;
const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
const target = t: {
@@ -477,7 +476,7 @@ pub fn build(b: *Builder) !void {
try addWasiUpdateStep(b, version);
}
-fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
+fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
const semver = try std.SemanticVersion.parse(version);
var target: std.zig.CrossTarget = .{
@@ -514,10 +513,10 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
}
fn addCompilerStep(
- b: *Builder,
+ b: *std.Build,
optimize: std.builtin.OptimizeMode,
target: std.zig.CrossTarget,
-) *std.build.LibExeObjStep {
+) *std.Build.LibExeObjStep {
const exe = b.addExecutable(.{
.name = "zig",
.root_source_file = .{ .path = "src/main.zig" },
@@ -543,9 +542,9 @@ const exe_cflags = [_][]const u8{
};
fn addCmakeCfgOptionsToExe(
- b: *Builder,
+ b: *std.Build,
cfg: CMakeConfig,
- exe: *std.build.LibExeObjStep,
+ exe: *std.Build.LibExeObjStep,
use_zig_libcxx: bool,
) !void {
if (exe.target.isDarwin()) {
@@ -624,7 +623,7 @@ fn addCmakeCfgOptionsToExe(
}
}
-fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
+fn addStaticLlvmOptionsToExe(exe: *std.Build.LibExeObjStep) !void {
// Adds the Zig C++ sources which both stage1 and stage2 need.
//
// We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
@@ -661,9 +660,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
}
fn addCxxKnownPath(
- b: *Builder,
+ b: *std.Build,
ctx: CMakeConfig,
- exe: *std.build.LibExeObjStep,
+ exe: *std.Build.LibExeObjStep,
objname: []const u8,
errtxt: ?[]const u8,
need_cpp_includes: bool,
@@ -696,7 +695,7 @@ fn addCxxKnownPath(
}
}
-fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
+fn addCMakeLibraryList(exe: *std.Build.LibExeObjStep, list: []const u8) void {
var it = mem.tokenize(u8, list, ";");
while (it.next()) |lib| {
if (mem.startsWith(u8, lib, "-l")) {
@@ -710,7 +709,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
}
const CMakeConfig = struct {
- llvm_linkage: std.build.LibExeObjStep.Linkage,
+ llvm_linkage: std.Build.LibExeObjStep.Linkage,
cmake_binary_dir: []const u8,
cmake_prefix_path: []const u8,
cmake_static_library_prefix: []const u8,
@@ -727,7 +726,7 @@ const CMakeConfig = struct {
const max_config_h_bytes = 1 * 1024 * 1024;
-fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
+fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
if (config_h_path_option) |path| {
var config_h_or_err = fs.cwd().openFile(path, .{});
if (config_h_or_err) |*file| {
@@ -773,7 +772,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
} else unreachable; // TODO should not need `else unreachable`.
}
-fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
+fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
var ctx: CMakeConfig = .{
.llvm_linkage = undefined,
.cmake_binary_dir = undefined,
@@ -862,7 +861,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
return ctx;
}
-fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
+fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
const duplicated = b.allocator.dupe(u8, s) catch unreachable;
for (duplicated) |*byte| switch (byte.*) {
'/' => byte.* = fs.path.sep,
diff --git a/doc/langref.html.in b/doc/langref.html.in
index c008149f41..1163ad0200 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -9528,9 +9528,9 @@ fn foo(comptime T: type, ptr: *T) T {
To add standard build options to a build.zig file:
{#code_begin|syntax|build#}
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "example",
@@ -10551,9 +10551,9 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
This build.zig file is automatically generated
by zig init-exe.
{#code_begin|syntax|build_executable#}
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
@@ -10588,9 +10588,9 @@ pub fn build(b: *Builder) void {
This build.zig file is automatically generated
by zig init-lib.
{#code_begin|syntax|build_library#}
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{
.name = "example",
@@ -10961,9 +10961,9 @@ int main(int argc, char **argv) {
}
{#end_syntax_block#}
{#code_begin|syntax|build_c#}
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addExecutable(.{
@@ -11025,9 +11025,9 @@ int main(int argc, char **argv) {
}
{#end_syntax_block#}
{#code_begin|syntax|build_object#}
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addExecutable(.{
diff --git a/lib/build_runner.zig b/lib/build_runner.zig
index 4df2eb1d62..189b118787 100644
--- a/lib/build_runner.zig
+++ b/lib/build_runner.zig
@@ -3,7 +3,6 @@ const std = @import("std");
const builtin = @import("builtin");
const io = std.io;
const fmt = std.fmt;
-const Builder = std.build.Builder;
const mem = std.mem;
const process = std.process;
const ArrayList = std.ArrayList;
@@ -42,7 +41,7 @@ pub fn main() !void {
return error.InvalidArgs;
};
- const builder = try Builder.create(
+ const builder = try std.Build.create(
allocator,
zig_exe,
build_root,
@@ -58,7 +57,7 @@ pub fn main() !void {
const stdout_stream = io.getStdOut().writer();
var install_prefix: ?[]const u8 = null;
- var dir_list = Builder.DirList{};
+ var dir_list = std.Build.DirList{};
// before arg parsing, check for the NO_COLOR environment variable
// if it exists, default the color setting to .off
@@ -230,7 +229,7 @@ pub fn main() !void {
};
}
-fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
+fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
// run the build script to collect the options
if (!already_ran_build) {
builder.resolveInstallPrefix(null, .{});
@@ -330,7 +329,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
);
}
-fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void {
+fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void {
usage(builder, already_ran_build, out_stream) catch {};
process.exit(1);
}
diff --git a/lib/init-exe/build.zig b/lib/init-exe/build.zig
index 36e5feddec..b88515b403 100644
--- a/lib/init-exe/build.zig
+++ b/lib/init-exe/build.zig
@@ -3,7 +3,7 @@ const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
diff --git a/lib/init-lib/build.zig b/lib/init-lib/build.zig
index 4a7b700dc2..5ebb55373f 100644
--- a/lib/init-lib/build.zig
+++ b/lib/init-lib/build.zig
@@ -3,7 +3,7 @@ const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
new file mode 100644
index 0000000000..a3c579c743
--- /dev/null
+++ b/lib/std/Build.zig
@@ -0,0 +1,1771 @@
+const std = @import("std.zig");
+const builtin = @import("builtin");
+const io = std.io;
+const fs = std.fs;
+const mem = std.mem;
+const debug = std.debug;
+const panic = std.debug.panic;
+const assert = debug.assert;
+const log = std.log;
+const ArrayList = std.ArrayList;
+const StringHashMap = std.StringHashMap;
+const Allocator = mem.Allocator;
+const process = std.process;
+const EnvMap = std.process.EnvMap;
+const fmt_lib = std.fmt;
+const File = std.fs.File;
+const CrossTarget = std.zig.CrossTarget;
+const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+const Sha256 = std.crypto.hash.sha2.Sha256;
+const Build = @This();
+
+pub const Step = @import("Build/Step.zig");
+pub const CheckFileStep = @import("Build/CheckFileStep.zig");
+pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
+pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
+pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
+pub const FmtStep = @import("Build/FmtStep.zig");
+pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
+pub const InstallDirStep = @import("Build/InstallDirStep.zig");
+pub const InstallFileStep = @import("Build/InstallFileStep.zig");
+pub const InstallRawStep = @import("Build/InstallRawStep.zig");
+pub const LibExeObjStep = @import("Build/LibExeObjStep.zig");
+pub const LogStep = @import("Build/LogStep.zig");
+pub const OptionsStep = @import("Build/OptionsStep.zig");
+pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
+pub const RunStep = @import("Build/RunStep.zig");
+pub const TranslateCStep = @import("Build/TranslateCStep.zig");
+pub const WriteFileStep = @import("Build/WriteFileStep.zig");
+
+install_tls: TopLevelStep,
+uninstall_tls: TopLevelStep,
+allocator: Allocator,
+user_input_options: UserInputOptionsMap,
+available_options_map: AvailableOptionsMap,
+available_options_list: ArrayList(AvailableOption),
+verbose: bool,
+verbose_link: bool,
+verbose_cc: bool,
+verbose_air: bool,
+verbose_llvm_ir: bool,
+verbose_cimport: bool,
+verbose_llvm_cpu_features: bool,
+/// The purpose of executing the command is for a human to read compile errors from the terminal
+prominent_compile_errors: bool,
+color: enum { auto, on, off } = .auto,
+reference_trace: ?u32 = null,
+invalid_user_input: bool,
+zig_exe: []const u8,
+default_step: *Step,
+env_map: *EnvMap,
+top_level_steps: ArrayList(*TopLevelStep),
+install_prefix: []const u8,
+dest_dir: ?[]const u8,
+lib_dir: []const u8,
+exe_dir: []const u8,
+h_dir: []const u8,
+install_path: []const u8,
+sysroot: ?[]const u8 = null,
+search_prefixes: ArrayList([]const u8),
+libc_file: ?[]const u8 = null,
+installed_files: ArrayList(InstalledFile),
+/// Path to the directory containing build.zig.
+build_root: []const u8,
+cache_root: []const u8,
+global_cache_root: []const u8,
+/// zig lib dir
+override_lib_dir: ?[]const u8,
+vcpkg_root: VcpkgRoot = .unattempted,
+pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
+args: ?[][]const u8 = null,
+debug_log_scopes: []const []const u8 = &.{},
+debug_compile_errors: bool = false,
+
+/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
+enable_darling: bool = false,
+/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
+enable_qemu: bool = false,
+/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
+enable_rosetta: bool = false,
+/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
+enable_wasmtime: bool = false,
+/// Use system Wine installation to run cross compiled Windows build artifacts.
+enable_wine: bool = false,
+/// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
+/// this will be the directory $glibc-build-dir/install/glibcs
+/// Given the example of the aarch64 target, this is the directory
+/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
+glibc_runtimes_dir: ?[]const u8 = null,
+
+/// Information about the native target. Computed before build() is invoked.
+host: NativeTargetInfo,
+
+dep_prefix: []const u8 = "",
+
+pub const ExecError = error{
+ ReadFailure,
+ ExitCodeFailure,
+ ProcessTerminated,
+ ExecNotSupported,
+} || std.ChildProcess.SpawnError;
+
+pub const PkgConfigError = error{
+ PkgConfigCrashed,
+ PkgConfigFailed,
+ PkgConfigNotInstalled,
+ PkgConfigInvalidOutput,
+};
+
+pub const PkgConfigPkg = struct {
+ name: []const u8,
+ desc: []const u8,
+};
+
+pub const CStd = enum {
+ C89,
+ C99,
+ C11,
+};
+
+const UserInputOptionsMap = StringHashMap(UserInputOption);
+const AvailableOptionsMap = StringHashMap(AvailableOption);
+
+const AvailableOption = struct {
+ name: []const u8,
+ type_id: TypeId,
+ description: []const u8,
+ /// If the `type_id` is `enum` this provides the list of enum options
+ enum_options: ?[]const []const u8,
+};
+
+const UserInputOption = struct {
+ name: []const u8,
+ value: UserValue,
+ used: bool,
+};
+
+const UserValue = union(enum) {
+ flag: void,
+ scalar: []const u8,
+ list: ArrayList([]const u8),
+ map: StringHashMap(*const UserValue),
+};
+
+const TypeId = enum {
+ bool,
+ int,
+ float,
+ @"enum",
+ string,
+ list,
+};
+
+const TopLevelStep = struct {
+ pub const base_id = .top_level;
+
+ step: Step,
+ description: []const u8,
+};
+
+pub const DirList = struct {
+ lib_dir: ?[]const u8 = null,
+ exe_dir: ?[]const u8 = null,
+ include_dir: ?[]const u8 = null,
+};
+
+pub fn create(
+ allocator: Allocator,
+ zig_exe: []const u8,
+ build_root: []const u8,
+ cache_root: []const u8,
+ global_cache_root: []const u8,
+) !*Build {
+ const env_map = try allocator.create(EnvMap);
+ env_map.* = try process.getEnvMap(allocator);
+
+ const host = try NativeTargetInfo.detect(.{});
+
+ const self = try allocator.create(Build);
+ self.* = Build{
+ .zig_exe = zig_exe,
+ .build_root = build_root,
+ .cache_root = try fs.path.relative(allocator, build_root, cache_root),
+ .global_cache_root = global_cache_root,
+ .verbose = false,
+ .verbose_link = false,
+ .verbose_cc = false,
+ .verbose_air = false,
+ .verbose_llvm_ir = false,
+ .verbose_cimport = false,
+ .verbose_llvm_cpu_features = false,
+ .prominent_compile_errors = false,
+ .invalid_user_input = false,
+ .allocator = allocator,
+ .user_input_options = UserInputOptionsMap.init(allocator),
+ .available_options_map = AvailableOptionsMap.init(allocator),
+ .available_options_list = ArrayList(AvailableOption).init(allocator),
+ .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
+ .default_step = undefined,
+ .env_map = env_map,
+ .search_prefixes = ArrayList([]const u8).init(allocator),
+ .install_prefix = undefined,
+ .lib_dir = undefined,
+ .exe_dir = undefined,
+ .h_dir = undefined,
+ .dest_dir = env_map.get("DESTDIR"),
+ .installed_files = ArrayList(InstalledFile).init(allocator),
+ .install_tls = TopLevelStep{
+ .step = Step.initNoOp(.top_level, "install", allocator),
+ .description = "Copy build artifacts to prefix path",
+ },
+ .uninstall_tls = TopLevelStep{
+ .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
+ .description = "Remove build artifacts from prefix path",
+ },
+ .override_lib_dir = null,
+ .install_path = undefined,
+ .args = null,
+ .host = host,
+ };
+ try self.top_level_steps.append(&self.install_tls);
+ try self.top_level_steps.append(&self.uninstall_tls);
+ self.default_step = &self.install_tls.step;
+ return self;
+}
+
+fn createChild(
+ parent: *Build,
+ dep_name: []const u8,
+ build_root: []const u8,
+ args: anytype,
+) !*Build {
+ const child = try createChildOnly(parent, dep_name, build_root);
+ try applyArgs(child, args);
+ return child;
+}
+
+fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
+ const allocator = parent.allocator;
+ const child = try allocator.create(Build);
+ child.* = .{
+ .allocator = allocator,
+ .install_tls = .{
+ .step = Step.initNoOp(.top_level, "install", allocator),
+ .description = "Copy build artifacts to prefix path",
+ },
+ .uninstall_tls = .{
+ .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
+ .description = "Remove build artifacts from prefix path",
+ },
+ .user_input_options = UserInputOptionsMap.init(allocator),
+ .available_options_map = AvailableOptionsMap.init(allocator),
+ .available_options_list = ArrayList(AvailableOption).init(allocator),
+ .verbose = parent.verbose,
+ .verbose_link = parent.verbose_link,
+ .verbose_cc = parent.verbose_cc,
+ .verbose_air = parent.verbose_air,
+ .verbose_llvm_ir = parent.verbose_llvm_ir,
+ .verbose_cimport = parent.verbose_cimport,
+ .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
+ .prominent_compile_errors = parent.prominent_compile_errors,
+ .color = parent.color,
+ .reference_trace = parent.reference_trace,
+ .invalid_user_input = false,
+ .zig_exe = parent.zig_exe,
+ .default_step = undefined,
+ .env_map = parent.env_map,
+ .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
+ .install_prefix = undefined,
+ .dest_dir = parent.dest_dir,
+ .lib_dir = parent.lib_dir,
+ .exe_dir = parent.exe_dir,
+ .h_dir = parent.h_dir,
+ .install_path = parent.install_path,
+ .sysroot = parent.sysroot,
+ .search_prefixes = ArrayList([]const u8).init(allocator),
+ .libc_file = parent.libc_file,
+ .installed_files = ArrayList(InstalledFile).init(allocator),
+ .build_root = build_root,
+ .cache_root = parent.cache_root,
+ .global_cache_root = parent.global_cache_root,
+ .override_lib_dir = parent.override_lib_dir,
+ .debug_log_scopes = parent.debug_log_scopes,
+ .debug_compile_errors = parent.debug_compile_errors,
+ .enable_darling = parent.enable_darling,
+ .enable_qemu = parent.enable_qemu,
+ .enable_rosetta = parent.enable_rosetta,
+ .enable_wasmtime = parent.enable_wasmtime,
+ .enable_wine = parent.enable_wine,
+ .glibc_runtimes_dir = parent.glibc_runtimes_dir,
+ .host = parent.host,
+ .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
+ };
+ try child.top_level_steps.append(&child.install_tls);
+ try child.top_level_steps.append(&child.uninstall_tls);
+ child.default_step = &child.install_tls.step;
+ return child;
+}
+
+fn applyArgs(b: *Build, args: anytype) !void {
+ inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
+ const v = @field(args, field.name);
+ const T = @TypeOf(v);
+ switch (T) {
+ CrossTarget => {
+ try b.user_input_options.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = try v.zigTriple(b.allocator) },
+ .used = false,
+ });
+ try b.user_input_options.put("cpu", .{
+ .name = "cpu",
+ .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
+ .used = false,
+ });
+ },
+ []const u8 => {
+ try b.user_input_options.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = v },
+ .used = false,
+ });
+ },
+ else => switch (@typeInfo(T)) {
+ .Bool => {
+ try b.user_input_options.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = if (v) "true" else "false" },
+ .used = false,
+ });
+ },
+ .Enum => {
+ try b.user_input_options.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = @tagName(v) },
+ .used = false,
+ });
+ },
+ .Int => {
+ try b.user_input_options.put(field.name, .{
+ .name = field.name,
+ .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
+ .used = false,
+ });
+ },
+ else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
+ },
+ }
+ }
+ const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
+ // Random bytes to make unique. Refresh this with new random bytes when
+ // implementation is modified in a non-backwards-compatible way.
+ var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
+ hash.update(b.dep_prefix);
+ // TODO additionally update the hash with `args`.
+
+ var digest: [16]u8 = undefined;
+ hash.final(&digest);
+ var hash_basename: [digest.len * 2]u8 = undefined;
+ _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
+ unreachable;
+
+ const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
+ b.resolveInstallPrefix(install_prefix, .{});
+}
+
+pub fn destroy(self: *Build) void {
+ self.env_map.deinit();
+ self.top_level_steps.deinit();
+ self.allocator.destroy(self);
+}
+
+/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
+pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
+ if (self.dest_dir) |dest_dir| {
+ self.install_prefix = install_prefix orelse "/usr";
+ self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
+ } else {
+ self.install_prefix = install_prefix orelse
+ (self.pathJoin(&.{ self.build_root, "zig-out" }));
+ self.install_path = self.install_prefix;
+ }
+
+ var lib_list = [_][]const u8{ self.install_path, "lib" };
+ var exe_list = [_][]const u8{ self.install_path, "bin" };
+ var h_list = [_][]const u8{ self.install_path, "include" };
+
+ if (dir_list.lib_dir) |dir| {
+ if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
+ lib_list[1] = dir;
+ }
+
+ if (dir_list.exe_dir) |dir| {
+ if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
+ exe_list[1] = dir;
+ }
+
+ if (dir_list.include_dir) |dir| {
+ if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
+ h_list[1] = dir;
+ }
+
+ self.lib_dir = self.pathJoin(&lib_list);
+ self.exe_dir = self.pathJoin(&exe_list);
+ self.h_dir = self.pathJoin(&h_list);
+}
+
+pub fn addOptions(self: *Build) *OptionsStep {
+ return OptionsStep.create(self);
+}
+
+pub const ExecutableOptions = struct {
+ name: []const u8,
+ root_source_file: ?FileSource = null,
+ version: ?std.builtin.Version = null,
+ target: CrossTarget = .{},
+ optimize: std.builtin.Mode = .Debug,
+ linkage: ?LibExeObjStep.Linkage = null,
+};
+
+pub fn addExecutable(b: *Build, options: ExecutableOptions) *LibExeObjStep {
+ return LibExeObjStep.create(b, .{
+ .name = options.name,
+ .root_source_file = options.root_source_file,
+ .version = options.version,
+ .target = options.target,
+ .optimize = options.optimize,
+ .kind = .exe,
+ .linkage = options.linkage,
+ });
+}
+
+pub const ObjectOptions = struct {
+ name: []const u8,
+ root_source_file: ?FileSource = null,
+ target: CrossTarget,
+ optimize: std.builtin.Mode,
+};
+
+pub fn addObject(b: *Build, options: ObjectOptions) *LibExeObjStep {
+ return LibExeObjStep.create(b, .{
+ .name = options.name,
+ .root_source_file = options.root_source_file,
+ .target = options.target,
+ .optimize = options.optimize,
+ .kind = .obj,
+ });
+}
+
+pub const SharedLibraryOptions = struct {
+ name: []const u8,
+ root_source_file: ?FileSource = null,
+ version: ?std.builtin.Version = null,
+ target: CrossTarget,
+ optimize: std.builtin.Mode,
+};
+
+pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *LibExeObjStep {
+ return LibExeObjStep.create(b, .{
+ .name = options.name,
+ .root_source_file = options.root_source_file,
+ .kind = .lib,
+ .linkage = .dynamic,
+ .version = options.version,
+ .target = options.target,
+ .optimize = options.optimize,
+ });
+}
+
+pub const StaticLibraryOptions = struct {
+ name: []const u8,
+ root_source_file: ?FileSource = null,
+ target: CrossTarget,
+ optimize: std.builtin.Mode,
+ version: ?std.builtin.Version = null,
+};
+
+pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *LibExeObjStep {
+ return LibExeObjStep.create(b, .{
+ .name = options.name,
+ .root_source_file = options.root_source_file,
+ .kind = .lib,
+ .linkage = .static,
+ .version = options.version,
+ .target = options.target,
+ .optimize = options.optimize,
+ });
+}
+
+pub const TestOptions = struct {
+ name: []const u8 = "test",
+ kind: LibExeObjStep.Kind = .@"test",
+ root_source_file: FileSource,
+ target: CrossTarget = .{},
+ optimize: std.builtin.Mode = .Debug,
+ version: ?std.builtin.Version = null,
+};
+
+pub fn addTest(b: *Build, options: TestOptions) *LibExeObjStep {
+ return LibExeObjStep.create(b, .{
+ .name = options.name,
+ .kind = options.kind,
+ .root_source_file = options.root_source_file,
+ .target = options.target,
+ .optimize = options.optimize,
+ });
+}
+
+pub const AssemblyOptions = struct {
+ name: []const u8,
+ source_file: FileSource,
+ target: CrossTarget,
+ optimize: std.builtin.Mode,
+};
+
+pub fn addAssembly(b: *Build, options: AssemblyOptions) *LibExeObjStep {
+ const obj_step = LibExeObjStep.create(b, .{
+ .name = options.name,
+ .root_source_file = null,
+ .target = options.target,
+ .optimize = options.optimize,
+ });
+ obj_step.addAssemblyFileSource(options.source_file.dupe(b));
+ return obj_step;
+}
+
+/// Initializes a RunStep with argv, which must at least have the path to the
+/// executable. More command line arguments can be added with `addArg`,
+/// `addArgs`, and `addArtifactArg`.
+/// Be careful using this function, as it introduces a system dependency.
+/// To run an executable built with zig build, see `LibExeObjStep.run`.
+pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
+ assert(argv.len >= 1);
+ const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
+ run_step.addArgs(argv);
+ return run_step;
+}
+
+pub fn addConfigHeader(
+ b: *Build,
+ source: FileSource,
+ style: ConfigHeaderStep.Style,
+ values: anytype,
+) *ConfigHeaderStep {
+ const config_header_step = ConfigHeaderStep.create(b, source, style);
+ config_header_step.addValues(values);
+ return config_header_step;
+}
+
+/// Allocator.dupe without the need to handle out of memory.
+pub fn dupe(self: *Build, bytes: []const u8) []u8 {
+ return self.allocator.dupe(u8, bytes) catch unreachable;
+}
+
+/// Duplicates an array of strings without the need to handle out of memory.
+pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 {
+ const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
+ for (strings) |s, i| {
+ array[i] = self.dupe(s);
+ }
+ return array;
+}
+
+/// Duplicates a path and converts all slashes to the OS's canonical path separator.
+pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
+ const the_copy = self.dupe(bytes);
+ for (the_copy) |*byte| {
+ switch (byte.*) {
+ '/', '\\' => byte.* = fs.path.sep,
+ else => {},
+ }
+ }
+ return the_copy;
+}
+
+/// Duplicates a package recursively.
+pub fn dupePkg(self: *Build, package: Pkg) Pkg {
+ var the_copy = Pkg{
+ .name = self.dupe(package.name),
+ .source = package.source.dupe(self),
+ };
+
+ if (package.dependencies) |dependencies| {
+ const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
+ the_copy.dependencies = new_dependencies;
+
+ for (dependencies) |dep_package, i| {
+ new_dependencies[i] = self.dupePkg(dep_package);
+ }
+ }
+ return the_copy;
+}
+
+pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
+ const write_file_step = self.addWriteFiles();
+ write_file_step.add(file_path, data);
+ return write_file_step;
+}
+
+pub fn addWriteFiles(self: *Build) *WriteFileStep {
+ const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
+ write_file_step.* = WriteFileStep.init(self);
+ return write_file_step;
+}
+
+pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
+ const data = self.fmt(format, args);
+ const log_step = self.allocator.create(LogStep) catch unreachable;
+ log_step.* = LogStep.init(self, data);
+ return log_step;
+}
+
+pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
+ const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
+ remove_dir_step.* = RemoveDirStep.init(self, dir_path);
+ return remove_dir_step;
+}
+
+pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
+ return FmtStep.create(self, paths);
+}
+
+pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
+ return TranslateCStep.create(self, options);
+}
+
+pub fn make(self: *Build, step_names: []const []const u8) !void {
+ try self.makePath(self.cache_root);
+
+ var wanted_steps = ArrayList(*Step).init(self.allocator);
+ defer wanted_steps.deinit();
+
+ if (step_names.len == 0) {
+ try wanted_steps.append(self.default_step);
+ } else {
+ for (step_names) |step_name| {
+ const s = try self.getTopLevelStepByName(step_name);
+ try wanted_steps.append(s);
+ }
+ }
+
+ for (wanted_steps.items) |s| {
+ try self.makeOneStep(s);
+ }
+}
+
+pub fn getInstallStep(self: *Build) *Step {
+ return &self.install_tls.step;
+}
+
+pub fn getUninstallStep(self: *Build) *Step {
+ return &self.uninstall_tls.step;
+}
+
+fn makeUninstall(uninstall_step: *Step) anyerror!void {
+ const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
+ const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
+
+ for (self.installed_files.items) |installed_file| {
+ const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
+ if (self.verbose) {
+ log.info("rm {s}", .{full_path});
+ }
+ fs.cwd().deleteTree(full_path) catch {};
+ }
+
+ // TODO remove empty directories
+}
+
+fn makeOneStep(self: *Build, s: *Step) anyerror!void {
+ if (s.loop_flag) {
+ log.err("Dependency loop detected:\n {s}", .{s.name});
+ return error.DependencyLoopDetected;
+ }
+ s.loop_flag = true;
+
+ for (s.dependencies.items) |dep| {
+ self.makeOneStep(dep) catch |err| {
+ if (err == error.DependencyLoopDetected) {
+ log.err(" {s}", .{s.name});
+ }
+ return err;
+ };
+ }
+
+ s.loop_flag = false;
+
+ try s.make();
+}
+
+fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
+ for (self.top_level_steps.items) |top_level_step| {
+ if (mem.eql(u8, top_level_step.step.name, name)) {
+ return &top_level_step.step;
+ }
+ }
+ log.err("Cannot run step '{s}' because it does not exist", .{name});
+ return error.InvalidStepName;
+}
+
+pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
+ const name = self.dupe(name_raw);
+ const description = self.dupe(description_raw);
+ const type_id = comptime typeToEnum(T);
+ const enum_options = if (type_id == .@"enum") blk: {
+ const fields = comptime std.meta.fields(T);
+ var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
+
+ inline for (fields) |field| {
+ options.appendAssumeCapacity(field.name);
+ }
+
+ break :blk options.toOwnedSlice() catch unreachable;
+ } else null;
+ const available_option = AvailableOption{
+ .name = name,
+ .type_id = type_id,
+ .description = description,
+ .enum_options = enum_options,
+ };
+ if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
+ panic("Option '{s}' declared twice", .{name});
+ }
+ self.available_options_list.append(available_option) catch unreachable;
+
+ const option_ptr = self.user_input_options.getPtr(name) orelse return null;
+ option_ptr.used = true;
+ switch (type_id) {
+ .bool => switch (option_ptr.value) {
+ .flag => return true,
+ .scalar => |s| {
+ if (mem.eql(u8, s, "true")) {
+ return true;
+ } else if (mem.eql(u8, s, "false")) {
+ return false;
+ } else {
+ log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
+ self.markInvalidUserInput();
+ return null;
+ }
+ },
+ .list, .map => {
+ log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
+ name, @tagName(option_ptr.value),
+ });
+ self.markInvalidUserInput();
+ return null;
+ },
+ },
+ .int => switch (option_ptr.value) {
+ .flag, .list, .map => {
+ log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
+ name, @tagName(option_ptr.value),
+ });
+ self.markInvalidUserInput();
+ return null;
+ },
+ .scalar => |s| {
+ const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
+ error.Overflow => {
+ log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
+ self.markInvalidUserInput();
+ return null;
+ },
+ else => {
+ log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
+ self.markInvalidUserInput();
+ return null;
+ },
+ };
+ return n;
+ },
+ },
+ .float => switch (option_ptr.value) {
+ .flag, .map, .list => {
+ log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
+ name, @tagName(option_ptr.value),
+ });
+ self.markInvalidUserInput();
+ return null;
+ },
+ .scalar => |s| {
+ const n = std.fmt.parseFloat(T, s) catch {
+ log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
+ self.markInvalidUserInput();
+ return null;
+ };
+ return n;
+ },
+ },
+ .@"enum" => switch (option_ptr.value) {
+ .flag, .map, .list => {
+ log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
+ name, @tagName(option_ptr.value),
+ });
+ self.markInvalidUserInput();
+ return null;
+ },
+ .scalar => |s| {
+ if (std.meta.stringToEnum(T, s)) |enum_lit| {
+ return enum_lit;
+ } else {
+ log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
+ self.markInvalidUserInput();
+ return null;
+ }
+ },
+ },
+ .string => switch (option_ptr.value) {
+ .flag, .list, .map => {
+ log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
+ name, @tagName(option_ptr.value),
+ });
+ self.markInvalidUserInput();
+ return null;
+ },
+ .scalar => |s| return s,
+ },
+ .list => switch (option_ptr.value) {
+ .flag, .map => {
+ log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
+ name, @tagName(option_ptr.value),
+ });
+ self.markInvalidUserInput();
+ return null;
+ },
+ .scalar => |s| {
+ return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
+ },
+ .list => |lst| return lst.items,
+ },
+ }
+}
+
+pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
+ const step_info = self.allocator.create(TopLevelStep) catch unreachable;
+ step_info.* = TopLevelStep{
+ .step = Step.initNoOp(.top_level, name, self.allocator),
+ .description = self.dupe(description),
+ };
+ self.top_level_steps.append(step_info) catch unreachable;
+ return &step_info.step;
+}
+
+pub const StandardOptimizeOptionOptions = struct {
+ preferred_optimize_mode: ?std.builtin.Mode = null,
+};
+
+pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode {
+ if (options.preferred_optimize_mode) |mode| {
+ if (self.option(bool, "release", "optimize for end users") orelse false) {
+ return mode;
+ } else {
+ return .Debug;
+ }
+ } else {
+ return self.option(
+ std.builtin.Mode,
+ "optimize",
+ "prioritize performance, safety, or binary size (-O flag)",
+ ) orelse .Debug;
+ }
+}
+
+pub const StandardTargetOptionsArgs = struct {
+ whitelist: ?[]const CrossTarget = null,
+
+ default_target: CrossTarget = CrossTarget{},
+};
+
+/// Exposes standard `zig build` options for choosing a target.
+pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
+ const maybe_triple = self.option(
+ []const u8,
+ "target",
+ "The CPU architecture, OS, and ABI to build for",
+ );
+ const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
+
+ if (maybe_triple == null and mcpu == null) {
+ return args.default_target;
+ }
+
+ const triple = maybe_triple orelse "native";
+
+ var diags: CrossTarget.ParseOptions.Diagnostics = .{};
+ const selected_target = CrossTarget.parse(.{
+ .arch_os_abi = triple,
+ .cpu_features = mcpu,
+ .diagnostics = &diags,
+ }) catch |err| switch (err) {
+ error.UnknownCpuModel => {
+ log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
+ diags.cpu_name.?,
+ @tagName(diags.arch.?),
+ });
+ for (diags.arch.?.allCpuModels()) |cpu| {
+ log.err(" {s}", .{cpu.name});
+ }
+ self.markInvalidUserInput();
+ return args.default_target;
+ },
+ error.UnknownCpuFeature => {
+ log.err(
+ \\Unknown CPU feature: '{s}'
+ \\Available CPU features for architecture '{s}':
+ \\
+ , .{
+ diags.unknown_feature_name.?,
+ @tagName(diags.arch.?),
+ });
+ for (diags.arch.?.allFeaturesList()) |feature| {
+ log.err(" {s}: {s}", .{ feature.name, feature.description });
+ }
+ self.markInvalidUserInput();
+ return args.default_target;
+ },
+ error.UnknownOperatingSystem => {
+ log.err(
+ \\Unknown OS: '{s}'
+ \\Available operating systems:
+ \\
+ , .{diags.os_name.?});
+ inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
+ log.err(" {s}", .{field.name});
+ }
+ self.markInvalidUserInput();
+ return args.default_target;
+ },
+ else => |e| {
+ log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
+ self.markInvalidUserInput();
+ return args.default_target;
+ },
+ };
+
+ const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
+
+ if (args.whitelist) |list| whitelist_check: {
+ // Make sure it's a match of one of the list.
+ var mismatch_triple = true;
+ var mismatch_cpu_features = true;
+ var whitelist_item = CrossTarget{};
+ for (list) |t| {
+ mismatch_cpu_features = true;
+ mismatch_triple = true;
+
+ const t_triple = t.zigTriple(self.allocator) catch unreachable;
+ if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
+ mismatch_triple = false;
+ whitelist_item = t;
+ if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
+ mismatch_cpu_features = false;
+ break :whitelist_check;
+ } else {
+ break;
+ }
+ }
+ }
+ if (mismatch_triple) {
+ log.err("Chosen target '{s}' does not match one of the supported targets:", .{
+ selected_canonicalized_triple,
+ });
+ for (list) |t| {
+ const t_triple = t.zigTriple(self.allocator) catch unreachable;
+ log.err(" {s}", .{t_triple});
+ }
+ } else {
+ assert(mismatch_cpu_features);
+ const whitelist_cpu = whitelist_item.getCpu();
+ const selected_cpu = selected_target.getCpu();
+ log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
+ selected_cpu.model.name,
+ });
+ log.err(" Supported feature Set: ", .{});
+ const all_features = whitelist_cpu.arch.allFeaturesList();
+ var populated_cpu_features = whitelist_cpu.model.features;
+ populated_cpu_features.populateDependencies(all_features);
+ for (all_features) |feature, i_usize| {
+ const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
+ const in_cpu_set = populated_cpu_features.isEnabled(i);
+ if (in_cpu_set) {
+ log.err("{s} ", .{feature.name});
+ }
+ }
+ log.err(" Remove: ", .{});
+ for (all_features) |feature, i_usize| {
+ const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
+ const in_cpu_set = populated_cpu_features.isEnabled(i);
+ const in_actual_set = selected_cpu.features.isEnabled(i);
+ if (in_actual_set and !in_cpu_set) {
+ log.err("{s} ", .{feature.name});
+ }
+ }
+ }
+ self.markInvalidUserInput();
+ return args.default_target;
+ }
+
+ return selected_target;
+}
+
+pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool {
+ const name = self.dupe(name_raw);
+ const value = self.dupe(value_raw);
+ const gop = try self.user_input_options.getOrPut(name);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = UserInputOption{
+ .name = name,
+ .value = .{ .scalar = value },
+ .used = false,
+ };
+ return false;
+ }
+
+ // option already exists
+ switch (gop.value_ptr.value) {
+ .scalar => |s| {
+ // turn it into a list
+ var list = ArrayList([]const u8).init(self.allocator);
+ list.append(s) catch unreachable;
+ list.append(value) catch unreachable;
+ self.user_input_options.put(name, .{
+ .name = name,
+ .value = .{ .list = list },
+ .used = false,
+ }) catch unreachable;
+ },
+ .list => |*list| {
+ // append to the list
+ list.append(value) catch unreachable;
+ self.user_input_options.put(name, .{
+ .name = name,
+ .value = .{ .list = list.* },
+ .used = false,
+ }) catch unreachable;
+ },
+ .flag => {
+ log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
+ return true;
+ },
+ .map => |*map| {
+ _ = map;
+ log.warn("TODO maps as command line arguments is not implemented yet.", .{});
+ return true;
+ },
+ }
+ return false;
+}
+
+pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
+ const name = self.dupe(name_raw);
+ const gop = try self.user_input_options.getOrPut(name);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = .{
+ .name = name,
+ .value = .{ .flag = {} },
+ .used = false,
+ };
+ return false;
+ }
+
+ // option already exists
+ switch (gop.value_ptr.value) {
+ .scalar => |s| {
+ log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
+ return true;
+ },
+ .list, .map => {
+ log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
+ return true;
+ },
+ .flag => {},
+ }
+ return false;
+}
+
+fn typeToEnum(comptime T: type) TypeId {
+ return switch (@typeInfo(T)) {
+ .Int => .int,
+ .Float => .float,
+ .Bool => .bool,
+ .Enum => .@"enum",
+ else => switch (T) {
+ []const u8 => .string,
+ []const []const u8 => .list,
+ else => @compileError("Unsupported type: " ++ @typeName(T)),
+ },
+ };
+}
+
+fn markInvalidUserInput(self: *Build) void {
+ self.invalid_user_input = true;
+}
+
+pub fn validateUserInputDidItFail(self: *Build) bool {
+ // make sure all args are used
+ var it = self.user_input_options.iterator();
+ while (it.next()) |entry| {
+ if (!entry.value_ptr.used) {
+ log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
+ self.markInvalidUserInput();
+ }
+ }
+
+ return self.invalid_user_input;
+}
+
+pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
+ return self.spawnChildEnvMap(null, self.env_map, argv);
+}
+
+fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
+ if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
+ for (argv) |arg| {
+ std.debug.print("{s} ", .{arg});
+ }
+ std.debug.print("\n", .{});
+}
+
+pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
+ if (self.verbose) {
+ printCmd(cwd, argv);
+ }
+
+ if (!std.process.can_spawn)
+ return error.ExecNotSupported;
+
+ var child = std.ChildProcess.init(argv, self.allocator);
+ child.cwd = cwd;
+ child.env_map = env_map;
+
+ const term = child.spawnAndWait() catch |err| {
+ log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
+ return err;
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ log.err("The following command exited with error code {}:", .{code});
+ printCmd(cwd, argv);
+ return error.UncleanExit;
+ }
+ },
+ else => {
+ log.err("The following command terminated unexpectedly:", .{});
+ printCmd(cwd, argv);
+
+ return error.UncleanExit;
+ },
+ }
+}
+
+pub fn makePath(self: *Build, path: []const u8) !void {
+ fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
+ log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
+ return err;
+ };
+}
+
+pub fn installArtifact(self: *Build, artifact: *LibExeObjStep) void {
+ self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
+}
+
+pub fn addInstallArtifact(self: *Build, artifact: *LibExeObjStep) *InstallArtifactStep {
+ return InstallArtifactStep.create(self, artifact);
+}
+
+///`dest_rel_path` is relative to prefix path
+pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
+ self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
+}
+
+pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
+ self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
+}
+
+///`dest_rel_path` is relative to bin path
+pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
+ self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
+}
+
+///`dest_rel_path` is relative to lib path
+pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
+ self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
+}
+
+/// Output format (BIN vs Intel HEX) determined by filename
+pub fn installRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
+ const raw = self.addInstallRaw(artifact, dest_filename, options);
+ self.getInstallStep().dependOn(&raw.step);
+ return raw;
+}
+
+///`dest_rel_path` is relative to install prefix path
+pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
+ return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
+}
+
+///`dest_rel_path` is relative to bin path
+pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
+ return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
+}
+
+///`dest_rel_path` is relative to lib path
+pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
+ return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
+}
+
+pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
+ return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
+}
+
+pub fn addInstallRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
+ return InstallRawStep.create(self, artifact, dest_filename, options);
+}
+
+pub fn addInstallFileWithDir(
+ self: *Build,
+ source: FileSource,
+ install_dir: InstallDir,
+ dest_rel_path: []const u8,
+) *InstallFileStep {
+ if (dest_rel_path.len == 0) {
+ panic("dest_rel_path must be non-empty", .{});
+ }
+ const install_step = self.allocator.create(InstallFileStep) catch unreachable;
+ install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
+ return install_step;
+}
+
+pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
+ const install_step = self.allocator.create(InstallDirStep) catch unreachable;
+ install_step.* = InstallDirStep.init(self, options);
+ return install_step;
+}
+
+pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
+ const file = InstalledFile{
+ .dir = dir,
+ .path = dest_rel_path,
+ };
+ self.installed_files.append(file.dupe(self)) catch unreachable;
+}
+
+pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
+ if (self.verbose) {
+ log.info("cp {s} {s} ", .{ source_path, dest_path });
+ }
+ const cwd = fs.cwd();
+ const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
+ if (self.verbose) switch (prev_status) {
+ .stale => log.info("# installed", .{}),
+ .fresh => log.info("# up-to-date", .{}),
+ };
+}
+
+pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
+ if (self.verbose) {
+ log.info("truncate {s}", .{dest_path});
+ }
+ const cwd = fs.cwd();
+ var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
+ error.FileNotFound => blk: {
+ if (fs.path.dirname(dest_path)) |dirname| {
+ try cwd.makePath(dirname);
+ }
+ break :blk try cwd.createFile(dest_path, .{});
+ },
+ else => |e| return e,
+ };
+ src_file.close();
+}
+
+pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
+ return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
+}
+
+/// Shorthand for `std.fs.path.join(Build.allocator, paths) catch unreachable`
+pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
+ return fs.path.join(self.allocator, paths) catch unreachable;
+}
+
+pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
+ return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
+}
+
+pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
+ // TODO report error for ambiguous situations
+ const exe_extension = @as(CrossTarget, .{}).exeFileExt();
+ for (self.search_prefixes.items) |search_prefix| {
+ for (names) |name| {
+ if (fs.path.isAbsolute(name)) {
+ return name;
+ }
+ const full_path = self.pathJoin(&.{
+ search_prefix,
+ "bin",
+ self.fmt("{s}{s}", .{ name, exe_extension }),
+ });
+ return fs.realpathAlloc(self.allocator, full_path) catch continue;
+ }
+ }
+ if (self.env_map.get("PATH")) |PATH| {
+ for (names) |name| {
+ if (fs.path.isAbsolute(name)) {
+ return name;
+ }
+ var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
+ while (it.next()) |path| {
+ const full_path = self.pathJoin(&.{
+ path,
+ self.fmt("{s}{s}", .{ name, exe_extension }),
+ });
+ return fs.realpathAlloc(self.allocator, full_path) catch continue;
+ }
+ }
+ }
+ for (names) |name| {
+ if (fs.path.isAbsolute(name)) {
+ return name;
+ }
+ for (paths) |path| {
+ const full_path = self.pathJoin(&.{
+ path,
+ self.fmt("{s}{s}", .{ name, exe_extension }),
+ });
+ return fs.realpathAlloc(self.allocator, full_path) catch continue;
+ }
+ }
+ return error.FileNotFound;
+}
+
+pub fn execAllowFail(
+ self: *Build,
+ argv: []const []const u8,
+ out_code: *u8,
+ stderr_behavior: std.ChildProcess.StdIo,
+) ExecError![]u8 {
+ assert(argv.len != 0);
+
+ if (!std.process.can_spawn)
+ return error.ExecNotSupported;
+
+ const max_output_size = 400 * 1024;
+ var child = std.ChildProcess.init(argv, self.allocator);
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Pipe;
+ child.stderr_behavior = stderr_behavior;
+ child.env_map = self.env_map;
+
+ try child.spawn();
+
+ const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
+ return error.ReadFailure;
+ };
+ errdefer self.allocator.free(stdout);
+
+ const term = try child.wait();
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ out_code.* = @truncate(u8, code);
+ return error.ExitCodeFailure;
+ }
+ return stdout;
+ },
+ .Signal, .Stopped, .Unknown => |code| {
+ out_code.* = @truncate(u8, code);
+ return error.ProcessTerminated;
+ },
+ }
+}
+
+pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
+ assert(argv.len != 0);
+
+ if (self.verbose) {
+ printCmd(null, argv);
+ }
+
+ if (!std.process.can_spawn) {
+ if (src_step) |s| log.err("{s}...", .{s.name});
+ log.err("Unable to spawn the following command: cannot spawn child process", .{});
+ printCmd(null, argv);
+ std.os.abort();
+ }
+
+ var code: u8 = undefined;
+ return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
+ error.ExecNotSupported => {
+ if (src_step) |s| log.err("{s}...", .{s.name});
+ log.err("Unable to spawn the following command: cannot spawn child process", .{});
+ printCmd(null, argv);
+ std.os.abort();
+ },
+ error.FileNotFound => {
+ if (src_step) |s| log.err("{s}...", .{s.name});
+ log.err("Unable to spawn the following command: file not found", .{});
+ printCmd(null, argv);
+ std.os.exit(@truncate(u8, code));
+ },
+ error.ExitCodeFailure => {
+ if (src_step) |s| log.err("{s}...", .{s.name});
+ if (self.prominent_compile_errors) {
+ log.err("The step exited with error code {d}", .{code});
+ } else {
+ log.err("The following command exited with error code {d}:", .{code});
+ printCmd(null, argv);
+ }
+
+ std.os.exit(@truncate(u8, code));
+ },
+ error.ProcessTerminated => {
+ if (src_step) |s| log.err("{s}...", .{s.name});
+ log.err("The following command terminated unexpectedly:", .{});
+ printCmd(null, argv);
+ std.os.exit(@truncate(u8, code));
+ },
+ else => |e| return e,
+ };
+}
+
+pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
+ return self.execFromStep(argv, null);
+}
+
+pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
+ self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
+}
+
+pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
+ assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
+ const base_dir = switch (dir) {
+ .prefix => self.install_path,
+ .bin => self.exe_dir,
+ .lib => self.lib_dir,
+ .header => self.h_dir,
+ .custom => |path| self.pathJoin(&.{ self.install_path, path }),
+ };
+ return fs.path.resolve(
+ self.allocator,
+ &[_][]const u8{ base_dir, dest_rel_path },
+ ) catch unreachable;
+}
+
+pub const Dependency = struct {
+ builder: *Build,
+
+ pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
+ var found: ?*LibExeObjStep = null;
+ for (d.builder.install_tls.step.dependencies.items) |dep_step| {
+ const inst = dep_step.cast(InstallArtifactStep) orelse continue;
+ if (mem.eql(u8, inst.artifact.name, name)) {
+ if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
+ found = inst.artifact;
+ }
+ }
+ return found orelse {
+ for (d.builder.install_tls.step.dependencies.items) |dep_step| {
+ const inst = dep_step.cast(InstallArtifactStep) orelse continue;
+ log.info("available artifact: '{s}'", .{inst.artifact.name});
+ }
+ panic("unable to find artifact '{s}'", .{name});
+ };
+ }
+};
+
+pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
+ const build_runner = @import("root");
+ const deps = build_runner.dependencies;
+
+ inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
+ if (mem.startsWith(u8, decl.name, b.dep_prefix) and
+ mem.endsWith(u8, decl.name, name) and
+ decl.name.len == b.dep_prefix.len + name.len)
+ {
+ const build_zig = @field(deps.imports, decl.name);
+ const build_root = @field(deps.build_root, decl.name);
+ return dependencyInner(b, name, build_root, build_zig, args);
+ }
+ }
+
+ const full_path = b.pathFromRoot("build.zig.ini");
+ std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
+ std.process.exit(1);
+}
+
+fn dependencyInner(
+ b: *Build,
+ name: []const u8,
+ build_root: []const u8,
+ comptime build_zig: type,
+ args: anytype,
+) *Dependency {
+ const sub_builder = b.createChild(name, build_root, args) catch unreachable;
+ sub_builder.runBuild(build_zig) catch unreachable;
+
+ if (sub_builder.validateUserInputDidItFail()) {
+ std.debug.dumpCurrentStackTrace(@returnAddress());
+ }
+
+ const dep = b.allocator.create(Dependency) catch unreachable;
+ dep.* = .{ .builder = sub_builder };
+ return dep;
+}
+
+pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
+ switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
+ .Void => build_zig.build(b),
+ .ErrorUnion => try build_zig.build(b),
+ else => @compileError("expected return type of build to be 'void' or '!void'"),
+ }
+}
+
+test "builder.findProgram compiles" {
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ defer arena.deinit();
+
+ const builder = try Build.create(
+ arena.allocator(),
+ "zig",
+ "zig-cache",
+ "zig-cache",
+ "zig-cache",
+ );
+ defer builder.destroy();
+ _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
+}
+
+pub const Pkg = struct {
+ name: []const u8,
+ source: FileSource,
+ dependencies: ?[]const Pkg = null,
+};
+
+/// A file that is generated by a build step.
+/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
+pub const GeneratedFile = struct {
+ /// The step that generates the file
+ step: *Step,
+
+ /// The path to the generated file. Must be either absolute or relative to the build root.
+ /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
+ path: ?[]const u8 = null,
+
+ pub fn getPath(self: GeneratedFile) []const u8 {
+ return self.path orelse std.debug.panic(
+ "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
+ .{self.step.name},
+ );
+ }
+};
+
+/// A file source is a reference to an existing or future file.
+///
+pub const FileSource = union(enum) {
+ /// A plain file path, relative to build root or absolute.
+ path: []const u8,
+
+ /// A file that is generated by an interface. Those files usually are
+ /// not available until built by a build step.
+ generated: *const GeneratedFile,
+
+ /// Returns a new file source that will have a relative path to the build root guaranteed.
+ /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
+ pub fn relative(path: []const u8) FileSource {
+ std.debug.assert(!std.fs.path.isAbsolute(path));
+ return FileSource{ .path = path };
+ }
+
+ /// Returns a string that can be shown to represent the file source.
+ /// Either returns the path or `"generated"`.
+ pub fn getDisplayName(self: FileSource) []const u8 {
+ return switch (self) {
+ .path => self.path,
+ .generated => "generated",
+ };
+ }
+
+ /// Adds dependencies this file source implies to the given step.
+ pub fn addStepDependencies(self: FileSource, other_step: *Step) void {
+ switch (self) {
+ .path => {},
+ .generated => |gen| other_step.dependOn(gen.step),
+ }
+ }
+
+ /// Should only be called during make(), returns a path relative to the build root or absolute.
+ pub fn getPath(self: FileSource, builder: *Build) []const u8 {
+ const path = switch (self) {
+ .path => |p| builder.pathFromRoot(p),
+ .generated => |gen| gen.getPath(),
+ };
+ return path;
+ }
+
+ /// Duplicates the file source for a given builder.
+ pub fn dupe(self: FileSource, b: *Build) FileSource {
+ return switch (self) {
+ .path => |p| .{ .path = b.dupePath(p) },
+ .generated => |gen| .{ .generated = gen },
+ };
+ }
+};
+
+/// Allocates a new string for assigning a value to a named macro.
+/// If the value is omitted, it is set to 1.
+/// `name` and `value` need not live longer than the function call.
+pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
+ var macro = allocator.alloc(
+ u8,
+ name.len + if (value) |value_slice| value_slice.len + 1 else 0,
+ ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
+ mem.copy(u8, macro, name);
+ if (value) |value_slice| {
+ macro[name.len] = '=';
+ mem.copy(u8, macro[name.len + 1 ..], value_slice);
+ }
+ return macro;
+}
+
+/// deprecated: use `InstallDirStep.Options`
+pub const InstallDirectoryOptions = InstallDirStep.Options;
+
+pub const VcpkgRoot = union(VcpkgRootStatus) {
+ unattempted: void,
+ not_found: void,
+ found: []const u8,
+};
+
+pub const VcpkgRootStatus = enum {
+ unattempted,
+ not_found,
+ found,
+};
+
+pub const InstallDir = union(enum) {
+ prefix: void,
+ lib: void,
+ bin: void,
+ header: void,
+ /// A path relative to the prefix
+ custom: []const u8,
+
+ /// Duplicates the install directory including the path if set to custom.
+ pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
+ if (self == .custom) {
+ // Written with this temporary to avoid RLS problems
+ const duped_path = builder.dupe(self.custom);
+ return .{ .custom = duped_path };
+ } else {
+ return self;
+ }
+ }
+};
+
+pub const InstalledFile = struct {
+ dir: InstallDir,
+ path: []const u8,
+
+ /// Duplicates the installed file path and directory.
+ pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile {
+ return .{
+ .dir = self.dir.dupe(builder),
+ .path = builder.dupe(self.path),
+ };
+ }
+};
+
+pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
+ // TODO this logic can disappear if cpu model + features becomes part of the target triple
+ const all_features = cpu.arch.allFeaturesList();
+ var populated_cpu_features = cpu.model.features;
+ populated_cpu_features.populateDependencies(all_features);
+
+ if (populated_cpu_features.eql(cpu.features)) {
+ // The CPU name alone is sufficient.
+ return cpu.model.name;
+ } else {
+ var mcpu_buffer = ArrayList(u8).init(allocator);
+ try mcpu_buffer.appendSlice(cpu.model.name);
+
+ for (all_features) |feature, i_usize| {
+ const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
+ const in_cpu_set = populated_cpu_features.isEnabled(i);
+ const in_actual_set = cpu.features.isEnabled(i);
+ if (in_cpu_set and !in_actual_set) {
+ try mcpu_buffer.writer().print("-{s}", .{feature.name});
+ } else if (!in_cpu_set and in_actual_set) {
+ try mcpu_buffer.writer().print("+{s}", .{feature.name});
+ }
+ }
+
+ return try mcpu_buffer.toOwnedSlice();
+ }
+}
+
+test "dupePkg()" {
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+ var builder = try Build.create(
+ arena.allocator(),
+ "test",
+ "test",
+ "test",
+ "test",
+ );
+ defer builder.destroy();
+
+ var pkg_dep = Pkg{
+ .name = "pkg_dep",
+ .source = .{ .path = "/not/a/pkg_dep.zig" },
+ };
+ var pkg_top = Pkg{
+ .name = "pkg_top",
+ .source = .{ .path = "/not/a/pkg_top.zig" },
+ .dependencies = &[_]Pkg{pkg_dep},
+ };
+ const duped = builder.dupePkg(pkg_top);
+
+ const original_deps = pkg_top.dependencies.?;
+ const dupe_deps = duped.dependencies.?;
+
+ // probably the same top level package details
+ try std.testing.expectEqualStrings(pkg_top.name, duped.name);
+
+ // probably the same dependencies
+ try std.testing.expectEqual(original_deps.len, dupe_deps.len);
+ try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
+
+ // could segfault otherwise if pointers in duplicated package's fields are
+ // the same as those in stack allocated package's fields
+ try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
+ try std.testing.expect(duped.name.ptr != pkg_top.name.ptr);
+ try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr);
+ try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
+ try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
+}
+
+test {
+ _ = CheckFileStep;
+ _ = CheckObjectStep;
+ _ = EmulatableRunStep;
+ _ = FmtStep;
+ _ = InstallArtifactStep;
+ _ = InstallDirStep;
+ _ = InstallFileStep;
+ _ = InstallRawStep;
+ _ = LibExeObjStep;
+ _ = LogStep;
+ _ = OptionsStep;
+ _ = RemoveDirStep;
+ _ = RunStep;
+ _ = TranslateCStep;
+ _ = WriteFileStep;
+}
diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig
new file mode 100644
index 0000000000..3b8cfe5263
--- /dev/null
+++ b/lib/std/Build/CheckFileStep.zig
@@ -0,0 +1,51 @@
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const fs = std.fs;
+const mem = std.mem;
+
+const CheckFileStep = @This();
+
+pub const base_id = .check_file;
+
+step: Step,
+builder: *std.Build,
+expected_matches: []const []const u8,
+source: std.Build.FileSource,
+max_bytes: usize = 20 * 1024 * 1024,
+
+pub fn create(
+ builder: *std.Build,
+ source: std.Build.FileSource,
+ expected_matches: []const []const u8,
+) *CheckFileStep {
+ const self = builder.allocator.create(CheckFileStep) catch unreachable;
+ self.* = CheckFileStep{
+ .builder = builder,
+ .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
+ .source = source.dupe(builder),
+ .expected_matches = builder.dupeStrings(expected_matches),
+ };
+ self.source.addStepDependencies(&self.step);
+ return self;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(CheckFileStep, "step", step);
+
+ const src_path = self.source.getPath(self.builder);
+ const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
+
+ for (self.expected_matches) |expected_match| {
+ if (mem.indexOf(u8, contents, expected_match) == null) {
+ std.debug.print(
+ \\
+ \\========= Expected to find: ===================
+ \\{s}
+ \\========= But file does not contain it: =======
+ \\{s}
+ \\
+ , .{ expected_match, contents });
+ return error.TestFailed;
+ }
+ }
+}
diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig
new file mode 100644
index 0000000000..7907be1787
--- /dev/null
+++ b/lib/std/Build/CheckObjectStep.zig
@@ -0,0 +1,1024 @@
+const std = @import("../std.zig");
+const assert = std.debug.assert;
+const fs = std.fs;
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const testing = std.testing;
+
+const CheckObjectStep = @This();
+
+const Allocator = mem.Allocator;
+const Step = std.Build.Step;
+const EmulatableRunStep = std.Build.EmulatableRunStep;
+
+pub const base_id = .check_object;
+
+step: Step,
+builder: *std.Build,
+source: std.Build.FileSource,
+max_bytes: usize = 20 * 1024 * 1024,
+checks: std.ArrayList(Check),
+dump_symtab: bool = false,
+obj_format: std.Target.ObjectFormat,
+
+pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
+ const gpa = builder.allocator;
+ const self = gpa.create(CheckObjectStep) catch unreachable;
+ self.* = .{
+ .builder = builder,
+ .step = Step.init(.check_file, "CheckObject", gpa, make),
+ .source = source.dupe(builder),
+ .checks = std.ArrayList(Check).init(gpa),
+ .obj_format = obj_format,
+ };
+ self.source.addStepDependencies(&self.step);
+ return self;
+}
+
+/// Runs and (optionally) compares the output of a binary.
+/// Asserts `self` was generated from an executable step.
+pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
+ const dependencies_len = self.step.dependencies.items.len;
+ assert(dependencies_len > 0);
+ const exe_step = self.step.dependencies.items[dependencies_len - 1];
+ const exe = exe_step.cast(std.Build.LibExeObjStep).?;
+ const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
+ emulatable_step.step.dependOn(&self.step);
+ return emulatable_step;
+}
+
+/// There two types of actions currently suported:
+/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
+/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
+/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
+/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
+/// it should be plenty useful in its current form.
+/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
+/// using the MatchAction. It currently only supports an addition. The operation is required
+/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
+/// to avoid any parsing really).
+/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
+/// they could then be added with this simple program `vmaddr entryoff +`.
+const Action = struct {
+ tag: enum { match, not_present, compute_cmp },
+ phrase: []const u8,
+ expected: ?ComputeCompareExpected = null,
+
+ /// Will return true if the `phrase` was found in the `haystack`.
+ /// Some examples include:
+ ///
+ /// LC 0 => will match in its entirety
+ /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
+ /// and save under `vmaddr` global name (see `global_vars` param)
+ /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
+ /// in that order with other letters in between
+ fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
+ assert(act.tag == .match or act.tag == .not_present);
+
+ var candidate_var: ?struct { name: []const u8, value: u64 } = null;
+ var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
+ var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
+
+ while (needle_it.next()) |needle_tok| {
+ const hay_tok = hay_it.next() orelse return false;
+
+ if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
+ // We have fuzzy matchers within the search pattern, so we match substrings.
+ var start = index;
+ var n_tok = needle_tok;
+ var h_tok = hay_tok;
+ while (true) {
+ n_tok = n_tok[start + 3 ..];
+ const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
+ n_tok[0..sub_end]
+ else
+ n_tok;
+ if (mem.indexOf(u8, h_tok, inner) == null) return false;
+ start = mem.indexOf(u8, n_tok, "{*}") orelse break;
+ }
+ } else if (mem.startsWith(u8, needle_tok, "{")) {
+ const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
+ if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
+
+ const name = needle_tok[1..closing_brace];
+ if (name.len == 0) return error.MissingBraceValue;
+ const value = try std.fmt.parseInt(u64, hay_tok, 16);
+ candidate_var = .{
+ .name = name,
+ .value = value,
+ };
+ } else {
+ if (!mem.eql(u8, hay_tok, needle_tok)) return false;
+ }
+ }
+
+ if (candidate_var) |v| {
+ try global_vars.putNoClobber(v.name, v.value);
+ }
+
+ return true;
+ }
+
+ /// Will return true if the `phrase` is correctly parsed into an RPN program and
+ /// its reduced, computed value compares using `op` with the expected value, either
+ /// a literal or another extracted variable.
+ fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
+ var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
+ var values = std.ArrayList(u64).init(gpa);
+
+ var it = mem.tokenize(u8, act.phrase, " ");
+ while (it.next()) |next| {
+ if (mem.eql(u8, next, "+")) {
+ try op_stack.append(.add);
+ } else if (mem.eql(u8, next, "-")) {
+ try op_stack.append(.sub);
+ } else if (mem.eql(u8, next, "%")) {
+ try op_stack.append(.mod);
+ } else if (mem.eql(u8, next, "*")) {
+ try op_stack.append(.mul);
+ } else {
+ const val = std.fmt.parseInt(u64, next, 0) catch blk: {
+ break :blk global_vars.get(next) orelse {
+ std.debug.print(
+ \\
+ \\========= Variable was not extracted: ===========
+ \\{s}
+ \\
+ , .{next});
+ return error.UnknownVariable;
+ };
+ };
+ try values.append(val);
+ }
+ }
+
+ var op_i: usize = 1;
+ var reduced: u64 = values.items[0];
+ for (op_stack.items) |op| {
+ const other = values.items[op_i];
+ switch (op) {
+ .add => {
+ reduced += other;
+ },
+ .sub => {
+ reduced -= other;
+ },
+ .mod => {
+ reduced %= other;
+ },
+ .mul => {
+ reduced *= other;
+ },
+ }
+ op_i += 1;
+ }
+
+ const exp_value = switch (act.expected.?.value) {
+ .variable => |name| global_vars.get(name) orelse {
+ std.debug.print(
+ \\
+ \\========= Variable was not extracted: ===========
+ \\{s}
+ \\
+ , .{name});
+ return error.UnknownVariable;
+ },
+ .literal => |x| x,
+ };
+ return math.compare(reduced, act.expected.?.op, exp_value);
+ }
+};
+
+const ComputeCompareExpected = struct {
+ op: math.CompareOperator,
+ value: union(enum) {
+ variable: []const u8,
+ literal: u64,
+ },
+
+ pub fn format(
+ value: @This(),
+ comptime fmt: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
+ _ = options;
+ try writer.print("{s} ", .{@tagName(value.op)});
+ switch (value.value) {
+ .variable => |name| try writer.writeAll(name),
+ .literal => |x| try writer.print("{x}", .{x}),
+ }
+ }
+};
+
+const Check = struct {
+ builder: *std.Build,
+ actions: std.ArrayList(Action),
+
+ fn create(b: *std.Build) Check {
+ return .{
+ .builder = b,
+ .actions = std.ArrayList(Action).init(b.allocator),
+ };
+ }
+
+ fn match(self: *Check, phrase: []const u8) void {
+ self.actions.append(.{
+ .tag = .match,
+ .phrase = self.builder.dupe(phrase),
+ }) catch unreachable;
+ }
+
+ fn notPresent(self: *Check, phrase: []const u8) void {
+ self.actions.append(.{
+ .tag = .not_present,
+ .phrase = self.builder.dupe(phrase),
+ }) catch unreachable;
+ }
+
+ fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
+ self.actions.append(.{
+ .tag = .compute_cmp,
+ .phrase = self.builder.dupe(phrase),
+ .expected = expected,
+ }) catch unreachable;
+ }
+};
+
+/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
+pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
+ var new_check = Check.create(self.builder);
+ new_check.match(phrase);
+ self.checks.append(new_check) catch unreachable;
+}
+
+/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
+/// Asserts at least one check already exists.
+pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
+ assert(self.checks.items.len > 0);
+ const last = &self.checks.items[self.checks.items.len - 1];
+ last.match(phrase);
+}
+
+/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
+/// however ensures there is no matching phrase in the output.
+/// Asserts at least one check already exists.
+pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
+ assert(self.checks.items.len > 0);
+ const last = &self.checks.items[self.checks.items.len - 1];
+ last.notPresent(phrase);
+}
+
+/// Creates a new check checking specifically symbol table parsed and dumped from the object
+/// file.
+/// Issuing this check will force parsing and dumping of the symbol table.
+pub fn checkInSymtab(self: *CheckObjectStep) void {
+ self.dump_symtab = true;
+ const symtab_label = switch (self.obj_format) {
+ .macho => MachODumper.symtab_label,
+ else => @panic("TODO other parsers"),
+ };
+ self.checkStart(symtab_label);
+}
+
+/// Creates a new standalone, singular check which allows running simple binary operations
+/// on the extracted variables. It will then compare the reduced program with the value of
+/// the expected variable.
+pub fn checkComputeCompare(
+ self: *CheckObjectStep,
+ program: []const u8,
+ expected: ComputeCompareExpected,
+) void {
+ var new_check = Check.create(self.builder);
+ new_check.computeCmp(program, expected);
+ self.checks.append(new_check) catch unreachable;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(CheckObjectStep, "step", step);
+
+ const gpa = self.builder.allocator;
+ const src_path = self.source.getPath(self.builder);
+ const contents = try fs.cwd().readFileAllocOptions(
+ gpa,
+ src_path,
+ self.max_bytes,
+ null,
+ @alignOf(u64),
+ null,
+ );
+
+ const output = switch (self.obj_format) {
+ .macho => try MachODumper.parseAndDump(contents, .{
+ .gpa = gpa,
+ .dump_symtab = self.dump_symtab,
+ }),
+ .elf => @panic("TODO elf parser"),
+ .coff => @panic("TODO coff parser"),
+ .wasm => try WasmDumper.parseAndDump(contents, .{
+ .gpa = gpa,
+ .dump_symtab = self.dump_symtab,
+ }),
+ else => unreachable,
+ };
+
+ var vars = std.StringHashMap(u64).init(gpa);
+
+ for (self.checks.items) |chk| {
+ var it = mem.tokenize(u8, output, "\r\n");
+ for (chk.actions.items) |act| {
+ switch (act.tag) {
+ .match => {
+ while (it.next()) |line| {
+ if (try act.match(line, &vars)) break;
+ } else {
+ std.debug.print(
+ \\
+ \\========= Expected to find: ==========================
+ \\{s}
+ \\========= But parsed file does not contain it: =======
+ \\{s}
+ \\
+ , .{ act.phrase, output });
+ return error.TestFailed;
+ }
+ },
+ .not_present => {
+ while (it.next()) |line| {
+ if (try act.match(line, &vars)) {
+ std.debug.print(
+ \\
+ \\========= Expected not to find: ===================
+ \\{s}
+ \\========= But parsed file does contain it: ========
+ \\{s}
+ \\
+ , .{ act.phrase, output });
+ return error.TestFailed;
+ }
+ }
+ },
+ .compute_cmp => {
+ const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
+ error.UnknownVariable => {
+ std.debug.print(
+ \\========= From parsed file: =====================
+ \\{s}
+ \\
+ , .{output});
+ return error.TestFailed;
+ },
+ else => |e| return e,
+ };
+ if (!res) {
+ std.debug.print(
+ \\
+ \\========= Comparison failed for action: ===========
+ \\{s} {}
+ \\========= From parsed file: =======================
+ \\{s}
+ \\
+ , .{ act.phrase, act.expected.?, output });
+ return error.TestFailed;
+ }
+ },
+ }
+ }
+ }
+}
+
+const Opts = struct {
+ gpa: ?Allocator = null,
+ dump_symtab: bool = false,
+};
+
+const MachODumper = struct {
+ const LoadCommandIterator = macho.LoadCommandIterator;
+ const symtab_label = "symtab";
+
+ fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
+ const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
+ var stream = std.io.fixedBufferStream(bytes);
+ const reader = stream.reader();
+
+ const hdr = try reader.readStruct(macho.mach_header_64);
+ if (hdr.magic != macho.MH_MAGIC_64) {
+ return error.InvalidMagicNumber;
+ }
+
+ var output = std.ArrayList(u8).init(gpa);
+ const writer = output.writer();
+
+ var symtab: []const macho.nlist_64 = undefined;
+ var strtab: []const u8 = undefined;
+ var sections = std.ArrayList(macho.section_64).init(gpa);
+ var imports = std.ArrayList([]const u8).init(gpa);
+
+ var it = LoadCommandIterator{
+ .ncmds = hdr.ncmds,
+ .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
+ };
+ var i: usize = 0;
+ while (it.next()) |cmd| {
+ switch (cmd.cmd()) {
+ .SEGMENT_64 => {
+ const seg = cmd.cast(macho.segment_command_64).?;
+ try sections.ensureUnusedCapacity(seg.nsects);
+ for (cmd.getSections()) |sect| {
+ sections.appendAssumeCapacity(sect);
+ }
+ },
+ .SYMTAB => if (opts.dump_symtab) {
+ const lc = cmd.cast(macho.symtab_command).?;
+ symtab = @ptrCast(
+ [*]const macho.nlist_64,
+ @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
+ )[0..lc.nsyms];
+ strtab = bytes[lc.stroff..][0..lc.strsize];
+ },
+ .LOAD_DYLIB,
+ .LOAD_WEAK_DYLIB,
+ .REEXPORT_DYLIB,
+ => {
+ try imports.append(cmd.getDylibPathName());
+ },
+ else => {},
+ }
+
+ try dumpLoadCommand(cmd, i, writer);
+ try writer.writeByte('\n');
+
+ i += 1;
+ }
+
+ if (opts.dump_symtab) {
+ try writer.print("{s}\n", .{symtab_label});
+ for (symtab) |sym| {
+ if (sym.stab()) continue;
+ const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
+ if (sym.sect()) {
+ const sect = sections.items[sym.n_sect - 1];
+ try writer.print("{x} ({s},{s})", .{
+ sym.n_value,
+ sect.segName(),
+ sect.sectName(),
+ });
+ if (sym.ext()) {
+ try writer.writeAll(" external");
+ }
+ try writer.print(" {s}\n", .{sym_name});
+ } else if (sym.undf()) {
+ const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
+ const import_name = blk: {
+ if (ordinal <= 0) {
+ if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
+ break :blk "self import";
+ if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
+ break :blk "main executable";
+ if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
+ break :blk "flat lookup";
+ unreachable;
+ }
+ const full_path = imports.items[@bitCast(u16, ordinal) - 1];
+ const basename = fs.path.basename(full_path);
+ assert(basename.len > 0);
+ const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
+ break :blk basename[0..ext];
+ };
+ try writer.writeAll("(undefined)");
+ if (sym.weakRef()) {
+ try writer.writeAll(" weak");
+ }
+ if (sym.ext()) {
+ try writer.writeAll(" external");
+ }
+ try writer.print(" {s} (from {s})\n", .{
+ sym_name,
+ import_name,
+ });
+ } else unreachable;
+ }
+ }
+
+ return output.toOwnedSlice();
+ }
+
+ fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
+ // print header first
+ try writer.print(
+ \\LC {d}
+ \\cmd {s}
+ \\cmdsize {d}
+ , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
+
+ switch (lc.cmd()) {
+ .SEGMENT_64 => {
+ const seg = lc.cast(macho.segment_command_64).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\segname {s}
+ \\vmaddr {x}
+ \\vmsize {x}
+ \\fileoff {x}
+ \\filesz {x}
+ , .{
+ seg.segName(),
+ seg.vmaddr,
+ seg.vmsize,
+ seg.fileoff,
+ seg.filesize,
+ });
+
+ for (lc.getSections()) |sect| {
+ try writer.writeByte('\n');
+ try writer.print(
+ \\sectname {s}
+ \\addr {x}
+ \\size {x}
+ \\offset {x}
+ \\align {x}
+ , .{
+ sect.sectName(),
+ sect.addr,
+ sect.size,
+ sect.offset,
+ sect.@"align",
+ });
+ }
+ },
+
+ .ID_DYLIB,
+ .LOAD_DYLIB,
+ .LOAD_WEAK_DYLIB,
+ .REEXPORT_DYLIB,
+ => {
+ const dylib = lc.cast(macho.dylib_command).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\name {s}
+ \\timestamp {d}
+ \\current version {x}
+ \\compatibility version {x}
+ , .{
+ lc.getDylibPathName(),
+ dylib.dylib.timestamp,
+ dylib.dylib.current_version,
+ dylib.dylib.compatibility_version,
+ });
+ },
+
+ .MAIN => {
+ const main = lc.cast(macho.entry_point_command).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\entryoff {x}
+ \\stacksize {x}
+ , .{ main.entryoff, main.stacksize });
+ },
+
+ .RPATH => {
+ try writer.writeByte('\n');
+ try writer.print(
+ \\path {s}
+ , .{
+ lc.getRpathPathName(),
+ });
+ },
+
+ .UUID => {
+ const uuid = lc.cast(macho.uuid_command).?;
+ try writer.writeByte('\n');
+ try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
+ },
+
+ .DATA_IN_CODE,
+ .FUNCTION_STARTS,
+ .CODE_SIGNATURE,
+ => {
+ const llc = lc.cast(macho.linkedit_data_command).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\dataoff {x}
+ \\datasize {x}
+ , .{ llc.dataoff, llc.datasize });
+ },
+
+ .DYLD_INFO_ONLY => {
+ const dlc = lc.cast(macho.dyld_info_command).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\rebaseoff {x}
+ \\rebasesize {x}
+ \\bindoff {x}
+ \\bindsize {x}
+ \\weakbindoff {x}
+ \\weakbindsize {x}
+ \\lazybindoff {x}
+ \\lazybindsize {x}
+ \\exportoff {x}
+ \\exportsize {x}
+ , .{
+ dlc.rebase_off,
+ dlc.rebase_size,
+ dlc.bind_off,
+ dlc.bind_size,
+ dlc.weak_bind_off,
+ dlc.weak_bind_size,
+ dlc.lazy_bind_off,
+ dlc.lazy_bind_size,
+ dlc.export_off,
+ dlc.export_size,
+ });
+ },
+
+ .SYMTAB => {
+ const slc = lc.cast(macho.symtab_command).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\symoff {x}
+ \\nsyms {x}
+ \\stroff {x}
+ \\strsize {x}
+ , .{
+ slc.symoff,
+ slc.nsyms,
+ slc.stroff,
+ slc.strsize,
+ });
+ },
+
+ .DYSYMTAB => {
+ const dlc = lc.cast(macho.dysymtab_command).?;
+ try writer.writeByte('\n');
+ try writer.print(
+ \\ilocalsym {x}
+ \\nlocalsym {x}
+ \\iextdefsym {x}
+ \\nextdefsym {x}
+ \\iundefsym {x}
+ \\nundefsym {x}
+ \\indirectsymoff {x}
+ \\nindirectsyms {x}
+ , .{
+ dlc.ilocalsym,
+ dlc.nlocalsym,
+ dlc.iextdefsym,
+ dlc.nextdefsym,
+ dlc.iundefsym,
+ dlc.nundefsym,
+ dlc.indirectsymoff,
+ dlc.nindirectsyms,
+ });
+ },
+
+ else => {},
+ }
+ }
+};
+
+const WasmDumper = struct {
+ const symtab_label = "symbols";
+
+ fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
+ const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
+ if (opts.dump_symtab) {
+ @panic("TODO: Implement symbol table parsing and dumping");
+ }
+
+ var fbs = std.io.fixedBufferStream(bytes);
+ const reader = fbs.reader();
+
+ const buf = try reader.readBytesNoEof(8);
+ if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
+ return error.InvalidMagicByte;
+ }
+ if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
+ return error.UnsupportedWasmVersion;
+ }
+
+ var output = std.ArrayList(u8).init(gpa);
+ errdefer output.deinit();
+ const writer = output.writer();
+
+ while (reader.readByte()) |current_byte| {
+ const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
+ std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
+ return err;
+ };
+
+ const section_length = try std.leb.readULEB128(u32, reader);
+ try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
+ fbs.pos += section_length;
+ } else |_| {} // reached end of stream
+
+ return output.toOwnedSlice();
+ }
+
+ fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
+ var fbs = std.io.fixedBufferStream(data);
+ const reader = fbs.reader();
+
+ try writer.print(
+ \\Section {s}
+ \\size {d}
+ , .{ @tagName(section), data.len });
+
+ switch (section) {
+ .type,
+ .import,
+ .function,
+ .table,
+ .memory,
+ .global,
+ .@"export",
+ .element,
+ .code,
+ .data,
+ => {
+ const entries = try std.leb.readULEB128(u32, reader);
+ try writer.print("\nentries {d}\n", .{entries});
+ try dumpSection(section, data[fbs.pos..], entries, writer);
+ },
+ .custom => {
+ const name_length = try std.leb.readULEB128(u32, reader);
+ const name = data[fbs.pos..][0..name_length];
+ fbs.pos += name_length;
+ try writer.print("\nname {s}\n", .{name});
+
+ if (mem.eql(u8, name, "name")) {
+ try parseDumpNames(reader, writer, data);
+ } else if (mem.eql(u8, name, "producers")) {
+ try parseDumpProducers(reader, writer, data);
+ } else if (mem.eql(u8, name, "target_features")) {
+ try parseDumpFeatures(reader, writer, data);
+ }
+ // TODO: Implement parsing and dumping other custom sections (such as relocations)
+ },
+ .start => {
+ const start = try std.leb.readULEB128(u32, reader);
+ try writer.print("\nstart {d}\n", .{start});
+ },
+ else => {}, // skip unknown sections
+ }
+ }
+
+ fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
+ var fbs = std.io.fixedBufferStream(data);
+ const reader = fbs.reader();
+
+ switch (section) {
+ .type => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ const func_type = try reader.readByte();
+ if (func_type != std.wasm.function_type) {
+ std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
+ return error.UnexpectedByte;
+ }
+ const params = try std.leb.readULEB128(u32, reader);
+ try writer.print("params {d}\n", .{params});
+ var index: u32 = 0;
+ while (index < params) : (index += 1) {
+ try parseDumpType(std.wasm.Valtype, reader, writer);
+ } else index = 0;
+ const returns = try std.leb.readULEB128(u32, reader);
+ try writer.print("returns {d}\n", .{returns});
+ while (index < returns) : (index += 1) {
+ try parseDumpType(std.wasm.Valtype, reader, writer);
+ }
+ }
+ },
+ .import => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ const module_name_len = try std.leb.readULEB128(u32, reader);
+ const module_name = data[fbs.pos..][0..module_name_len];
+ fbs.pos += module_name_len;
+ const name_len = try std.leb.readULEB128(u32, reader);
+ const name = data[fbs.pos..][0..name_len];
+ fbs.pos += name_len;
+
+ const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
+ std.debug.print("Invalid import kind\n", .{});
+ return err;
+ };
+
+ try writer.print(
+ \\module {s}
+ \\name {s}
+ \\kind {s}
+ , .{ module_name, name, @tagName(kind) });
+ try writer.writeByte('\n');
+ switch (kind) {
+ .function => {
+ try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
+ },
+ .memory => {
+ try parseDumpLimits(reader, writer);
+ },
+ .global => {
+ try parseDumpType(std.wasm.Valtype, reader, writer);
+ try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
+ },
+ .table => {
+ try parseDumpType(std.wasm.RefType, reader, writer);
+ try parseDumpLimits(reader, writer);
+ },
+ }
+ }
+ },
+ .function => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
+ }
+ },
+ .table => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ try parseDumpType(std.wasm.RefType, reader, writer);
+ try parseDumpLimits(reader, writer);
+ }
+ },
+ .memory => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ try parseDumpLimits(reader, writer);
+ }
+ },
+ .global => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ try parseDumpType(std.wasm.Valtype, reader, writer);
+ try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
+ try parseDumpInit(reader, writer);
+ }
+ },
+ .@"export" => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ const name_len = try std.leb.readULEB128(u32, reader);
+ const name = data[fbs.pos..][0..name_len];
+ fbs.pos += name_len;
+ const kind_byte = try std.leb.readULEB128(u8, reader);
+ const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
+ std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
+ return err;
+ };
+ const index = try std.leb.readULEB128(u32, reader);
+ try writer.print(
+ \\name {s}
+ \\kind {s}
+ \\index {d}
+ , .{ name, @tagName(kind), index });
+ try writer.writeByte('\n');
+ }
+ },
+ .element => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
+ try parseDumpInit(reader, writer);
+
+ const function_indexes = try std.leb.readULEB128(u32, reader);
+ var function_index: u32 = 0;
+ try writer.print("indexes {d}\n", .{function_indexes});
+ while (function_index < function_indexes) : (function_index += 1) {
+ try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
+ }
+ }
+ },
+ .code => {}, // code section is considered opaque to linker
+ .data => {
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ const index = try std.leb.readULEB128(u32, reader);
+ try writer.print("memory index 0x{x}\n", .{index});
+ try parseDumpInit(reader, writer);
+ const size = try std.leb.readULEB128(u32, reader);
+ try writer.print("size {d}\n", .{size});
+ try reader.skipBytes(size, .{}); // we do not care about the content of the segments
+ }
+ },
+ else => unreachable,
+ }
+ }
+
+ fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
+ const type_byte = try reader.readByte();
+ const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
+ std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
+ return err;
+ };
+ try writer.print("type {s}\n", .{@tagName(valtype)});
+ }
+
+ fn parseDumpLimits(reader: anytype, writer: anytype) !void {
+ const flags = try std.leb.readULEB128(u8, reader);
+ const min = try std.leb.readULEB128(u32, reader);
+
+ try writer.print("min {x}\n", .{min});
+ if (flags != 0) {
+ try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
+ }
+ }
+
+ fn parseDumpInit(reader: anytype, writer: anytype) !void {
+ const byte = try std.leb.readULEB128(u8, reader);
+ const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
+ std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
+ return err;
+ };
+ switch (opcode) {
+ .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
+ .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
+ .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
+ .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
+ .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
+ else => unreachable,
+ }
+ const end_opcode = try std.leb.readULEB128(u8, reader);
+ if (end_opcode != std.wasm.opcode(.end)) {
+ std.debug.print("expected 'end' opcode in init expression\n", .{});
+ return error.MissingEndOpcode;
+ }
+ }
+
+ fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
+ while (reader.context.pos < data.len) {
+ try parseDumpType(std.wasm.NameSubsection, reader, writer);
+ const size = try std.leb.readULEB128(u32, reader);
+ const entries = try std.leb.readULEB128(u32, reader);
+ try writer.print(
+ \\size {d}
+ \\names {d}
+ , .{ size, entries });
+ try writer.writeByte('\n');
+ var i: u32 = 0;
+ while (i < entries) : (i += 1) {
+ const index = try std.leb.readULEB128(u32, reader);
+ const name_len = try std.leb.readULEB128(u32, reader);
+ const pos = reader.context.pos;
+ const name = data[pos..][0..name_len];
+ reader.context.pos += name_len;
+
+ try writer.print(
+ \\index {d}
+ \\name {s}
+ , .{ index, name });
+ try writer.writeByte('\n');
+ }
+ }
+ }
+
+ fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
+ const field_count = try std.leb.readULEB128(u32, reader);
+ try writer.print("fields {d}\n", .{field_count});
+ var current_field: u32 = 0;
+ while (current_field < field_count) : (current_field += 1) {
+ const field_name_length = try std.leb.readULEB128(u32, reader);
+ const field_name = data[reader.context.pos..][0..field_name_length];
+ reader.context.pos += field_name_length;
+
+ const value_count = try std.leb.readULEB128(u32, reader);
+ try writer.print(
+ \\field_name {s}
+ \\values {d}
+ , .{ field_name, value_count });
+ try writer.writeByte('\n');
+ var current_value: u32 = 0;
+ while (current_value < value_count) : (current_value += 1) {
+ const value_length = try std.leb.readULEB128(u32, reader);
+ const value = data[reader.context.pos..][0..value_length];
+ reader.context.pos += value_length;
+
+ const version_length = try std.leb.readULEB128(u32, reader);
+ const version = data[reader.context.pos..][0..version_length];
+ reader.context.pos += version_length;
+
+ try writer.print(
+ \\value_name {s}
+ \\version {s}
+ , .{ value, version });
+ try writer.writeByte('\n');
+ }
+ }
+ }
+
+ fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
+ const feature_count = try std.leb.readULEB128(u32, reader);
+ try writer.print("features {d}\n", .{feature_count});
+
+ var index: u32 = 0;
+ while (index < feature_count) : (index += 1) {
+ const prefix_byte = try std.leb.readULEB128(u8, reader);
+ const name_length = try std.leb.readULEB128(u32, reader);
+ const feature_name = data[reader.context.pos..][0..name_length];
+ reader.context.pos += name_length;
+
+ try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
+ }
+ }
+};
diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig
new file mode 100644
index 0000000000..b961227c9c
--- /dev/null
+++ b/lib/std/Build/ConfigHeaderStep.zig
@@ -0,0 +1,287 @@
+const std = @import("../std.zig");
+const ConfigHeaderStep = @This();
+const Step = std.Build.Step;
+
+pub const base_id: Step.Id = .config_header;
+
+pub const Style = enum {
+ /// The configure format supported by autotools. It uses `#undef foo` to
+ /// mark lines that can be substituted with different values.
+ autoconf,
+ /// The configure format supported by CMake. It uses `@@FOO@@` and
+ /// `#cmakedefine` for template substitution.
+ cmake,
+};
+
+pub const Value = union(enum) {
+ undef,
+ defined,
+ boolean: bool,
+ int: i64,
+ ident: []const u8,
+ string: []const u8,
+};
+
+step: Step,
+builder: *std.Build,
+source: std.Build.FileSource,
+style: Style,
+values: std.StringHashMap(Value),
+max_bytes: usize = 2 * 1024 * 1024,
+output_dir: []const u8,
+output_basename: []const u8,
+
+pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep {
+ const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
+ const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
+ self.* = .{
+ .builder = builder,
+ .step = Step.init(base_id, name, builder.allocator, make),
+ .source = source,
+ .style = style,
+ .values = std.StringHashMap(Value).init(builder.allocator),
+ .output_dir = undefined,
+ .output_basename = "config.h",
+ };
+ switch (source) {
+ .path => |p| {
+ const basename = std.fs.path.basename(p);
+ if (std.mem.endsWith(u8, basename, ".h.in")) {
+ self.output_basename = basename[0 .. basename.len - 3];
+ }
+ },
+ else => {},
+ }
+ return self;
+}
+
+pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
+ return addValuesInner(self, values) catch @panic("OOM");
+}
+
+fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
+ inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
+ switch (@typeInfo(field.type)) {
+ .Null => {
+ try self.values.put(field.name, .undef);
+ },
+ .Void => {
+ try self.values.put(field.name, .defined);
+ },
+ .Bool => {
+ try self.values.put(field.name, .{ .boolean = @field(values, field.name) });
+ },
+ .ComptimeInt => {
+ try self.values.put(field.name, .{ .int = @field(values, field.name) });
+ },
+ .EnumLiteral => {
+ try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) });
+ },
+ .Pointer => |ptr| {
+ switch (@typeInfo(ptr.child)) {
+ .Array => |array| {
+ if (ptr.size == .One and array.child == u8) {
+ try self.values.put(field.name, .{ .string = @field(values, field.name) });
+ continue;
+ }
+ },
+ else => {},
+ }
+
+ @compileError("unsupported ConfigHeaderStep value type: " ++
+ @typeName(field.type));
+ },
+ else => @compileError("unsupported ConfigHeaderStep value type: " ++
+ @typeName(field.type)),
+ }
+ }
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
+ const gpa = self.builder.allocator;
+ const src_path = self.source.getPath(self.builder);
+ const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
+
+ // The cache is used here not really as a way to speed things up - because writing
+ // the data to a file would probably be very fast - but as a way to find a canonical
+ // location to put build artifacts.
+
+ // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
+ // files, then two ConfigHeaderStep executing in parallel might clobber each other.
+
+ // TODO port the cache system from the compiler to zig std lib. Until then
+ // we construct the path directly, and no "cache hit" detection happens;
+ // the files are always written.
+ // Note there is very similar code over in WriteFileStep
+ const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
+ // Random bytes to make ConfigHeaderStep unique. Refresh this with new
+ // random bytes when ConfigHeaderStep implementation is modified in a
+ // non-backwards-compatible way.
+ var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
+ hash.update(self.source.getDisplayName());
+ hash.update(contents);
+
+ var digest: [16]u8 = undefined;
+ hash.final(&digest);
+ var hash_basename: [digest.len * 2]u8 = undefined;
+ _ = std.fmt.bufPrint(
+ &hash_basename,
+ "{s}",
+ .{std.fmt.fmtSliceHexLower(&digest)},
+ ) catch unreachable;
+
+ self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
+ self.builder.cache_root, "o", &hash_basename,
+ });
+ var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
+ std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
+ return err;
+ };
+ defer dir.close();
+
+ var values_copy = try self.values.clone();
+ defer values_copy.deinit();
+
+ var output = std.ArrayList(u8).init(gpa);
+ defer output.deinit();
+ try output.ensureTotalCapacity(contents.len);
+
+ try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
+
+ switch (self.style) {
+ .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
+ .cmake => try render_cmake(contents, &output, &values_copy, src_path),
+ }
+
+ try dir.writeFile(self.output_basename, output.items);
+}
+
+fn render_autoconf(
+ contents: []const u8,
+ output: *std.ArrayList(u8),
+ values_copy: *std.StringHashMap(Value),
+ src_path: []const u8,
+) !void {
+ var any_errors = false;
+ var line_index: u32 = 0;
+ var line_it = std.mem.split(u8, contents, "\n");
+ while (line_it.next()) |line| : (line_index += 1) {
+ if (!std.mem.startsWith(u8, line, "#")) {
+ try output.appendSlice(line);
+ try output.appendSlice("\n");
+ continue;
+ }
+ var it = std.mem.tokenize(u8, line[1..], " \t\r");
+ const undef = it.next().?;
+ if (!std.mem.eql(u8, undef, "undef")) {
+ try output.appendSlice(line);
+ try output.appendSlice("\n");
+ continue;
+ }
+ const name = it.rest();
+ const kv = values_copy.fetchRemove(name) orelse {
+ std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
+ src_path, line_index + 1, name,
+ });
+ any_errors = true;
+ continue;
+ };
+ try renderValue(output, name, kv.value);
+ }
+
+ {
+ var it = values_copy.iterator();
+ while (it.next()) |entry| {
+ const name = entry.key_ptr.*;
+ std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
+ }
+ }
+
+ if (any_errors) {
+ return error.HeaderConfigFailed;
+ }
+}
+
+fn render_cmake(
+ contents: []const u8,
+ output: *std.ArrayList(u8),
+ values_copy: *std.StringHashMap(Value),
+ src_path: []const u8,
+) !void {
+ var any_errors = false;
+ var line_index: u32 = 0;
+ var line_it = std.mem.split(u8, contents, "\n");
+ while (line_it.next()) |line| : (line_index += 1) {
+ if (!std.mem.startsWith(u8, line, "#")) {
+ try output.appendSlice(line);
+ try output.appendSlice("\n");
+ continue;
+ }
+ var it = std.mem.tokenize(u8, line[1..], " \t\r");
+ const cmakedefine = it.next().?;
+ if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
+ try output.appendSlice(line);
+ try output.appendSlice("\n");
+ continue;
+ }
+ const name = it.next() orelse {
+ std.debug.print("{s}:{d}: error: missing define name\n", .{
+ src_path, line_index + 1,
+ });
+ any_errors = true;
+ continue;
+ };
+ const kv = values_copy.fetchRemove(name) orelse {
+ std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
+ src_path, line_index + 1, name,
+ });
+ any_errors = true;
+ continue;
+ };
+ try renderValue(output, name, kv.value);
+ }
+
+ {
+ var it = values_copy.iterator();
+ while (it.next()) |entry| {
+ const name = entry.key_ptr.*;
+ std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
+ }
+ }
+
+ if (any_errors) {
+ return error.HeaderConfigFailed;
+ }
+}
+
+fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
+ switch (value) {
+ .undef => {
+ try output.appendSlice("/* #undef ");
+ try output.appendSlice(name);
+ try output.appendSlice(" */\n");
+ },
+ .defined => {
+ try output.appendSlice("#define ");
+ try output.appendSlice(name);
+ try output.appendSlice("\n");
+ },
+ .boolean => |b| {
+ try output.appendSlice("#define ");
+ try output.appendSlice(name);
+ try output.appendSlice(" ");
+ try output.appendSlice(if (b) "true\n" else "false\n");
+ },
+ .int => |i| {
+ try output.writer().print("#define {s} {d}\n", .{ name, i });
+ },
+ .ident => |ident| {
+ try output.writer().print("#define {s} {s}\n", .{ name, ident });
+ },
+ .string => |string| {
+ // TODO: use C-specific escaping instead of zig string literals
+ try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
+ },
+ }
+}
diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig
new file mode 100644
index 0000000000..b7b12d791f
--- /dev/null
+++ b/lib/std/Build/EmulatableRunStep.zig
@@ -0,0 +1,213 @@
+//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
+//! When a binary is foreign, but emulation for the target is disabled, the specified binary
+//! will not be run and therefore also not validated against its output.
+//! This step can be useful when wishing to run a built binary on multiple platforms,
+//! without having to verify if it's possible to be ran against.
+
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const LibExeObjStep = std.Build.LibExeObjStep;
+const RunStep = std.Build.RunStep;
+
+const fs = std.fs;
+const process = std.process;
+const EnvMap = process.EnvMap;
+
+const EmulatableRunStep = @This();
+
+pub const base_id = .emulatable_run;
+
+const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
+
+step: Step,
+builder: *std.Build,
+
+/// The artifact (executable) to be run by this step
+exe: *LibExeObjStep,
+
+/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
+expected_exit_code: ?u8 = 0,
+
+/// Override this field to modify the environment
+env_map: ?*EnvMap,
+
+/// Set this to modify the current working directory
+cwd: ?[]const u8,
+
+stdout_action: RunStep.StdIoAction = .inherit,
+stderr_action: RunStep.StdIoAction = .inherit,
+
+/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
+/// or through emulation.
+hide_foreign_binaries_warning: bool,
+
+/// Creates a step that will execute the given artifact. This step will allow running the
+/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
+/// When set to false, and the binary is foreign, running the executable is skipped.
+/// Asserts given artifact is an executable.
+pub fn create(builder: *std.Build, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep {
+ std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
+ const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
+
+ const option_name = "hide-foreign-warnings";
+ const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
+ break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
+ } else false;
+
+ self.* = .{
+ .builder = builder,
+ .step = Step.init(.emulatable_run, name, builder.allocator, make),
+ .exe = artifact,
+ .env_map = null,
+ .cwd = null,
+ .hide_foreign_binaries_warning = hide_warnings,
+ };
+ self.step.dependOn(&artifact.step);
+
+ return self;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(EmulatableRunStep, "step", step);
+ const host_info = self.builder.host;
+
+ var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
+ defer argv_list.deinit();
+
+ const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
+ switch (host_info.getExternalExecutor(self.exe.target_info, .{
+ .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
+ .link_libc = self.exe.is_linking_libc,
+ })) {
+ .native => {},
+ .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
+ .wine => |bin_name| if (self.builder.enable_wine) {
+ try argv_list.append(bin_name);
+ } else return,
+ .qemu => |bin_name| if (self.builder.enable_qemu) {
+ const glibc_dir_arg = if (need_cross_glibc)
+ self.builder.glibc_runtimes_dir orelse return
+ else
+ null;
+ try argv_list.append(bin_name);
+ if (glibc_dir_arg) |dir| {
+ // TODO look into making this a call to `linuxTriple`. This
+ // needs the directory to be called "i686" rather than
+ // "x86" which is why we do it manually here.
+ const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
+ const cpu_arch = self.exe.target.getCpuArch();
+ const os_tag = self.exe.target.getOsTag();
+ const abi = self.exe.target.getAbi();
+ const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
+ "i686"
+ else
+ @tagName(cpu_arch);
+ const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
+ dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
+ });
+
+ try argv_list.append("-L");
+ try argv_list.append(full_dir);
+ }
+ } else return warnAboutForeignBinaries(self),
+ .darling => |bin_name| if (self.builder.enable_darling) {
+ try argv_list.append(bin_name);
+ } else return warnAboutForeignBinaries(self),
+ .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
+ try argv_list.append(bin_name);
+ try argv_list.append("--dir=.");
+ } else return warnAboutForeignBinaries(self),
+ else => return warnAboutForeignBinaries(self),
+ }
+
+ if (self.exe.target.isWindows()) {
+ // On Windows we don't have rpaths so we have to add .dll search paths to PATH
+ RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
+ }
+
+ const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
+ try argv_list.append(executable_path);
+
+ try RunStep.runCommand(
+ argv_list.items,
+ self.builder,
+ self.expected_exit_code,
+ self.stdout_action,
+ self.stderr_action,
+ .Inherit,
+ self.env_map,
+ self.cwd,
+ false,
+ );
+}
+
+pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
+ self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
+}
+
+pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
+ self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
+}
+
+fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
+ if (step.hide_foreign_binaries_warning) return;
+ const builder = step.builder;
+ const artifact = step.exe;
+
+ const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
+ const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
+ const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
+ const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
+ switch (builder.host.getExternalExecutor(target_info, .{
+ .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
+ .link_libc = artifact.is_linking_libc,
+ })) {
+ .native => unreachable,
+ .bad_dl => |foreign_dl| {
+ const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
+ 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", .{
+ host_dl, foreign_dl, host_dl,
+ });
+ },
+ .bad_os_or_cpu => {
+ std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
+ host_name, foreign_name,
+ });
+ },
+ .darling => if (!builder.enable_darling) {
+ std.debug.print(
+ "the host system ({s}) does not appear to be capable of executing binaries " ++
+ "from the target ({s}). Consider enabling darling.\n",
+ .{ host_name, foreign_name },
+ );
+ },
+ .rosetta => if (!builder.enable_rosetta) {
+ std.debug.print(
+ "the host system ({s}) does not appear to be capable of executing binaries " ++
+ "from the target ({s}). Consider enabling rosetta.\n",
+ .{ host_name, foreign_name },
+ );
+ },
+ .wine => if (!builder.enable_wine) {
+ std.debug.print(
+ "the host system ({s}) does not appear to be capable of executing binaries " ++
+ "from the target ({s}). Consider enabling wine.\n",
+ .{ host_name, foreign_name },
+ );
+ },
+ .qemu => if (!builder.enable_qemu) {
+ std.debug.print(
+ "the host system ({s}) does not appear to be capable of executing binaries " ++
+ "from the target ({s}). Consider enabling qemu.\n",
+ .{ host_name, foreign_name },
+ );
+ },
+ .wasmtime => {
+ std.debug.print(
+ "the host system ({s}) does not appear to be capable of executing binaries " ++
+ "from the target ({s}). Consider enabling wasmtime.\n",
+ .{ host_name, foreign_name },
+ );
+ },
+ }
+}
diff --git a/lib/std/Build/FmtStep.zig b/lib/std/Build/FmtStep.zig
new file mode 100644
index 0000000000..44a93dee66
--- /dev/null
+++ b/lib/std/Build/FmtStep.zig
@@ -0,0 +1,32 @@
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const FmtStep = @This();
+
+pub const base_id = .fmt;
+
+step: Step,
+builder: *std.Build,
+argv: [][]const u8,
+
+pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {
+ const self = builder.allocator.create(FmtStep) catch unreachable;
+ const name = "zig fmt";
+ self.* = FmtStep{
+ .step = Step.init(.fmt, name, builder.allocator, make),
+ .builder = builder,
+ .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable,
+ };
+
+ self.argv[0] = builder.zig_exe;
+ self.argv[1] = "fmt";
+ for (paths) |path, i| {
+ self.argv[2 + i] = builder.pathFromRoot(path);
+ }
+ return self;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(FmtStep, "step", step);
+
+ return self.builder.spawnChild(self.argv);
+}
diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig
new file mode 100644
index 0000000000..929b30e935
--- /dev/null
+++ b/lib/std/Build/InstallArtifactStep.zig
@@ -0,0 +1,85 @@
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const LibExeObjStep = std.Build.LibExeObjStep;
+const InstallDir = std.Build.InstallDir;
+const InstallArtifactStep = @This();
+
+pub const base_id = .install_artifact;
+
+step: Step,
+builder: *std.Build,
+artifact: *LibExeObjStep,
+dest_dir: InstallDir,
+pdb_dir: ?InstallDir,
+h_dir: ?InstallDir,
+
+pub fn create(builder: *std.Build, artifact: *LibExeObjStep) *InstallArtifactStep {
+ if (artifact.install_step) |s| return s;
+
+ const self = builder.allocator.create(InstallArtifactStep) catch unreachable;
+ self.* = InstallArtifactStep{
+ .builder = builder,
+ .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
+ .artifact = artifact,
+ .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
+ .obj => @panic("Cannot install a .obj build artifact."),
+ .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
+ .exe, .test_exe => InstallDir{ .bin = {} },
+ .lib => InstallDir{ .lib = {} },
+ },
+ .pdb_dir = if (artifact.producesPdbFile()) blk: {
+ if (artifact.kind == .exe or artifact.kind == .test_exe) {
+ break :blk InstallDir{ .bin = {} };
+ } else {
+ break :blk InstallDir{ .lib = {} };
+ }
+ } else null,
+ .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
+ };
+ self.step.dependOn(&artifact.step);
+ artifact.install_step = self;
+
+ builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
+ if (self.artifact.isDynamicLibrary()) {
+ if (artifact.major_only_filename) |name| {
+ builder.pushInstalledFile(.lib, name);
+ }
+ if (artifact.name_only_filename) |name| {
+ builder.pushInstalledFile(.lib, name);
+ }
+ if (self.artifact.target.isWindows()) {
+ builder.pushInstalledFile(.lib, artifact.out_lib_filename);
+ }
+ }
+ if (self.pdb_dir) |pdb_dir| {
+ builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
+ }
+ if (self.h_dir) |h_dir| {
+ builder.pushInstalledFile(h_dir, artifact.out_h_filename);
+ }
+ return self;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(InstallArtifactStep, "step", step);
+ const builder = self.builder;
+
+ const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
+ try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
+ if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
+ try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
+ }
+ if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
+ const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
+ try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
+ }
+ if (self.pdb_dir) |pdb_dir| {
+ const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
+ try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
+ }
+ if (self.h_dir) |h_dir| {
+ const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
+ try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
+ }
+ self.artifact.installed_path = full_dest_path;
+}
diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig
new file mode 100644
index 0000000000..41dbb3e35a
--- /dev/null
+++ b/lib/std/Build/InstallDirStep.zig
@@ -0,0 +1,93 @@
+const std = @import("../std.zig");
+const mem = std.mem;
+const fs = std.fs;
+const Step = std.Build.Step;
+const InstallDir = std.Build.InstallDir;
+const InstallDirStep = @This();
+const log = std.log;
+
+step: Step,
+builder: *std.Build,
+options: Options,
+/// This is used by the build system when a file being installed comes from one
+/// package but is being installed by another.
+override_source_builder: ?*std.Build = null,
+
+pub const base_id = .install_dir;
+
+pub const Options = struct {
+ source_dir: []const u8,
+ install_dir: InstallDir,
+ install_subdir: []const u8,
+ /// File paths which end in any of these suffixes will be excluded
+ /// from being installed.
+ exclude_extensions: []const []const u8 = &.{},
+ /// File paths which end in any of these suffixes will result in
+ /// empty files being installed. This is mainly intended for large
+ /// test.zig files in order to prevent needless installation bloat.
+ /// However if the files were not present at all, then
+ /// `@import("test.zig")` would be a compile error.
+ blank_extensions: []const []const u8 = &.{},
+
+ fn dupe(self: Options, b: *std.Build) Options {
+ return .{
+ .source_dir = b.dupe(self.source_dir),
+ .install_dir = self.install_dir.dupe(b),
+ .install_subdir = b.dupe(self.install_subdir),
+ .exclude_extensions = b.dupeStrings(self.exclude_extensions),
+ .blank_extensions = b.dupeStrings(self.blank_extensions),
+ };
+ }
+};
+
+pub fn init(
+ builder: *std.Build,
+ options: Options,
+) InstallDirStep {
+ builder.pushInstalledFile(options.install_dir, options.install_subdir);
+ return InstallDirStep{
+ .builder = builder,
+ .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
+ .options = options.dupe(builder),
+ };
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(InstallDirStep, "step", step);
+ const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
+ const src_builder = self.override_source_builder orelse self.builder;
+ const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
+ var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
+ log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
+ full_src_dir, @errorName(err),
+ });
+ return error.StepFailed;
+ };
+ defer src_dir.close();
+ var it = try src_dir.walk(self.builder.allocator);
+ next_entry: while (try it.next()) |entry| {
+ for (self.options.exclude_extensions) |ext| {
+ if (mem.endsWith(u8, entry.path, ext)) {
+ continue :next_entry;
+ }
+ }
+
+ const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
+ const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
+
+ switch (entry.kind) {
+ .Directory => try fs.cwd().makePath(dest_path),
+ .File => {
+ for (self.options.blank_extensions) |ext| {
+ if (mem.endsWith(u8, entry.path, ext)) {
+ try self.builder.truncateFile(dest_path);
+ continue :next_entry;
+ }
+ }
+
+ try self.builder.updateFile(full_path, dest_path);
+ },
+ else => continue,
+ }
+ }
+}
diff --git a/lib/std/Build/InstallFileStep.zig b/lib/std/Build/InstallFileStep.zig
new file mode 100644
index 0000000000..8c8d8ad2d4
--- /dev/null
+++ b/lib/std/Build/InstallFileStep.zig
@@ -0,0 +1,40 @@
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const FileSource = std.Build.FileSource;
+const InstallDir = std.Build.InstallDir;
+const InstallFileStep = @This();
+
+pub const base_id = .install_file;
+
+step: Step,
+builder: *std.Build,
+source: FileSource,
+dir: InstallDir,
+dest_rel_path: []const u8,
+/// This is used by the build system when a file being installed comes from one
+/// package but is being installed by another.
+override_source_builder: ?*std.Build = null,
+
+pub fn init(
+ builder: *std.Build,
+ source: FileSource,
+ dir: InstallDir,
+ dest_rel_path: []const u8,
+) InstallFileStep {
+ builder.pushInstalledFile(dir, dest_rel_path);
+ return InstallFileStep{
+ .builder = builder,
+ .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
+ .source = source.dupe(builder),
+ .dir = dir.dupe(builder),
+ .dest_rel_path = builder.dupePath(dest_rel_path),
+ };
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(InstallFileStep, "step", step);
+ const src_builder = self.override_source_builder orelse self.builder;
+ const full_src_path = self.source.getPath(src_builder);
+ const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
+ try self.builder.updateFile(full_src_path, full_dest_path);
+}
diff --git a/lib/std/Build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig
new file mode 100644
index 0000000000..08d646ff88
--- /dev/null
+++ b/lib/std/Build/InstallRawStep.zig
@@ -0,0 +1,110 @@
+//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
+//! subcommand rather than containing an implementation directly.
+
+const std = @import("std");
+const InstallRawStep = @This();
+
+const Allocator = std.mem.Allocator;
+const ArenaAllocator = std.heap.ArenaAllocator;
+const ArrayListUnmanaged = std.ArrayListUnmanaged;
+const File = std.fs.File;
+const InstallDir = std.Build.InstallDir;
+const LibExeObjStep = std.Build.LibExeObjStep;
+const Step = std.Build.Step;
+const elf = std.elf;
+const fs = std.fs;
+const io = std.io;
+const sort = std.sort;
+
+pub const base_id = .install_raw;
+
+pub const RawFormat = enum {
+ bin,
+ hex,
+};
+
+step: Step,
+builder: *std.Build,
+artifact: *LibExeObjStep,
+dest_dir: InstallDir,
+dest_filename: []const u8,
+options: CreateOptions,
+output_file: std.Build.GeneratedFile,
+
+pub const CreateOptions = struct {
+ format: ?RawFormat = null,
+ dest_dir: ?InstallDir = null,
+ only_section: ?[]const u8 = null,
+ pad_to: ?u64 = null,
+};
+
+pub fn create(
+ builder: *std.Build,
+ artifact: *LibExeObjStep,
+ dest_filename: []const u8,
+ options: CreateOptions,
+) *InstallRawStep {
+ const self = builder.allocator.create(InstallRawStep) catch unreachable;
+ self.* = InstallRawStep{
+ .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
+ .builder = builder,
+ .artifact = artifact,
+ .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
+ .obj => unreachable,
+ .@"test" => unreachable,
+ .exe, .test_exe => .bin,
+ .lib => unreachable,
+ },
+ .dest_filename = dest_filename,
+ .options = options,
+ .output_file = std.Build.GeneratedFile{ .step = &self.step },
+ };
+ self.step.dependOn(&artifact.step);
+
+ builder.pushInstalledFile(self.dest_dir, dest_filename);
+ return self;
+}
+
+pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource {
+ return std.Build.FileSource{ .generated = &self.output_file };
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(InstallRawStep, "step", step);
+ const b = self.builder;
+
+ if (self.artifact.target.getObjectFormat() != .elf) {
+ std.debug.print("InstallRawStep only works with ELF format.\n", .{});
+ return error.InvalidObjectFormat;
+ }
+
+ const full_src_path = self.artifact.getOutputSource().getPath(b);
+ const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
+ self.output_file.path = full_dest_path;
+
+ fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable;
+
+ var argv_list = std.ArrayList([]const u8).init(b.allocator);
+ try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
+
+ if (self.options.only_section) |only_section| {
+ try argv_list.appendSlice(&.{ "-j", only_section });
+ }
+ if (self.options.pad_to) |pad_to| {
+ try argv_list.appendSlice(&.{
+ "--pad-to",
+ b.fmt("{d}", .{pad_to}),
+ });
+ }
+ if (self.options.format) |format| switch (format) {
+ .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
+ .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
+ };
+
+ try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
+ _ = try self.builder.execFromStep(argv_list.items, &self.step);
+}
+
+test {
+ std.testing.refAllDecls(InstallRawStep);
+}
diff --git a/lib/std/Build/LibExeObjStep.zig b/lib/std/Build/LibExeObjStep.zig
new file mode 100644
index 0000000000..af9d34440d
--- /dev/null
+++ b/lib/std/Build/LibExeObjStep.zig
@@ -0,0 +1,2045 @@
+const builtin = @import("builtin");
+const std = @import("../std.zig");
+const mem = std.mem;
+const log = std.log;
+const fs = std.fs;
+const assert = std.debug.assert;
+const panic = std.debug.panic;
+const ArrayList = std.ArrayList;
+const StringHashMap = std.StringHashMap;
+const Sha256 = std.crypto.hash.sha2.Sha256;
+const Allocator = mem.Allocator;
+const Step = std.Build.Step;
+const CrossTarget = std.zig.CrossTarget;
+const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+const FileSource = std.Build.FileSource;
+const PkgConfigPkg = std.Build.PkgConfigPkg;
+const PkgConfigError = std.Build.PkgConfigError;
+const ExecError = std.Build.ExecError;
+const Pkg = std.Build.Pkg;
+const VcpkgRoot = std.Build.VcpkgRoot;
+const InstallDir = std.Build.InstallDir;
+const InstallArtifactStep = std.Build.InstallArtifactStep;
+const GeneratedFile = std.Build.GeneratedFile;
+const InstallRawStep = std.Build.InstallRawStep;
+const EmulatableRunStep = std.Build.EmulatableRunStep;
+const CheckObjectStep = std.Build.CheckObjectStep;
+const RunStep = std.Build.RunStep;
+const OptionsStep = std.Build.OptionsStep;
+const ConfigHeaderStep = std.Build.ConfigHeaderStep;
+const LibExeObjStep = @This();
+
+pub const base_id = .lib_exe_obj;
+
+step: Step,
+builder: *std.Build,
+name: []const u8,
+target: CrossTarget,
+target_info: NativeTargetInfo,
+optimize: std.builtin.Mode,
+linker_script: ?FileSource = null,
+version_script: ?[]const u8 = null,
+out_filename: []const u8,
+linkage: ?Linkage = null,
+version: ?std.builtin.Version,
+kind: Kind,
+major_only_filename: ?[]const u8,
+name_only_filename: ?[]const u8,
+strip: ?bool,
+unwind_tables: ?bool,
+// keep in sync with src/link.zig:CompressDebugSections
+compress_debug_sections: enum { none, zlib } = .none,
+lib_paths: ArrayList([]const u8),
+rpaths: ArrayList([]const u8),
+framework_dirs: ArrayList([]const u8),
+frameworks: StringHashMap(FrameworkLinkInfo),
+verbose_link: bool,
+verbose_cc: bool,
+emit_analysis: EmitOption = .default,
+emit_asm: EmitOption = .default,
+emit_bin: EmitOption = .default,
+emit_docs: EmitOption = .default,
+emit_implib: EmitOption = .default,
+emit_llvm_bc: EmitOption = .default,
+emit_llvm_ir: EmitOption = .default,
+// Lots of things depend on emit_h having a consistent path,
+// so it is not an EmitOption for now.
+emit_h: bool = false,
+bundle_compiler_rt: ?bool = null,
+single_threaded: ?bool = null,
+stack_protector: ?bool = null,
+disable_stack_probing: bool,
+disable_sanitize_c: bool,
+sanitize_thread: bool,
+rdynamic: bool,
+import_memory: bool = false,
+/// For WebAssembly targets, this will allow for undefined symbols to
+/// be imported from the host environment.
+import_symbols: bool = false,
+import_table: bool = false,
+export_table: bool = false,
+initial_memory: ?u64 = null,
+max_memory: ?u64 = null,
+shared_memory: bool = false,
+global_base: ?u64 = null,
+c_std: std.Build.CStd,
+override_lib_dir: ?[]const u8,
+main_pkg_path: ?[]const u8,
+exec_cmd_args: ?[]const ?[]const u8,
+name_prefix: []const u8,
+filter: ?[]const u8,
+test_evented_io: bool = false,
+test_runner: ?[]const u8,
+code_model: std.builtin.CodeModel = .default,
+wasi_exec_model: ?std.builtin.WasiExecModel = null,
+/// Symbols to be exported when compiling to wasm
+export_symbol_names: []const []const u8 = &.{},
+
+root_src: ?FileSource,
+out_h_filename: []const u8,
+out_lib_filename: []const u8,
+out_pdb_filename: []const u8,
+packages: ArrayList(Pkg),
+
+object_src: []const u8,
+
+link_objects: ArrayList(LinkObject),
+include_dirs: ArrayList(IncludeDir),
+c_macros: ArrayList([]const u8),
+installed_headers: ArrayList(*Step),
+output_dir: ?[]const u8,
+is_linking_libc: bool = false,
+is_linking_libcpp: bool = false,
+vcpkg_bin_path: ?[]const u8 = null,
+
+/// This may be set in order to override the default install directory
+override_dest_dir: ?InstallDir,
+installed_path: ?[]const u8,
+install_step: ?*InstallArtifactStep,
+
+/// Base address for an executable image.
+image_base: ?u64 = null,
+
+libc_file: ?FileSource = null,
+
+valgrind_support: ?bool = null,
+each_lib_rpath: ?bool = null,
+/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
+/// which can be used to coordinate a stripped binary with its debug symbols.
+/// As an example, the bloaty project refuses to work unless its inputs have
+/// build ids, in order to prevent accidental mismatches.
+/// The default is to not include this section because it slows down linking.
+build_id: ?bool = null,
+
+/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
+/// file.
+link_eh_frame_hdr: bool = false,
+link_emit_relocs: bool = false,
+
+/// Place every function in its own section so that unused ones may be
+/// safely garbage-collected during the linking phase.
+link_function_sections: bool = false,
+
+/// Remove functions and data that are unreachable by the entry point or
+/// exported symbols.
+link_gc_sections: ?bool = null,
+
+linker_allow_shlib_undefined: ?bool = null,
+
+/// Permit read-only relocations in read-only segments. Disallowed by default.
+link_z_notext: bool = false,
+
+/// Force all relocations to be read-only after processing.
+link_z_relro: bool = true,
+
+/// Allow relocations to be lazily processed after load.
+link_z_lazy: bool = false,
+
+/// Common page size
+link_z_common_page_size: ?u64 = null,
+
+/// Maximum page size
+link_z_max_page_size: ?u64 = null,
+
+/// (Darwin) Install name for the dylib
+install_name: ?[]const u8 = null,
+
+/// (Darwin) Path to entitlements file
+entitlements: ?[]const u8 = null,
+
+/// (Darwin) Size of the pagezero segment.
+pagezero_size: ?u64 = null,
+
+/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
+/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
+/// option.
+/// By default, if no option is specified, the linker assumes `paths_first` as the default
+/// search strategy.
+search_strategy: ?enum { paths_first, dylibs_first } = null,
+
+/// (Darwin) Set size of the padding between the end of load commands
+/// and start of `__TEXT,__text` section.
+headerpad_size: ?u32 = null,
+
+/// (Darwin) Automatically Set size of the padding between the end of load commands
+/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
+headerpad_max_install_names: bool = false,
+
+/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
+dead_strip_dylibs: bool = false,
+
+/// Position Independent Code
+force_pic: ?bool = null,
+
+/// Position Independent Executable
+pie: ?bool = null,
+
+red_zone: ?bool = null,
+
+omit_frame_pointer: ?bool = null,
+dll_export_fns: ?bool = null,
+
+subsystem: ?std.Target.SubSystem = null,
+
+entry_symbol_name: ?[]const u8 = null,
+
+/// Overrides the default stack size
+stack_size: ?u64 = null,
+
+want_lto: ?bool = null,
+use_llvm: ?bool = null,
+use_lld: ?bool = null,
+
+output_path_source: GeneratedFile,
+output_lib_path_source: GeneratedFile,
+output_h_path_source: GeneratedFile,
+output_pdb_path_source: GeneratedFile,
+
+pub const CSourceFiles = struct {
+ files: []const []const u8,
+ flags: []const []const u8,
+};
+
+pub const CSourceFile = struct {
+ source: FileSource,
+ args: []const []const u8,
+
+ pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile {
+ return .{
+ .source = self.source.dupe(b),
+ .args = b.dupeStrings(self.args),
+ };
+ }
+};
+
+pub const LinkObject = union(enum) {
+ static_path: FileSource,
+ other_step: *LibExeObjStep,
+ system_lib: SystemLib,
+ assembly_file: FileSource,
+ c_source_file: *CSourceFile,
+ c_source_files: *CSourceFiles,
+};
+
+pub const SystemLib = struct {
+ name: []const u8,
+ needed: bool,
+ weak: bool,
+ use_pkg_config: enum {
+ /// Don't use pkg-config, just pass -lfoo where foo is name.
+ no,
+ /// Try to get information on how to link the library from pkg-config.
+ /// If that fails, fall back to passing -lfoo where foo is name.
+ yes,
+ /// Try to get information on how to link the library from pkg-config.
+ /// If that fails, error out.
+ force,
+ },
+};
+
+const FrameworkLinkInfo = struct {
+ needed: bool = false,
+ weak: bool = false,
+};
+
+pub const IncludeDir = union(enum) {
+ raw_path: []const u8,
+ raw_path_system: []const u8,
+ other_step: *LibExeObjStep,
+ config_header_step: *ConfigHeaderStep,
+};
+
+pub const Options = struct {
+ name: []const u8,
+ root_source_file: ?FileSource = null,
+ target: CrossTarget,
+ optimize: std.builtin.Mode,
+ kind: Kind,
+ linkage: ?Linkage = null,
+ version: ?std.builtin.Version = null,
+};
+
+pub const Kind = enum {
+ exe,
+ lib,
+ obj,
+ @"test",
+ test_exe,
+};
+
+pub const Linkage = enum { dynamic, static };
+
+pub const EmitOption = union(enum) {
+ default: void,
+ no_emit: void,
+ emit: void,
+ emit_to: []const u8,
+
+ fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 {
+ return switch (self) {
+ .no_emit => b.fmt("-fno-{s}", .{arg_name}),
+ .default => null,
+ .emit => b.fmt("-f{s}", .{arg_name}),
+ .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
+ };
+ }
+};
+
+pub fn create(builder: *std.Build, options: Options) *LibExeObjStep {
+ const name = builder.dupe(options.name);
+ const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
+ if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
+ panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
+ }
+
+ const self = builder.allocator.create(LibExeObjStep) catch unreachable;
+ self.* = LibExeObjStep{
+ .strip = null,
+ .unwind_tables = null,
+ .builder = builder,
+ .verbose_link = false,
+ .verbose_cc = false,
+ .optimize = options.optimize,
+ .target = options.target,
+ .linkage = options.linkage,
+ .kind = options.kind,
+ .root_src = root_src,
+ .name = name,
+ .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
+ .step = Step.init(base_id, name, builder.allocator, make),
+ .version = options.version,
+ .out_filename = undefined,
+ .out_h_filename = builder.fmt("{s}.h", .{name}),
+ .out_lib_filename = undefined,
+ .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
+ .major_only_filename = null,
+ .name_only_filename = null,
+ .packages = ArrayList(Pkg).init(builder.allocator),
+ .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
+ .link_objects = ArrayList(LinkObject).init(builder.allocator),
+ .c_macros = ArrayList([]const u8).init(builder.allocator),
+ .lib_paths = ArrayList([]const u8).init(builder.allocator),
+ .rpaths = ArrayList([]const u8).init(builder.allocator),
+ .framework_dirs = ArrayList([]const u8).init(builder.allocator),
+ .installed_headers = ArrayList(*Step).init(builder.allocator),
+ .object_src = undefined,
+ .c_std = std.Build.CStd.C99,
+ .override_lib_dir = null,
+ .main_pkg_path = null,
+ .exec_cmd_args = null,
+ .name_prefix = "",
+ .filter = null,
+ .test_runner = null,
+ .disable_stack_probing = false,
+ .disable_sanitize_c = false,
+ .sanitize_thread = false,
+ .rdynamic = false,
+ .output_dir = null,
+ .override_dest_dir = null,
+ .installed_path = null,
+ .install_step = null,
+
+ .output_path_source = GeneratedFile{ .step = &self.step },
+ .output_lib_path_source = GeneratedFile{ .step = &self.step },
+ .output_h_path_source = GeneratedFile{ .step = &self.step },
+ .output_pdb_path_source = GeneratedFile{ .step = &self.step },
+
+ .target_info = undefined, // populated in computeOutFileNames
+ };
+ self.computeOutFileNames();
+ if (root_src) |rs| rs.addStepDependencies(&self.step);
+ return self;
+}
+
+fn computeOutFileNames(self: *LibExeObjStep) void {
+ self.target_info = NativeTargetInfo.detect(self.target) catch
+ unreachable;
+
+ const target = self.target_info.target;
+
+ self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
+ .root_name = self.name,
+ .target = target,
+ .output_mode = switch (self.kind) {
+ .lib => .Lib,
+ .obj => .Obj,
+ .exe, .@"test", .test_exe => .Exe,
+ },
+ .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
+ .dynamic => .Dynamic,
+ .static => .Static,
+ }) else null,
+ .version = self.version,
+ }) catch unreachable;
+
+ if (self.kind == .lib) {
+ if (self.linkage != null and self.linkage.? == .static) {
+ self.out_lib_filename = self.out_filename;
+ } else if (self.version) |version| {
+ if (target.isDarwin()) {
+ self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
+ self.name,
+ version.major,
+ });
+ self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
+ self.out_lib_filename = self.out_filename;
+ } else if (target.os.tag == .windows) {
+ self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
+ } else {
+ self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
+ self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
+ self.out_lib_filename = self.out_filename;
+ }
+ } else {
+ if (target.isDarwin()) {
+ self.out_lib_filename = self.out_filename;
+ } else if (target.os.tag == .windows) {
+ self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
+ } else {
+ self.out_lib_filename = self.out_filename;
+ }
+ }
+ if (self.output_dir != null) {
+ self.output_lib_path_source.path = self.builder.pathJoin(
+ &.{ self.output_dir.?, self.out_lib_filename },
+ );
+ }
+ }
+}
+
+pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
+ self.output_dir = self.builder.dupePath(dir);
+}
+
+pub fn install(self: *LibExeObjStep) void {
+ self.builder.installArtifact(self);
+}
+
+pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
+ return self.builder.installRaw(self, dest_filename, options);
+}
+
+pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
+ const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
+ a.builder.getInstallStep().dependOn(&install_file.step);
+ a.installed_headers.append(&install_file.step) catch unreachable;
+}
+
+pub fn installHeadersDirectory(
+ a: *LibExeObjStep,
+ src_dir_path: []const u8,
+ dest_rel_path: []const u8,
+) void {
+ return installHeadersDirectoryOptions(a, .{
+ .source_dir = src_dir_path,
+ .install_dir = .header,
+ .install_subdir = dest_rel_path,
+ });
+}
+
+pub fn installHeadersDirectoryOptions(
+ a: *LibExeObjStep,
+ options: std.Build.InstallDirStep.Options,
+) void {
+ const install_dir = a.builder.addInstallDirectory(options);
+ a.builder.getInstallStep().dependOn(&install_dir.step);
+ a.installed_headers.append(&install_dir.step) catch unreachable;
+}
+
+pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
+ assert(l.kind == .lib);
+ const install_step = a.builder.getInstallStep();
+ // Copy each element from installed_headers, modifying the builder
+ // to be the new parent's builder.
+ for (l.installed_headers.items) |step| {
+ const step_copy = switch (step.id) {
+ inline .install_file, .install_dir => |id| blk: {
+ const T = id.Type();
+ const ptr = a.builder.allocator.create(T) catch unreachable;
+ ptr.* = step.cast(T).?.*;
+ ptr.override_source_builder = ptr.builder;
+ ptr.builder = a.builder;
+ break :blk &ptr.step;
+ },
+ else => unreachable,
+ };
+ a.installed_headers.append(step_copy) catch unreachable;
+ install_step.dependOn(step_copy);
+ }
+ a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
+}
+
+/// Creates a `RunStep` with an executable built with `addExecutable`.
+/// Add command line arguments with `addArg`.
+pub fn run(exe: *LibExeObjStep) *RunStep {
+ assert(exe.kind == .exe or exe.kind == .test_exe);
+
+ // It doesn't have to be native. We catch that if you actually try to run it.
+ // Consider that this is declarative; the run step may not be run unless a user
+ // option is supplied.
+ const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
+ run_step.addArtifactArg(exe);
+
+ if (exe.kind == .test_exe) {
+ run_step.addArg(exe.builder.zig_exe);
+ }
+
+ if (exe.vcpkg_bin_path) |path| {
+ run_step.addPathDir(path);
+ }
+
+ return run_step;
+}
+
+/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
+/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
+/// When a binary cannot be ran through emulation or the option is disabled, a warning
+/// will be printed and the binary will *NOT* be ran.
+pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
+ assert(exe.kind == .exe or exe.kind == .test_exe);
+
+ const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
+ if (exe.vcpkg_bin_path) |path| {
+ RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
+ }
+ return run_step;
+}
+
+pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
+ return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
+}
+
+pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
+ self.linker_script = source.dupe(self.builder);
+ source.addStepDependencies(&self.step);
+}
+
+pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
+ self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
+}
+
+pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
+ self.frameworks.put(self.builder.dupe(framework_name), .{
+ .needed = true,
+ }) catch unreachable;
+}
+
+pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
+ self.frameworks.put(self.builder.dupe(framework_name), .{
+ .weak = true,
+ }) catch unreachable;
+}
+
+/// Returns whether the library, executable, or object depends on a particular system library.
+pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
+ if (isLibCLibrary(name)) {
+ return self.is_linking_libc;
+ }
+ if (isLibCppLibrary(name)) {
+ return self.is_linking_libcpp;
+ }
+ for (self.link_objects.items) |link_object| {
+ switch (link_object) {
+ .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
+ else => continue,
+ }
+ }
+ return false;
+}
+
+pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
+ assert(lib.kind == .lib);
+ self.linkLibraryOrObject(lib);
+}
+
+pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
+ return self.kind == .lib and self.linkage == Linkage.dynamic;
+}
+
+pub fn isStaticLibrary(self: *LibExeObjStep) bool {
+ return self.kind == .lib and self.linkage != Linkage.dynamic;
+}
+
+pub fn producesPdbFile(self: *LibExeObjStep) bool {
+ if (!self.target.isWindows() and !self.target.isUefi()) return false;
+ if (self.target.getObjectFormat() == .c) return false;
+ if (self.strip == true) return false;
+ return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
+}
+
+pub fn linkLibC(self: *LibExeObjStep) void {
+ self.is_linking_libc = true;
+}
+
+pub fn linkLibCpp(self: *LibExeObjStep) void {
+ self.is_linking_libcpp = true;
+}
+
+/// If the value is omitted, it is set to 1.
+/// `name` and `value` need not live longer than the function call.
+pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
+ const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
+ self.c_macros.append(macro) catch unreachable;
+}
+
+/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
+pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
+ self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
+}
+
+/// This one has no integration with anything, it just puts -lname on the command line.
+/// Prefer to use `linkSystemLibrary` instead.
+pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = false,
+ .weak = false,
+ .use_pkg_config = .no,
+ },
+ }) catch unreachable;
+}
+
+/// This one has no integration with anything, it just puts -needed-lname on the command line.
+/// Prefer to use `linkSystemLibraryNeeded` instead.
+pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = true,
+ .weak = false,
+ .use_pkg_config = .no,
+ },
+ }) catch unreachable;
+}
+
+/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
+/// command line. Prefer to use `linkSystemLibraryWeak` instead.
+pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = false,
+ .weak = true,
+ .use_pkg_config = .no,
+ },
+ }) catch unreachable;
+}
+
+/// This links against a system library, exclusively using pkg-config to find the library.
+/// Prefer to use `linkSystemLibrary` instead.
+pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(lib_name),
+ .needed = false,
+ .weak = false,
+ .use_pkg_config = .force,
+ },
+ }) catch unreachable;
+}
+
+/// This links against a system library, exclusively using pkg-config to find the library.
+/// Prefer to use `linkSystemLibraryNeeded` instead.
+pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(lib_name),
+ .needed = true,
+ .weak = false,
+ .use_pkg_config = .force,
+ },
+ }) catch unreachable;
+}
+
+/// Run pkg-config for the given library name and parse the output, returning the arguments
+/// that should be passed to zig to link the given library.
+pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
+ const pkg_name = match: {
+ // First we have to map the library name to pkg config name. Unfortunately,
+ // there are several examples where this is not straightforward:
+ // -lSDL2 -> pkg-config sdl2
+ // -lgdk-3 -> pkg-config gdk-3.0
+ // -latk-1.0 -> pkg-config atk
+ const pkgs = try getPkgConfigList(self.builder);
+
+ // Exact match means instant winner.
+ for (pkgs) |pkg| {
+ if (mem.eql(u8, pkg.name, lib_name)) {
+ break :match pkg.name;
+ }
+ }
+
+ // Next we'll try ignoring case.
+ for (pkgs) |pkg| {
+ if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
+ break :match pkg.name;
+ }
+ }
+
+ // Now try appending ".0".
+ for (pkgs) |pkg| {
+ if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
+ if (pos != 0) continue;
+ if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
+ break :match pkg.name;
+ }
+ }
+ }
+
+ // Trimming "-1.0".
+ if (mem.endsWith(u8, lib_name, "-1.0")) {
+ const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
+ for (pkgs) |pkg| {
+ if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
+ break :match pkg.name;
+ }
+ }
+ }
+
+ return error.PackageNotFound;
+ };
+
+ var code: u8 = undefined;
+ const stdout = if (self.builder.execAllowFail(&[_][]const u8{
+ "pkg-config",
+ pkg_name,
+ "--cflags",
+ "--libs",
+ }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
+ error.ProcessTerminated => return error.PkgConfigCrashed,
+ error.ExecNotSupported => return error.PkgConfigFailed,
+ error.ExitCodeFailure => return error.PkgConfigFailed,
+ error.FileNotFound => return error.PkgConfigNotInstalled,
+ error.ChildExecFailed => return error.PkgConfigFailed,
+ else => return err,
+ };
+
+ var zig_args = ArrayList([]const u8).init(self.builder.allocator);
+ defer zig_args.deinit();
+
+ var it = mem.tokenize(u8, stdout, " \r\n\t");
+ while (it.next()) |tok| {
+ if (mem.eql(u8, tok, "-I")) {
+ const dir = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
+ } else if (mem.startsWith(u8, tok, "-I")) {
+ try zig_args.append(tok);
+ } else if (mem.eql(u8, tok, "-L")) {
+ const dir = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
+ } else if (mem.startsWith(u8, tok, "-L")) {
+ try zig_args.append(tok);
+ } else if (mem.eql(u8, tok, "-l")) {
+ const lib = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
+ } else if (mem.startsWith(u8, tok, "-l")) {
+ try zig_args.append(tok);
+ } else if (mem.eql(u8, tok, "-D")) {
+ const macro = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
+ } else if (mem.startsWith(u8, tok, "-D")) {
+ try zig_args.append(tok);
+ } else if (self.builder.verbose) {
+ log.warn("Ignoring pkg-config flag '{s}'", .{tok});
+ }
+ }
+
+ return zig_args.toOwnedSlice();
+}
+
+pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
+ self.linkSystemLibraryInner(name, .{});
+}
+
+pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
+ self.linkSystemLibraryInner(name, .{ .needed = true });
+}
+
+pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
+ self.linkSystemLibraryInner(name, .{ .weak = true });
+}
+
+fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
+ needed: bool = false,
+ weak: bool = false,
+}) void {
+ if (isLibCLibrary(name)) {
+ self.linkLibC();
+ return;
+ }
+ if (isLibCppLibrary(name)) {
+ self.linkLibCpp();
+ return;
+ }
+
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = opts.needed,
+ .weak = opts.weak,
+ .use_pkg_config = .yes,
+ },
+ }) catch unreachable;
+}
+
+pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
+ assert(self.kind == .@"test" or self.kind == .test_exe);
+ self.name_prefix = self.builder.dupe(text);
+}
+
+pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
+ assert(self.kind == .@"test" or self.kind == .test_exe);
+ self.filter = if (text) |t| self.builder.dupe(t) else null;
+}
+
+pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
+ assert(self.kind == .@"test" or self.kind == .test_exe);
+ self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
+}
+
+/// Handy when you have many C/C++ source files and want them all to have the same flags.
+pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
+ const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
+
+ const files_copy = self.builder.dupeStrings(files);
+ const flags_copy = self.builder.dupeStrings(flags);
+
+ c_source_files.* = .{
+ .files = files_copy,
+ .flags = flags_copy,
+ };
+ self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
+}
+
+pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
+ self.addCSourceFileSource(.{
+ .args = flags,
+ .source = .{ .path = file },
+ });
+}
+
+pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
+ const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
+ c_source_file.* = source.dupe(self.builder);
+ self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
+ source.source.addStepDependencies(&self.step);
+}
+
+pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
+ self.verbose_link = value;
+}
+
+pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
+ self.verbose_cc = value;
+}
+
+pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
+ self.override_lib_dir = self.builder.dupePath(dir_path);
+}
+
+pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
+ self.main_pkg_path = self.builder.dupePath(dir_path);
+}
+
+pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
+ self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
+}
+
+/// Returns the generated executable, library or object file.
+/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
+pub fn getOutputSource(self: *LibExeObjStep) FileSource {
+ return FileSource{ .generated = &self.output_path_source };
+}
+
+/// Returns the generated import library. This function can only be called for libraries.
+pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
+ assert(self.kind == .lib);
+ return FileSource{ .generated = &self.output_lib_path_source };
+}
+
+/// Returns the generated header file.
+/// This function can only be called for libraries or object files which have `emit_h` set.
+pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
+ assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
+ assert(self.emit_h);
+ return FileSource{ .generated = &self.output_h_path_source };
+}
+
+/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
+pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
+ // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
+ assert(self.target.isWindows() or self.target.isUefi());
+ return FileSource{ .generated = &self.output_pdb_path_source };
+}
+
+pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
+ self.link_objects.append(.{
+ .assembly_file = .{ .path = self.builder.dupe(path) },
+ }) catch unreachable;
+}
+
+pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
+ const source_duped = source.dupe(self.builder);
+ self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
+ source_duped.addStepDependencies(&self.step);
+}
+
+pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
+ self.addObjectFileSource(.{ .path = source_file });
+}
+
+pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
+ self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
+ source.addStepDependencies(&self.step);
+}
+
+pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
+ assert(obj.kind == .obj);
+ self.linkLibraryOrObject(obj);
+}
+
+pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
+pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
+pub const addLibPath = @compileError("deprecated, use addLibraryPath");
+pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
+
+pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
+ self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
+}
+
+pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
+ self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
+}
+
+pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
+ self.step.dependOn(&config_header.step);
+ self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
+}
+
+pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
+ self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
+}
+
+pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
+ self.rpaths.append(self.builder.dupe(path)) catch unreachable;
+}
+
+pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
+ self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
+}
+
+pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
+ self.packages.append(self.builder.dupePkg(package)) catch unreachable;
+ self.addRecursiveBuildDeps(package);
+}
+
+pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
+ self.addPackage(options.getPackage(package_name));
+}
+
+fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
+ package.source.addStepDependencies(&self.step);
+ if (package.dependencies) |deps| {
+ for (deps) |dep| {
+ self.addRecursiveBuildDeps(dep);
+ }
+ }
+}
+
+pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
+ self.addPackage(Pkg{
+ .name = self.builder.dupe(name),
+ .source = .{ .path = self.builder.dupe(pkg_index_path) },
+ });
+}
+
+/// If Vcpkg was found on the system, it will be added to include and lib
+/// paths for the specified target.
+pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
+ // Ideally in the Unattempted case we would call the function recursively
+ // after findVcpkgRoot and have only one switch statement, but the compiler
+ // cannot resolve the error set.
+ switch (self.builder.vcpkg_root) {
+ .unattempted => {
+ self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
+ VcpkgRoot{ .found = root }
+ else
+ .not_found;
+ },
+ .not_found => return error.VcpkgNotFound,
+ .found => {},
+ }
+
+ switch (self.builder.vcpkg_root) {
+ .unattempted => unreachable,
+ .not_found => return error.VcpkgNotFound,
+ .found => |root| {
+ const allocator = self.builder.allocator;
+ const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
+ defer self.builder.allocator.free(triplet);
+
+ const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
+ errdefer allocator.free(include_path);
+ try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
+
+ const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
+ try self.lib_paths.append(lib_path);
+
+ self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
+ },
+ }
+}
+
+pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
+ assert(self.kind == .@"test");
+ const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
+ for (args) |arg, i| {
+ duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
+ }
+ self.exec_cmd_args = duped_args;
+}
+
+fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
+ self.step.dependOn(&other.step);
+ self.link_objects.append(.{ .other_step = other }) catch unreachable;
+ self.include_dirs.append(.{ .other_step = other }) catch unreachable;
+}
+
+fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
+ const builder = self.builder;
+
+ try zig_args.append("--pkg-begin");
+ try zig_args.append(pkg.name);
+ try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
+
+ if (pkg.dependencies) |dependencies| {
+ for (dependencies) |sub_pkg| {
+ try self.makePackageCmd(sub_pkg, zig_args);
+ }
+ }
+
+ try zig_args.append("--pkg-end");
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(LibExeObjStep, "step", step);
+ const builder = self.builder;
+
+ if (self.root_src == null and self.link_objects.items.len == 0) {
+ log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
+ return error.NeedAnObject;
+ }
+
+ var zig_args = ArrayList([]const u8).init(builder.allocator);
+ defer zig_args.deinit();
+
+ zig_args.append(builder.zig_exe) catch unreachable;
+
+ const cmd = switch (self.kind) {
+ .lib => "build-lib",
+ .exe => "build-exe",
+ .obj => "build-obj",
+ .@"test" => "test",
+ .test_exe => "test",
+ };
+ zig_args.append(cmd) catch unreachable;
+
+ if (builder.color != .auto) {
+ try zig_args.append("--color");
+ try zig_args.append(@tagName(builder.color));
+ }
+
+ if (builder.reference_trace) |some| {
+ try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
+ }
+
+ try addFlag(&zig_args, "LLVM", self.use_llvm);
+ try addFlag(&zig_args, "LLD", self.use_lld);
+
+ if (self.target.ofmt) |ofmt| {
+ try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
+ }
+
+ if (self.entry_symbol_name) |entry| {
+ try zig_args.append("--entry");
+ try zig_args.append(entry);
+ }
+
+ if (self.stack_size) |stack_size| {
+ try zig_args.append("--stack");
+ try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
+ }
+
+ if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
+
+ // We will add link objects from transitive dependencies, but we want to keep
+ // all link objects in the same order provided.
+ // This array is used to keep self.link_objects immutable.
+ var transitive_deps: TransitiveDeps = .{
+ .link_objects = ArrayList(LinkObject).init(builder.allocator),
+ .seen_system_libs = StringHashMap(void).init(builder.allocator),
+ .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
+ .is_linking_libcpp = self.is_linking_libcpp,
+ .is_linking_libc = self.is_linking_libc,
+ .frameworks = &self.frameworks,
+ };
+
+ try transitive_deps.seen_steps.put(&self.step, {});
+ try transitive_deps.add(self.link_objects.items);
+
+ var prev_has_extra_flags = false;
+
+ for (transitive_deps.link_objects.items) |link_object| {
+ switch (link_object) {
+ .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
+
+ .other_step => |other| switch (other.kind) {
+ .exe => @panic("Cannot link with an executable build artifact"),
+ .test_exe => @panic("Cannot link with an executable build artifact"),
+ .@"test" => @panic("Cannot link with a test"),
+ .obj => {
+ try zig_args.append(other.getOutputSource().getPath(builder));
+ },
+ .lib => l: {
+ if (self.isStaticLibrary() and other.isStaticLibrary()) {
+ // Avoid putting a static library inside a static library.
+ break :l;
+ }
+
+ const full_path_lib = other.getOutputLibSource().getPath(builder);
+ try zig_args.append(full_path_lib);
+
+ if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
+ if (fs.path.dirname(full_path_lib)) |dirname| {
+ try zig_args.append("-rpath");
+ try zig_args.append(dirname);
+ }
+ }
+ },
+ },
+
+ .system_lib => |system_lib| {
+ const prefix: []const u8 = prefix: {
+ if (system_lib.needed) break :prefix "-needed-l";
+ if (system_lib.weak) {
+ if (self.target.isDarwin()) break :prefix "-weak-l";
+ log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
+ }
+ break :prefix "-l";
+ };
+ switch (system_lib.use_pkg_config) {
+ .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
+ .yes, .force => {
+ if (self.runPkgConfig(system_lib.name)) |args| {
+ try zig_args.appendSlice(args);
+ } else |err| switch (err) {
+ error.PkgConfigInvalidOutput,
+ error.PkgConfigCrashed,
+ error.PkgConfigFailed,
+ error.PkgConfigNotInstalled,
+ error.PackageNotFound,
+ => switch (system_lib.use_pkg_config) {
+ .yes => {
+ // pkg-config failed, so fall back to linking the library
+ // by name directly.
+ try zig_args.append(builder.fmt("{s}{s}", .{
+ prefix,
+ system_lib.name,
+ }));
+ },
+ .force => {
+ panic("pkg-config failed for library {s}", .{system_lib.name});
+ },
+ .no => unreachable,
+ },
+
+ else => |e| return e,
+ }
+ },
+ }
+ },
+
+ .assembly_file => |asm_file| {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-extra-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ try zig_args.append(asm_file.getPath(builder));
+ },
+
+ .c_source_file => |c_source_file| {
+ if (c_source_file.args.len == 0) {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ } else {
+ try zig_args.append("-cflags");
+ for (c_source_file.args) |arg| {
+ try zig_args.append(arg);
+ }
+ try zig_args.append("--");
+ }
+ try zig_args.append(c_source_file.source.getPath(builder));
+ },
+
+ .c_source_files => |c_source_files| {
+ if (c_source_files.flags.len == 0) {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ } else {
+ try zig_args.append("-cflags");
+ for (c_source_files.flags) |flag| {
+ try zig_args.append(flag);
+ }
+ try zig_args.append("--");
+ }
+ for (c_source_files.files) |file| {
+ try zig_args.append(builder.pathFromRoot(file));
+ }
+ },
+ }
+ }
+
+ if (transitive_deps.is_linking_libcpp) {
+ try zig_args.append("-lc++");
+ }
+
+ if (transitive_deps.is_linking_libc) {
+ try zig_args.append("-lc");
+ }
+
+ if (self.image_base) |image_base| {
+ try zig_args.append("--image-base");
+ try zig_args.append(builder.fmt("0x{x}", .{image_base}));
+ }
+
+ if (self.filter) |filter| {
+ try zig_args.append("--test-filter");
+ try zig_args.append(filter);
+ }
+
+ if (self.test_evented_io) {
+ try zig_args.append("--test-evented-io");
+ }
+
+ if (self.name_prefix.len != 0) {
+ try zig_args.append("--test-name-prefix");
+ try zig_args.append(self.name_prefix);
+ }
+
+ if (self.test_runner) |test_runner| {
+ try zig_args.append("--test-runner");
+ try zig_args.append(builder.pathFromRoot(test_runner));
+ }
+
+ for (builder.debug_log_scopes) |log_scope| {
+ try zig_args.append("--debug-log");
+ try zig_args.append(log_scope);
+ }
+
+ if (builder.debug_compile_errors) {
+ try zig_args.append("--debug-compile-errors");
+ }
+
+ if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
+ if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
+ if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
+ if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
+ if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
+ if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
+
+ if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
+ if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
+ if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
+ if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
+ if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
+ if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
+ if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
+
+ if (self.emit_h) try zig_args.append("-femit-h");
+
+ try addFlag(&zig_args, "strip", self.strip);
+ try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
+
+ switch (self.compress_debug_sections) {
+ .none => {},
+ .zlib => try zig_args.append("--compress-debug-sections=zlib"),
+ }
+
+ if (self.link_eh_frame_hdr) {
+ try zig_args.append("--eh-frame-hdr");
+ }
+ if (self.link_emit_relocs) {
+ try zig_args.append("--emit-relocs");
+ }
+ if (self.link_function_sections) {
+ try zig_args.append("-ffunction-sections");
+ }
+ if (self.link_gc_sections) |x| {
+ try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
+ }
+ if (self.linker_allow_shlib_undefined) |x| {
+ try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
+ }
+ if (self.link_z_notext) {
+ try zig_args.append("-z");
+ try zig_args.append("notext");
+ }
+ if (!self.link_z_relro) {
+ try zig_args.append("-z");
+ try zig_args.append("norelro");
+ }
+ if (self.link_z_lazy) {
+ try zig_args.append("-z");
+ try zig_args.append("lazy");
+ }
+ if (self.link_z_common_page_size) |size| {
+ try zig_args.append("-z");
+ try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
+ }
+ if (self.link_z_max_page_size) |size| {
+ try zig_args.append("-z");
+ try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
+ }
+
+ if (self.libc_file) |libc_file| {
+ try zig_args.append("--libc");
+ try zig_args.append(libc_file.getPath(builder));
+ } else if (builder.libc_file) |libc_file| {
+ try zig_args.append("--libc");
+ try zig_args.append(libc_file);
+ }
+
+ switch (self.optimize) {
+ .Debug => {}, // Skip since it's the default.
+ else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable,
+ }
+
+ try zig_args.append("--cache-dir");
+ try zig_args.append(builder.pathFromRoot(builder.cache_root));
+
+ try zig_args.append("--global-cache-dir");
+ try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
+
+ zig_args.append("--name") catch unreachable;
+ zig_args.append(self.name) catch unreachable;
+
+ if (self.linkage) |some| switch (some) {
+ .dynamic => try zig_args.append("-dynamic"),
+ .static => try zig_args.append("-static"),
+ };
+ if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
+ if (self.version) |version| {
+ zig_args.append("--version") catch unreachable;
+ zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
+ }
+
+ if (self.target.isDarwin()) {
+ const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
+ self.target.libPrefix(),
+ self.name,
+ self.target.dynamicLibSuffix(),
+ });
+ try zig_args.append("-install_name");
+ try zig_args.append(install_name);
+ }
+ }
+
+ if (self.entitlements) |entitlements| {
+ try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
+ }
+ if (self.pagezero_size) |pagezero_size| {
+ const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
+ try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
+ }
+ if (self.search_strategy) |strat| switch (strat) {
+ .paths_first => try zig_args.append("-search_paths_first"),
+ .dylibs_first => try zig_args.append("-search_dylibs_first"),
+ };
+ if (self.headerpad_size) |headerpad_size| {
+ const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
+ try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
+ }
+ if (self.headerpad_max_install_names) {
+ try zig_args.append("-headerpad_max_install_names");
+ }
+ if (self.dead_strip_dylibs) {
+ try zig_args.append("-dead_strip_dylibs");
+ }
+
+ try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
+ try addFlag(&zig_args, "single-threaded", self.single_threaded);
+ if (self.disable_stack_probing) {
+ try zig_args.append("-fno-stack-check");
+ }
+ try addFlag(&zig_args, "stack-protector", self.stack_protector);
+ if (self.red_zone) |red_zone| {
+ if (red_zone) {
+ try zig_args.append("-mred-zone");
+ } else {
+ try zig_args.append("-mno-red-zone");
+ }
+ }
+ try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
+ try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
+
+ if (self.disable_sanitize_c) {
+ try zig_args.append("-fno-sanitize-c");
+ }
+ if (self.sanitize_thread) {
+ try zig_args.append("-fsanitize-thread");
+ }
+ if (self.rdynamic) {
+ try zig_args.append("-rdynamic");
+ }
+ if (self.import_memory) {
+ try zig_args.append("--import-memory");
+ }
+ if (self.import_symbols) {
+ try zig_args.append("--import-symbols");
+ }
+ if (self.import_table) {
+ try zig_args.append("--import-table");
+ }
+ if (self.export_table) {
+ try zig_args.append("--export-table");
+ }
+ if (self.initial_memory) |initial_memory| {
+ try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
+ }
+ if (self.max_memory) |max_memory| {
+ try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
+ }
+ if (self.shared_memory) {
+ try zig_args.append("--shared-memory");
+ }
+ if (self.global_base) |global_base| {
+ try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
+ }
+
+ if (self.code_model != .default) {
+ try zig_args.append("-mcmodel");
+ try zig_args.append(@tagName(self.code_model));
+ }
+ if (self.wasi_exec_model) |model| {
+ try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
+ }
+ for (self.export_symbol_names) |symbol_name| {
+ try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
+ }
+
+ if (!self.target.isNative()) {
+ try zig_args.appendSlice(&.{
+ "-target", try self.target.zigTriple(builder.allocator),
+ "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
+ });
+
+ if (self.target.dynamic_linker.get()) |dynamic_linker| {
+ try zig_args.append("--dynamic-linker");
+ try zig_args.append(dynamic_linker);
+ }
+ }
+
+ if (self.linker_script) |linker_script| {
+ try zig_args.append("--script");
+ try zig_args.append(linker_script.getPath(builder));
+ }
+
+ if (self.version_script) |version_script| {
+ try zig_args.append("--version-script");
+ try zig_args.append(builder.pathFromRoot(version_script));
+ }
+
+ if (self.kind == .@"test") {
+ if (self.exec_cmd_args) |exec_cmd_args| {
+ for (exec_cmd_args) |cmd_arg| {
+ if (cmd_arg) |arg| {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(arg);
+ } else {
+ try zig_args.append("--test-cmd-bin");
+ }
+ }
+ } else {
+ const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
+
+ switch (builder.host.getExternalExecutor(self.target_info, .{
+ .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
+ .link_libc = transitive_deps.is_linking_libc,
+ })) {
+ .native => {},
+ .bad_dl, .bad_os_or_cpu => {
+ try zig_args.append("--test-no-exec");
+ },
+ .rosetta => if (builder.enable_rosetta) {
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ .qemu => |bin_name| ok: {
+ if (builder.enable_qemu) qemu: {
+ const glibc_dir_arg = if (need_cross_glibc)
+ builder.glibc_runtimes_dir orelse break :qemu
+ else
+ null;
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ if (glibc_dir_arg) |dir| {
+ // TODO look into making this a call to `linuxTriple`. This
+ // needs the directory to be called "i686" rather than
+ // "x86" which is why we do it manually here.
+ const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
+ const cpu_arch = self.target.getCpuArch();
+ const os_tag = self.target.getOsTag();
+ const abi = self.target.getAbi();
+ const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
+ "i686"
+ else
+ @tagName(cpu_arch);
+ const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
+ dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
+ });
+
+ try zig_args.append("--test-cmd");
+ try zig_args.append("-L");
+ try zig_args.append("--test-cmd");
+ try zig_args.append(full_dir);
+ }
+ try zig_args.append("--test-cmd-bin");
+ break :ok;
+ }
+ try zig_args.append("--test-no-exec");
+ },
+ .wine => |bin_name| if (builder.enable_wine) {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ .wasmtime => |bin_name| if (builder.enable_wasmtime) {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ try zig_args.append("--test-cmd");
+ try zig_args.append("--dir=.");
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ .darling => |bin_name| if (builder.enable_darling) {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ }
+ }
+ } else if (self.kind == .test_exe) {
+ try zig_args.append("--test-no-exec");
+ }
+
+ for (self.packages.items) |pkg| {
+ try self.makePackageCmd(pkg, &zig_args);
+ }
+
+ for (self.include_dirs.items) |include_dir| {
+ switch (include_dir) {
+ .raw_path => |include_path| {
+ try zig_args.append("-I");
+ try zig_args.append(builder.pathFromRoot(include_path));
+ },
+ .raw_path_system => |include_path| {
+ if (builder.sysroot != null) {
+ try zig_args.append("-iwithsysroot");
+ } else {
+ try zig_args.append("-isystem");
+ }
+
+ const resolved_include_path = builder.pathFromRoot(include_path);
+
+ const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
+ // We need to check for disk designator and strip it out from dir path so
+ // that zig/clang can concat resolved_include_path with sysroot.
+ const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
+
+ if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
+ break :blk resolved_include_path[where + disk_designator.len ..];
+ }
+
+ break :blk resolved_include_path;
+ } else resolved_include_path;
+
+ try zig_args.append(common_include_path);
+ },
+ .other_step => |other| {
+ if (other.emit_h) {
+ const h_path = other.getOutputHSource().getPath(builder);
+ try zig_args.append("-isystem");
+ try zig_args.append(fs.path.dirname(h_path).?);
+ }
+ if (other.installed_headers.items.len > 0) {
+ for (other.installed_headers.items) |install_step| {
+ try install_step.make();
+ }
+ try zig_args.append("-I");
+ try zig_args.append(builder.pathJoin(&.{
+ other.builder.install_prefix, "include",
+ }));
+ }
+ },
+ .config_header_step => |config_header| {
+ try zig_args.append("-I");
+ try zig_args.append(config_header.output_dir);
+ },
+ }
+ }
+
+ for (self.lib_paths.items) |lib_path| {
+ try zig_args.append("-L");
+ try zig_args.append(lib_path);
+ }
+
+ for (self.rpaths.items) |rpath| {
+ try zig_args.append("-rpath");
+ try zig_args.append(rpath);
+ }
+
+ for (self.c_macros.items) |c_macro| {
+ try zig_args.append("-D");
+ try zig_args.append(c_macro);
+ }
+
+ if (self.target.isDarwin()) {
+ for (self.framework_dirs.items) |dir| {
+ if (builder.sysroot != null) {
+ try zig_args.append("-iframeworkwithsysroot");
+ } else {
+ try zig_args.append("-iframework");
+ }
+ try zig_args.append(dir);
+ try zig_args.append("-F");
+ try zig_args.append(dir);
+ }
+
+ var it = self.frameworks.iterator();
+ while (it.next()) |entry| {
+ const name = entry.key_ptr.*;
+ const info = entry.value_ptr.*;
+ if (info.needed) {
+ zig_args.append("-needed_framework") catch unreachable;
+ } else if (info.weak) {
+ zig_args.append("-weak_framework") catch unreachable;
+ } else {
+ zig_args.append("-framework") catch unreachable;
+ }
+ zig_args.append(name) catch unreachable;
+ }
+ } else {
+ if (self.framework_dirs.items.len > 0) {
+ log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
+ }
+
+ if (self.frameworks.count() > 0) {
+ log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
+ }
+ }
+
+ if (builder.sysroot) |sysroot| {
+ try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
+ }
+
+ for (builder.search_prefixes.items) |search_prefix| {
+ try zig_args.append("-L");
+ try zig_args.append(builder.pathJoin(&.{
+ search_prefix, "lib",
+ }));
+ try zig_args.append("-I");
+ try zig_args.append(builder.pathJoin(&.{
+ search_prefix, "include",
+ }));
+ }
+
+ try addFlag(&zig_args, "valgrind", self.valgrind_support);
+ try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
+ try addFlag(&zig_args, "build-id", self.build_id);
+
+ if (self.override_lib_dir) |dir| {
+ try zig_args.append("--zig-lib-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ } else if (builder.override_lib_dir) |dir| {
+ try zig_args.append("--zig-lib-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
+
+ if (self.main_pkg_path) |dir| {
+ try zig_args.append("--main-pkg-path");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
+
+ try addFlag(&zig_args, "PIC", self.force_pic);
+ try addFlag(&zig_args, "PIE", self.pie);
+ try addFlag(&zig_args, "lto", self.want_lto);
+
+ if (self.subsystem) |subsystem| {
+ try zig_args.append("--subsystem");
+ try zig_args.append(switch (subsystem) {
+ .Console => "console",
+ .Windows => "windows",
+ .Posix => "posix",
+ .Native => "native",
+ .EfiApplication => "efi_application",
+ .EfiBootServiceDriver => "efi_boot_service_driver",
+ .EfiRom => "efi_rom",
+ .EfiRuntimeDriver => "efi_runtime_driver",
+ });
+ }
+
+ try zig_args.append("--enable-cache");
+
+ // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
+ // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
+ // pass that to zig, e.g. via 'zig build-lib @args.rsp'
+ // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
+ var args_length: usize = 0;
+ for (zig_args.items) |arg| {
+ args_length += arg.len + 1; // +1 to account for null terminator
+ }
+ if (args_length >= 30 * 1024) {
+ const args_dir = try fs.path.join(
+ builder.allocator,
+ &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
+ );
+ try std.fs.cwd().makePath(args_dir);
+
+ var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
+ defer args_arena.deinit();
+
+ const args_to_escape = zig_args.items[2..];
+ var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
+
+ arg_blk: for (args_to_escape) |arg| {
+ for (arg) |c, arg_idx| {
+ if (c == '\\' or c == '"') {
+ // Slow path for arguments that need to be escaped. We'll need to allocate and copy
+ var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
+ const writer = escaped.writer();
+ writer.writeAll(arg[0..arg_idx]) catch unreachable;
+ for (arg[arg_idx..]) |to_escape| {
+ if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
+ try writer.writeByte(to_escape);
+ }
+ escaped_args.appendAssumeCapacity(escaped.items);
+ continue :arg_blk;
+ }
+ }
+ escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
+ }
+
+ // Write the args to zig-cache/args/ to avoid conflicts with
+ // other zig build commands running in parallel.
+ const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
+ const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
+
+ var args_hash: [Sha256.digest_length]u8 = undefined;
+ Sha256.hash(args, &args_hash, .{});
+ var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
+ _ = try std.fmt.bufPrint(
+ &args_hex_hash,
+ "{s}",
+ .{std.fmt.fmtSliceHexLower(&args_hash)},
+ );
+
+ const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
+ try std.fs.cwd().writeFile(args_file, args);
+
+ zig_args.shrinkRetainingCapacity(2);
+ try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
+ }
+
+ const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
+ const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
+
+ if (self.output_dir) |output_dir| {
+ var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
+ defer src_dir.close();
+
+ // Create the output directory if it doesn't exist.
+ try std.fs.cwd().makePath(output_dir);
+
+ var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
+ defer dest_dir.close();
+
+ var it = src_dir.iterate();
+ while (try it.next()) |entry| {
+ // The compiler can put these files into the same directory, but we don't
+ // want to copy them over.
+ if (mem.eql(u8, entry.name, "llvm-ar.id") or
+ mem.eql(u8, entry.name, "libs.txt") or
+ mem.eql(u8, entry.name, "builtin.zig") or
+ mem.eql(u8, entry.name, "zld.id") or
+ mem.eql(u8, entry.name, "lld.id")) continue;
+
+ _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
+ }
+ } else {
+ self.output_dir = build_output_dir;
+ }
+
+ // This will ensure all output filenames will now have the output_dir available!
+ self.computeOutFileNames();
+
+ // Update generated files
+ if (self.output_dir != null) {
+ self.output_path_source.path = builder.pathJoin(
+ &.{ self.output_dir.?, self.out_filename },
+ );
+
+ if (self.emit_h) {
+ self.output_h_path_source.path = builder.pathJoin(
+ &.{ self.output_dir.?, self.out_h_filename },
+ );
+ }
+
+ if (self.target.isWindows() or self.target.isUefi()) {
+ self.output_pdb_path_source.path = builder.pathJoin(
+ &.{ self.output_dir.?, self.out_pdb_filename },
+ );
+ }
+ }
+
+ if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
+ try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
+ }
+}
+
+fn isLibCLibrary(name: []const u8) bool {
+ const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
+ for (libc_libraries) |libc_lib_name| {
+ if (mem.eql(u8, name, libc_lib_name))
+ return true;
+ }
+ return false;
+}
+
+fn isLibCppLibrary(name: []const u8) bool {
+ const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
+ for (libcpp_libraries) |libcpp_lib_name| {
+ if (mem.eql(u8, name, libcpp_lib_name))
+ return true;
+ }
+ return false;
+}
+
+/// Returned slice must be freed by the caller.
+fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
+ const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
+ defer allocator.free(appdata_path);
+
+ const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
+ defer allocator.free(path_file);
+
+ const file = fs.cwd().openFile(path_file, .{}) catch return null;
+ defer file.close();
+
+ const size = @intCast(usize, try file.getEndPos());
+ const vcpkg_path = try allocator.alloc(u8, size);
+ const size_read = try file.read(vcpkg_path);
+ std.debug.assert(size == size_read);
+
+ return vcpkg_path;
+}
+
+pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
+ const out_dir = fs.path.dirname(output_path) orelse ".";
+ const out_basename = fs.path.basename(output_path);
+ // sym link for libfoo.so.1 to libfoo.so.1.2.3
+ const major_only_path = fs.path.join(
+ allocator,
+ &[_][]const u8{ out_dir, filename_major_only },
+ ) catch unreachable;
+ fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
+ log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
+ return err;
+ };
+ // sym link for libfoo.so to libfoo.so.1
+ const name_only_path = fs.path.join(
+ allocator,
+ &[_][]const u8{ out_dir, filename_name_only },
+ ) catch unreachable;
+ fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
+ log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
+ return err;
+ };
+}
+
+fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
+ const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
+ var list = ArrayList(PkgConfigPkg).init(self.allocator);
+ errdefer list.deinit();
+ var line_it = mem.tokenize(u8, stdout, "\r\n");
+ while (line_it.next()) |line| {
+ if (mem.trim(u8, line, " \t").len == 0) continue;
+ var tok_it = mem.tokenize(u8, line, " \t");
+ try list.append(PkgConfigPkg{
+ .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
+ .desc = tok_it.rest(),
+ });
+ }
+ return list.toOwnedSlice();
+}
+
+fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
+ if (self.pkg_config_pkg_list) |res| {
+ return res;
+ }
+ var code: u8 = undefined;
+ if (execPkgConfigList(self, &code)) |list| {
+ self.pkg_config_pkg_list = list;
+ return list;
+ } else |err| {
+ const result = switch (err) {
+ error.ProcessTerminated => error.PkgConfigCrashed,
+ error.ExecNotSupported => error.PkgConfigFailed,
+ error.ExitCodeFailure => error.PkgConfigFailed,
+ error.FileNotFound => error.PkgConfigNotInstalled,
+ error.InvalidName => error.PkgConfigNotInstalled,
+ error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
+ error.ChildExecFailed => error.PkgConfigFailed,
+ else => return err,
+ };
+ self.pkg_config_pkg_list = result;
+ return result;
+ }
+}
+
+test "addPackage" {
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+
+ var builder = try std.Build.create(
+ arena.allocator(),
+ "test",
+ "test",
+ "test",
+ "test",
+ );
+ defer builder.destroy();
+
+ const pkg_dep = Pkg{
+ .name = "pkg_dep",
+ .source = .{ .path = "/not/a/pkg_dep.zig" },
+ };
+ const pkg_top = Pkg{
+ .name = "pkg_dep",
+ .source = .{ .path = "/not/a/pkg_top.zig" },
+ .dependencies = &[_]Pkg{pkg_dep},
+ };
+
+ var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
+ exe.addPackage(pkg_top);
+
+ try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
+
+ const dupe = exe.packages.items[0];
+ try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
+}
+
+fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
+ const cond = opt orelse return;
+ try args.ensureUnusedCapacity(1);
+ if (cond) {
+ args.appendAssumeCapacity("-f" ++ name);
+ } else {
+ args.appendAssumeCapacity("-fno-" ++ name);
+ }
+}
+
+const TransitiveDeps = struct {
+ link_objects: ArrayList(LinkObject),
+ seen_system_libs: StringHashMap(void),
+ seen_steps: std.AutoHashMap(*const Step, void),
+ is_linking_libcpp: bool,
+ is_linking_libc: bool,
+ frameworks: *StringHashMap(FrameworkLinkInfo),
+
+ fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
+ try td.link_objects.ensureUnusedCapacity(link_objects.len);
+
+ for (link_objects) |link_object| {
+ try td.link_objects.append(link_object);
+ switch (link_object) {
+ .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
+ else => {},
+ }
+ }
+ }
+
+ fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
+ // Inherit dependency on libc and libc++
+ td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
+ td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
+
+ // Inherit dependencies on darwin frameworks
+ if (!dyn) {
+ var it = other.frameworks.iterator();
+ while (it.next()) |framework| {
+ try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
+ }
+ }
+
+ // Inherit dependencies on system libraries and static libraries.
+ for (other.link_objects.items) |other_link_object| {
+ switch (other_link_object) {
+ .system_lib => |system_lib| {
+ if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
+ continue;
+
+ if (dyn)
+ continue;
+
+ try td.link_objects.append(other_link_object);
+ },
+ .other_step => |inner_other| {
+ if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
+ continue;
+
+ if (!dyn)
+ try td.link_objects.append(other_link_object);
+
+ try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
+ },
+ else => continue,
+ }
+ }
+ }
+};
diff --git a/lib/std/Build/LogStep.zig b/lib/std/Build/LogStep.zig
new file mode 100644
index 0000000000..6d51df8cbd
--- /dev/null
+++ b/lib/std/Build/LogStep.zig
@@ -0,0 +1,23 @@
+const std = @import("../std.zig");
+const log = std.log;
+const Step = std.Build.Step;
+const LogStep = @This();
+
+pub const base_id = .log;
+
+step: Step,
+builder: *std.Build,
+data: []const u8,
+
+pub fn init(builder: *std.Build, data: []const u8) LogStep {
+ return LogStep{
+ .builder = builder,
+ .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
+ .data = builder.dupe(data),
+ };
+}
+
+fn make(step: *Step) anyerror!void {
+ const self = @fieldParentPtr(LogStep, "step", step);
+ log.info("{s}", .{self.data});
+}
diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig
new file mode 100644
index 0000000000..3d26807411
--- /dev/null
+++ b/lib/std/Build/OptionsStep.zig
@@ -0,0 +1,363 @@
+const std = @import("../std.zig");
+const builtin = @import("builtin");
+const fs = std.fs;
+const Step = std.Build.Step;
+const GeneratedFile = std.Build.GeneratedFile;
+const LibExeObjStep = std.Build.LibExeObjStep;
+const FileSource = std.Build.FileSource;
+
+const OptionsStep = @This();
+
+pub const base_id = .options;
+
+step: Step,
+generated_file: GeneratedFile,
+builder: *std.Build,
+
+contents: std.ArrayList(u8),
+artifact_args: std.ArrayList(OptionArtifactArg),
+file_source_args: std.ArrayList(OptionFileSourceArg),
+
+pub fn create(builder: *std.Build) *OptionsStep {
+ const self = builder.allocator.create(OptionsStep) catch unreachable;
+ self.* = .{
+ .builder = builder,
+ .step = Step.init(.options, "options", builder.allocator, make),
+ .generated_file = undefined,
+ .contents = std.ArrayList(u8).init(builder.allocator),
+ .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
+ .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
+ };
+ self.generated_file = .{ .step = &self.step };
+
+ return self;
+}
+
+pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
+ const out = self.contents.writer();
+ switch (T) {
+ []const []const u8 => {
+ out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
+ for (value) |slice| {
+ out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
+ }
+ out.writeAll("};\n") catch unreachable;
+ return;
+ },
+ [:0]const u8 => {
+ out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
+ return;
+ },
+ []const u8 => {
+ out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
+ return;
+ },
+ ?[:0]const u8 => {
+ out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
+ if (value) |payload| {
+ out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
+ } else {
+ out.writeAll("null;\n") catch unreachable;
+ }
+ return;
+ },
+ ?[]const u8 => {
+ out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
+ if (value) |payload| {
+ out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
+ } else {
+ out.writeAll("null;\n") catch unreachable;
+ }
+ return;
+ },
+ std.builtin.Version => {
+ out.print(
+ \\pub const {}: @import("std").builtin.Version = .{{
+ \\ .major = {d},
+ \\ .minor = {d},
+ \\ .patch = {d},
+ \\}};
+ \\
+ , .{
+ std.zig.fmtId(name),
+
+ value.major,
+ value.minor,
+ value.patch,
+ }) catch unreachable;
+ return;
+ },
+ std.SemanticVersion => {
+ out.print(
+ \\pub const {}: @import("std").SemanticVersion = .{{
+ \\ .major = {d},
+ \\ .minor = {d},
+ \\ .patch = {d},
+ \\
+ , .{
+ std.zig.fmtId(name),
+
+ value.major,
+ value.minor,
+ value.patch,
+ }) catch unreachable;
+ if (value.pre) |some| {
+ out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
+ }
+ if (value.build) |some| {
+ out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
+ }
+ out.writeAll("};\n") catch unreachable;
+ return;
+ },
+ else => {},
+ }
+ switch (@typeInfo(T)) {
+ .Enum => |enum_info| {
+ out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
+ inline for (enum_info.fields) |field| {
+ out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
+ }
+ out.writeAll("};\n") catch unreachable;
+ out.print("pub const {}: {s} = {s}.{s};\n", .{
+ std.zig.fmtId(name),
+ std.zig.fmtId(@typeName(T)),
+ std.zig.fmtId(@typeName(T)),
+ std.zig.fmtId(@tagName(value)),
+ }) catch unreachable;
+ return;
+ },
+ else => {},
+ }
+ out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable;
+ printLiteral(out, value, 0) catch unreachable;
+ out.writeAll(";\n") catch unreachable;
+}
+
+// TODO: non-recursive?
+fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
+ const T = @TypeOf(val);
+ switch (@typeInfo(T)) {
+ .Array => {
+ try out.print("{s} {{\n", .{@typeName(T)});
+ for (val) |item| {
+ try out.writeByteNTimes(' ', indent + 4);
+ try printLiteral(out, item, indent + 4);
+ try out.writeAll(",\n");
+ }
+ try out.writeByteNTimes(' ', indent);
+ try out.writeAll("}");
+ },
+ .Pointer => |p| {
+ if (p.size != .Slice) {
+ @compileError("Non-slice pointers are not yet supported in build options");
+ }
+ try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
+ for (val) |item| {
+ try out.writeByteNTimes(' ', indent + 4);
+ try printLiteral(out, item, indent + 4);
+ try out.writeAll(",\n");
+ }
+ try out.writeByteNTimes(' ', indent);
+ try out.writeAll("}");
+ },
+ .Optional => {
+ if (val) |inner| {
+ return printLiteral(out, inner, indent);
+ } else {
+ return out.writeAll("null");
+ }
+ },
+ .Void,
+ .Bool,
+ .Int,
+ .ComptimeInt,
+ .Float,
+ .Null,
+ => try out.print("{any}", .{val}),
+ else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
+ }
+}
+
+/// The value is the path in the cache dir.
+/// Adds a dependency automatically.
+pub fn addOptionFileSource(
+ self: *OptionsStep,
+ name: []const u8,
+ source: FileSource,
+) void {
+ self.file_source_args.append(.{
+ .name = name,
+ .source = source.dupe(self.builder),
+ }) catch unreachable;
+ source.addStepDependencies(&self.step);
+}
+
+/// The value is the path in the cache dir.
+/// Adds a dependency automatically.
+pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
+ self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
+ self.step.dependOn(&artifact.step);
+}
+
+pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg {
+ return .{ .name = package_name, .source = self.getSource() };
+}
+
+pub fn getSource(self: *OptionsStep) FileSource {
+ return .{ .generated = &self.generated_file };
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(OptionsStep, "step", step);
+
+ for (self.artifact_args.items) |item| {
+ self.addOption(
+ []const u8,
+ item.name,
+ self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
+ );
+ }
+
+ for (self.file_source_args.items) |item| {
+ self.addOption(
+ []const u8,
+ item.name,
+ item.source.getPath(self.builder),
+ );
+ }
+
+ const options_directory = self.builder.pathFromRoot(
+ try fs.path.join(
+ self.builder.allocator,
+ &[_][]const u8{ self.builder.cache_root, "options" },
+ ),
+ );
+
+ try fs.cwd().makePath(options_directory);
+
+ const options_file = try fs.path.join(
+ self.builder.allocator,
+ &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
+ );
+
+ try fs.cwd().writeFile(options_file, self.contents.items);
+
+ self.generated_file.path = options_file;
+}
+
+fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
+ // This implementation is copied from `WriteFileStep.make`
+
+ var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
+
+ // Random bytes to make OptionsStep unique. Refresh this with
+ // new random bytes when OptionsStep implementation is modified
+ // in a non-backwards-compatible way.
+ hash.update("yL0Ya4KkmcCjBlP8");
+ hash.update(self.contents.items);
+
+ var digest: [48]u8 = undefined;
+ hash.final(&digest);
+ var hash_basename: [64]u8 = undefined;
+ _ = fs.base64_encoder.encode(&hash_basename, &digest);
+ return hash_basename;
+}
+
+const OptionArtifactArg = struct {
+ name: []const u8,
+ artifact: *LibExeObjStep,
+};
+
+const OptionFileSourceArg = struct {
+ name: []const u8,
+ source: FileSource,
+};
+
+test "OptionsStep" {
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+ var builder = try std.Build.create(
+ arena.allocator(),
+ "test",
+ "test",
+ "test",
+ "test",
+ );
+ defer builder.destroy();
+
+ const options = builder.addOptions();
+
+ // TODO this regressed at some point
+ //const KeywordEnum = enum {
+ // @"0.8.1",
+ //};
+
+ const nested_array = [2][2]u16{
+ [2]u16{ 300, 200 },
+ [2]u16{ 300, 200 },
+ };
+ const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
+
+ options.addOption(usize, "option1", 1);
+ options.addOption(?usize, "option2", null);
+ options.addOption(?usize, "option3", 3);
+ options.addOption(comptime_int, "option4", 4);
+ options.addOption([]const u8, "string", "zigisthebest");
+ options.addOption(?[]const u8, "optional_string", null);
+ options.addOption([2][2]u16, "nested_array", nested_array);
+ options.addOption([]const []const u16, "nested_slice", nested_slice);
+ //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
+ options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
+ options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
+
+ try std.testing.expectEqualStrings(
+ \\pub const option1: usize = 1;
+ \\pub const option2: ?usize = null;
+ \\pub const option3: ?usize = 3;
+ \\pub const option4: comptime_int = 4;
+ \\pub const string: []const u8 = "zigisthebest";
+ \\pub const optional_string: ?[]const u8 = null;
+ \\pub const nested_array: [2][2]u16 = [2][2]u16 {
+ \\ [2]u16 {
+ \\ 300,
+ \\ 200,
+ \\ },
+ \\ [2]u16 {
+ \\ 300,
+ \\ 200,
+ \\ },
+ \\};
+ \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
+ \\ &[_]u16 {
+ \\ 300,
+ \\ 200,
+ \\ },
+ \\ &[_]u16 {
+ \\ 300,
+ \\ 200,
+ \\ },
+ \\};
+ //\\pub const KeywordEnum = enum {
+ //\\ @"0.8.1",
+ //\\};
+ //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
+ \\pub const version: @import("std").builtin.Version = .{
+ \\ .major = 0,
+ \\ .minor = 1,
+ \\ .patch = 2,
+ \\};
+ \\pub const semantic_version: @import("std").SemanticVersion = .{
+ \\ .major = 0,
+ \\ .minor = 1,
+ \\ .patch = 2,
+ \\ .pre = "foo",
+ \\ .build = "bar",
+ \\};
+ \\
+ , options.contents.items);
+
+ _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
+}
diff --git a/lib/std/Build/RemoveDirStep.zig b/lib/std/Build/RemoveDirStep.zig
new file mode 100644
index 0000000000..f3b71dcec1
--- /dev/null
+++ b/lib/std/Build/RemoveDirStep.zig
@@ -0,0 +1,29 @@
+const std = @import("../std.zig");
+const log = std.log;
+const fs = std.fs;
+const Step = std.Build.Step;
+const RemoveDirStep = @This();
+
+pub const base_id = .remove_dir;
+
+step: Step,
+builder: *std.Build,
+dir_path: []const u8,
+
+pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {
+ return RemoveDirStep{
+ .builder = builder,
+ .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
+ .dir_path = builder.dupePath(dir_path),
+ };
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(RemoveDirStep, "step", step);
+
+ const full_path = self.builder.pathFromRoot(self.dir_path);
+ fs.cwd().deleteTree(full_path) catch |err| {
+ log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
+ return err;
+ };
+}
diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig
new file mode 100644
index 0000000000..83e4f7f66c
--- /dev/null
+++ b/lib/std/Build/RunStep.zig
@@ -0,0 +1,376 @@
+const std = @import("../std.zig");
+const builtin = @import("builtin");
+const Step = std.Build.Step;
+const LibExeObjStep = std.Build.LibExeObjStep;
+const WriteFileStep = std.Build.WriteFileStep;
+const fs = std.fs;
+const mem = std.mem;
+const process = std.process;
+const ArrayList = std.ArrayList;
+const EnvMap = process.EnvMap;
+const Allocator = mem.Allocator;
+const ExecError = std.Build.ExecError;
+
+const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
+
+const RunStep = @This();
+
+pub const base_id: Step.Id = .run;
+
+step: Step,
+builder: *std.Build,
+
+/// See also addArg and addArgs to modifying this directly
+argv: ArrayList(Arg),
+
+/// Set this to modify the current working directory
+cwd: ?[]const u8,
+
+/// Override this field to modify the environment, or use setEnvironmentVariable
+env_map: ?*EnvMap,
+
+stdout_action: StdIoAction = .inherit,
+stderr_action: StdIoAction = .inherit,
+
+stdin_behavior: std.ChildProcess.StdIo = .Inherit,
+
+/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
+expected_exit_code: ?u8 = 0,
+
+/// Print the command before running it
+print: bool,
+
+pub const StdIoAction = union(enum) {
+ inherit,
+ ignore,
+ expect_exact: []const u8,
+ expect_matches: []const []const u8,
+};
+
+pub const Arg = union(enum) {
+ artifact: *LibExeObjStep,
+ file_source: std.Build.FileSource,
+ bytes: []u8,
+};
+
+pub fn create(builder: *std.Build, name: []const u8) *RunStep {
+ const self = builder.allocator.create(RunStep) catch unreachable;
+ self.* = RunStep{
+ .builder = builder,
+ .step = Step.init(base_id, name, builder.allocator, make),
+ .argv = ArrayList(Arg).init(builder.allocator),
+ .cwd = null,
+ .env_map = null,
+ .print = builder.verbose,
+ };
+ return self;
+}
+
+pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
+ self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
+ self.step.dependOn(&artifact.step);
+}
+
+pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
+ self.argv.append(Arg{
+ .file_source = file_source.dupe(self.builder),
+ }) catch unreachable;
+ file_source.addStepDependencies(&self.step);
+}
+
+pub fn addArg(self: *RunStep, arg: []const u8) void {
+ self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
+}
+
+pub fn addArgs(self: *RunStep, args: []const []const u8) void {
+ for (args) |arg| {
+ self.addArg(arg);
+ }
+}
+
+pub fn clearEnvironment(self: *RunStep) void {
+ const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable;
+ new_env_map.* = EnvMap.init(self.builder.allocator);
+ self.env_map = new_env_map;
+}
+
+pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
+ addPathDirInternal(&self.step, self.builder, search_path);
+}
+
+/// For internal use only, users of `RunStep` should use `addPathDir` directly.
+pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void {
+ const env_map = getEnvMapInternal(step, builder.allocator);
+
+ const key = "PATH";
+ var prev_path = env_map.get(key);
+
+ if (prev_path) |pp| {
+ const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
+ env_map.put(key, new_path) catch unreachable;
+ } else {
+ env_map.put(key, builder.dupePath(search_path)) catch unreachable;
+ }
+}
+
+pub fn getEnvMap(self: *RunStep) *EnvMap {
+ return getEnvMapInternal(&self.step, self.builder.allocator);
+}
+
+fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
+ const maybe_env_map = switch (step.id) {
+ .run => step.cast(RunStep).?.env_map,
+ .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
+ else => unreachable,
+ };
+ return maybe_env_map orelse {
+ const env_map = allocator.create(EnvMap) catch unreachable;
+ env_map.* = process.getEnvMap(allocator) catch unreachable;
+ switch (step.id) {
+ .run => step.cast(RunStep).?.env_map = env_map,
+ .emulatable_run => step.cast(RunStep).?.env_map = env_map,
+ else => unreachable,
+ }
+ return env_map;
+ };
+}
+
+pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
+ const env_map = self.getEnvMap();
+ env_map.put(
+ self.builder.dupe(key),
+ self.builder.dupe(value),
+ ) catch unreachable;
+}
+
+pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
+ self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
+}
+
+pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
+ self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
+}
+
+fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
+ return switch (action) {
+ .ignore => .Ignore,
+ .inherit => .Inherit,
+ .expect_exact, .expect_matches => .Pipe,
+ };
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(RunStep, "step", step);
+
+ var argv_list = ArrayList([]const u8).init(self.builder.allocator);
+ for (self.argv.items) |arg| {
+ switch (arg) {
+ .bytes => |bytes| try argv_list.append(bytes),
+ .file_source => |file| try argv_list.append(file.getPath(self.builder)),
+ .artifact => |artifact| {
+ if (artifact.target.isWindows()) {
+ // On Windows we don't have rpaths so we have to add .dll search paths to PATH
+ self.addPathForDynLibs(artifact);
+ }
+ const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
+ try argv_list.append(executable_path);
+ },
+ }
+ }
+
+ try runCommand(
+ argv_list.items,
+ self.builder,
+ self.expected_exit_code,
+ self.stdout_action,
+ self.stderr_action,
+ self.stdin_behavior,
+ self.env_map,
+ self.cwd,
+ self.print,
+ );
+}
+
+pub fn runCommand(
+ argv: []const []const u8,
+ builder: *std.Build,
+ expected_exit_code: ?u8,
+ stdout_action: StdIoAction,
+ stderr_action: StdIoAction,
+ stdin_behavior: std.ChildProcess.StdIo,
+ env_map: ?*EnvMap,
+ maybe_cwd: ?[]const u8,
+ print: bool,
+) !void {
+ const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
+
+ if (!std.process.can_spawn) {
+ const cmd = try std.mem.join(builder.allocator, " ", argv);
+ std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
+ builder.allocator.free(cmd);
+ return ExecError.ExecNotSupported;
+ }
+
+ var child = std.ChildProcess.init(argv, builder.allocator);
+ child.cwd = cwd;
+ child.env_map = env_map orelse builder.env_map;
+
+ child.stdin_behavior = stdin_behavior;
+ child.stdout_behavior = stdIoActionToBehavior(stdout_action);
+ child.stderr_behavior = stdIoActionToBehavior(stderr_action);
+
+ if (print)
+ printCmd(cwd, argv);
+
+ child.spawn() catch |err| {
+ std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
+ return err;
+ };
+
+ // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
+
+ var stdout: ?[]const u8 = null;
+ defer if (stdout) |s| builder.allocator.free(s);
+
+ switch (stdout_action) {
+ .expect_exact, .expect_matches => {
+ stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
+ },
+ .inherit, .ignore => {},
+ }
+
+ var stderr: ?[]const u8 = null;
+ defer if (stderr) |s| builder.allocator.free(s);
+
+ switch (stderr_action) {
+ .expect_exact, .expect_matches => {
+ stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
+ },
+ .inherit, .ignore => {},
+ }
+
+ const term = child.wait() catch |err| {
+ std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
+ return err;
+ };
+
+ switch (term) {
+ .Exited => |code| blk: {
+ const expected_code = expected_exit_code orelse break :blk;
+
+ if (code != expected_code) {
+ if (builder.prominent_compile_errors) {
+ std.debug.print("Run step exited with error code {} (expected {})\n", .{
+ code,
+ expected_code,
+ });
+ } else {
+ std.debug.print("The following command exited with error code {} (expected {}):\n", .{
+ code,
+ expected_code,
+ });
+ printCmd(cwd, argv);
+ }
+
+ return error.UnexpectedExitCode;
+ }
+ },
+ else => {
+ std.debug.print("The following command terminated unexpectedly:\n", .{});
+ printCmd(cwd, argv);
+ return error.UncleanExit;
+ },
+ }
+
+ switch (stderr_action) {
+ .inherit, .ignore => {},
+ .expect_exact => |expected_bytes| {
+ if (!mem.eql(u8, expected_bytes, stderr.?)) {
+ std.debug.print(
+ \\
+ \\========= Expected this stderr: =========
+ \\{s}
+ \\========= But found: ====================
+ \\{s}
+ \\
+ , .{ expected_bytes, stderr.? });
+ printCmd(cwd, argv);
+ return error.TestFailed;
+ }
+ },
+ .expect_matches => |matches| for (matches) |match| {
+ if (mem.indexOf(u8, stderr.?, match) == null) {
+ std.debug.print(
+ \\
+ \\========= Expected to find in stderr: =========
+ \\{s}
+ \\========= But stderr does not contain it: =====
+ \\{s}
+ \\
+ , .{ match, stderr.? });
+ printCmd(cwd, argv);
+ return error.TestFailed;
+ }
+ },
+ }
+
+ switch (stdout_action) {
+ .inherit, .ignore => {},
+ .expect_exact => |expected_bytes| {
+ if (!mem.eql(u8, expected_bytes, stdout.?)) {
+ std.debug.print(
+ \\
+ \\========= Expected this stdout: =========
+ \\{s}
+ \\========= But found: ====================
+ \\{s}
+ \\
+ , .{ expected_bytes, stdout.? });
+ printCmd(cwd, argv);
+ return error.TestFailed;
+ }
+ },
+ .expect_matches => |matches| for (matches) |match| {
+ if (mem.indexOf(u8, stdout.?, match) == null) {
+ std.debug.print(
+ \\
+ \\========= Expected to find in stdout: =========
+ \\{s}
+ \\========= But stdout does not contain it: =====
+ \\{s}
+ \\
+ , .{ match, stdout.? });
+ printCmd(cwd, argv);
+ return error.TestFailed;
+ }
+ },
+ }
+}
+
+fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
+ if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
+ for (argv) |arg| {
+ std.debug.print("{s} ", .{arg});
+ }
+ std.debug.print("\n", .{});
+}
+
+fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
+ addPathForDynLibsInternal(&self.step, self.builder, artifact);
+}
+
+/// This should only be used for internal usage, this is called automatically
+/// for the user.
+pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *LibExeObjStep) void {
+ for (artifact.link_objects.items) |link_object| {
+ switch (link_object) {
+ .other_step => |other| {
+ if (other.target.isWindows() and other.isDynamicLibrary()) {
+ addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
+ addPathForDynLibsInternal(step, builder, other);
+ }
+ },
+ else => {},
+ }
+ }
+}
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
new file mode 100644
index 0000000000..86d6645c29
--- /dev/null
+++ b/lib/std/Build/Step.zig
@@ -0,0 +1,97 @@
+id: Id,
+name: []const u8,
+makeFn: *const fn (self: *Step) anyerror!void,
+dependencies: std.ArrayList(*Step),
+loop_flag: bool,
+done_flag: bool,
+
+pub const Id = enum {
+ top_level,
+ lib_exe_obj,
+ install_artifact,
+ install_file,
+ install_dir,
+ log,
+ remove_dir,
+ fmt,
+ translate_c,
+ write_file,
+ run,
+ emulatable_run,
+ check_file,
+ check_object,
+ config_header,
+ install_raw,
+ options,
+ custom,
+
+ pub fn Type(comptime id: Id) type {
+ return switch (id) {
+ .top_level => Build.TopLevelStep,
+ .lib_exe_obj => Build.LibExeObjStep,
+ .install_artifact => Build.InstallArtifactStep,
+ .install_file => Build.InstallFileStep,
+ .install_dir => Build.InstallDirStep,
+ .log => Build.LogStep,
+ .remove_dir => Build.RemoveDirStep,
+ .fmt => Build.FmtStep,
+ .translate_c => Build.TranslateCStep,
+ .write_file => Build.WriteFileStep,
+ .run => Build.RunStep,
+ .emulatable_run => Build.EmulatableRunStep,
+ .check_file => Build.CheckFileStep,
+ .check_object => Build.CheckObjectStep,
+ .config_header => Build.ConfigHeaderStep,
+ .install_raw => Build.InstallRawStep,
+ .options => Build.OptionsStep,
+ .custom => @compileError("no type available for custom step"),
+ };
+ }
+};
+
+pub fn init(
+ id: Id,
+ name: []const u8,
+ allocator: Allocator,
+ makeFn: *const fn (self: *Step) anyerror!void,
+) Step {
+ return Step{
+ .id = id,
+ .name = allocator.dupe(u8, name) catch unreachable,
+ .makeFn = makeFn,
+ .dependencies = std.ArrayList(*Step).init(allocator),
+ .loop_flag = false,
+ .done_flag = false,
+ };
+}
+
+pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
+ return init(id, name, allocator, makeNoOp);
+}
+
+pub fn make(self: *Step) !void {
+ if (self.done_flag) return;
+
+ try self.makeFn(self);
+ self.done_flag = true;
+}
+
+pub fn dependOn(self: *Step, other: *Step) void {
+ self.dependencies.append(other) catch unreachable;
+}
+
+fn makeNoOp(self: *Step) anyerror!void {
+ _ = self;
+}
+
+pub fn cast(step: *Step, comptime T: type) ?*T {
+ if (step.id == T.base_id) {
+ return @fieldParentPtr(T, "step", step);
+ }
+ return null;
+}
+
+const Step = @This();
+const std = @import("../std.zig");
+const Build = std.Build;
+const Allocator = std.mem.Allocator;
diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig
new file mode 100644
index 0000000000..5404747846
--- /dev/null
+++ b/lib/std/Build/TranslateCStep.zig
@@ -0,0 +1,136 @@
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const LibExeObjStep = std.Build.LibExeObjStep;
+const CheckFileStep = std.Build.CheckFileStep;
+const fs = std.fs;
+const mem = std.mem;
+const CrossTarget = std.zig.CrossTarget;
+
+const TranslateCStep = @This();
+
+pub const base_id = .translate_c;
+
+step: Step,
+builder: *std.Build,
+source: std.Build.FileSource,
+include_dirs: std.ArrayList([]const u8),
+c_macros: std.ArrayList([]const u8),
+output_dir: ?[]const u8,
+out_basename: []const u8,
+target: CrossTarget,
+optimize: std.builtin.OptimizeMode,
+output_file: std.Build.GeneratedFile,
+
+pub const Options = struct {
+ source_file: std.Build.FileSource,
+ target: CrossTarget,
+ optimize: std.builtin.OptimizeMode,
+};
+
+pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
+ const self = builder.allocator.create(TranslateCStep) catch unreachable;
+ const source = options.source_file.dupe(builder);
+ self.* = TranslateCStep{
+ .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
+ .builder = builder,
+ .source = source,
+ .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
+ .c_macros = std.ArrayList([]const u8).init(builder.allocator),
+ .output_dir = null,
+ .out_basename = undefined,
+ .target = options.target,
+ .optimize = options.optimize,
+ .output_file = std.Build.GeneratedFile{ .step = &self.step },
+ };
+ source.addStepDependencies(&self.step);
+ return self;
+}
+
+pub const AddExecutableOptions = struct {
+ name: ?[]const u8 = null,
+ version: ?std.builtin.Version = null,
+ target: ?CrossTarget = null,
+ optimize: ?std.builtin.Mode = null,
+ linkage: ?LibExeObjStep.Linkage = null,
+};
+
+/// Creates a step to build an executable from the translated source.
+pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *LibExeObjStep {
+ return self.builder.addExecutable(.{
+ .root_source_file = .{ .generated = &self.output_file },
+ .name = options.name orelse "translated_c",
+ .version = options.version,
+ .target = options.target orelse self.target,
+ .optimize = options.optimize orelse self.optimize,
+ .linkage = options.linkage,
+ });
+}
+
+pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
+ self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
+}
+
+pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
+ return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
+}
+
+/// If the value is omitted, it is set to 1.
+/// `name` and `value` need not live longer than the function call.
+pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
+ const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
+ self.c_macros.append(macro) catch unreachable;
+}
+
+/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
+pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
+ self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(TranslateCStep, "step", step);
+
+ var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
+ try argv_list.append(self.builder.zig_exe);
+ try argv_list.append("translate-c");
+ try argv_list.append("-lc");
+
+ try argv_list.append("--enable-cache");
+
+ if (!self.target.isNative()) {
+ try argv_list.append("-target");
+ try argv_list.append(try self.target.zigTriple(self.builder.allocator));
+ }
+
+ switch (self.optimize) {
+ .Debug => {}, // Skip since it's the default.
+ else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
+ }
+
+ for (self.include_dirs.items) |include_dir| {
+ try argv_list.append("-I");
+ try argv_list.append(include_dir);
+ }
+
+ for (self.c_macros.items) |c_macro| {
+ try argv_list.append("-D");
+ try argv_list.append(c_macro);
+ }
+
+ try argv_list.append(self.source.getPath(self.builder));
+
+ const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
+ const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
+
+ self.out_basename = fs.path.basename(output_path);
+ if (self.output_dir) |output_dir| {
+ const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
+ try self.builder.updateFile(output_path, full_dest);
+ } else {
+ self.output_dir = fs.path.dirname(output_path).?;
+ }
+
+ self.output_file.path = fs.path.join(
+ self.builder.allocator,
+ &[_][]const u8{ self.output_dir.?, self.out_basename },
+ ) catch unreachable;
+}
diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig
new file mode 100644
index 0000000000..59ac568221
--- /dev/null
+++ b/lib/std/Build/WriteFileStep.zig
@@ -0,0 +1,115 @@
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const fs = std.fs;
+const ArrayList = std.ArrayList;
+
+const WriteFileStep = @This();
+
+pub const base_id = .write_file;
+
+step: Step,
+builder: *std.Build,
+output_dir: []const u8,
+files: std.TailQueue(File),
+
+pub const File = struct {
+ source: std.Build.GeneratedFile,
+ basename: []const u8,
+ bytes: []const u8,
+};
+
+pub fn init(builder: *std.Build) WriteFileStep {
+ return WriteFileStep{
+ .builder = builder,
+ .step = Step.init(.write_file, "writefile", builder.allocator, make),
+ .files = .{},
+ .output_dir = undefined,
+ };
+}
+
+pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
+ const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
+ node.* = .{
+ .data = .{
+ .source = std.Build.GeneratedFile{ .step = &self.step },
+ .basename = self.builder.dupePath(basename),
+ .bytes = self.builder.dupe(bytes),
+ },
+ };
+
+ self.files.append(node);
+}
+
+/// Gets a file source for the given basename. If the file does not exist, returns `null`.
+pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource {
+ var it = step.files.first;
+ while (it) |node| : (it = node.next) {
+ if (std.mem.eql(u8, node.data.basename, basename))
+ return std.Build.FileSource{ .generated = &node.data.source };
+ }
+ return null;
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(WriteFileStep, "step", step);
+
+ // The cache is used here not really as a way to speed things up - because writing
+ // the data to a file would probably be very fast - but as a way to find a canonical
+ // location to put build artifacts.
+
+ // If, for example, a hard-coded path was used as the location to put WriteFileStep
+ // files, then two WriteFileSteps executing in parallel might clobber each other.
+
+ // TODO port the cache system from the compiler to zig std lib. Until then
+ // we directly construct the path, and no "cache hit" detection happens;
+ // the files are always written.
+ // Note there is similar code over in ConfigHeaderStep.
+ const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
+ // Random bytes to make WriteFileStep unique. Refresh this with
+ // new random bytes when WriteFileStep implementation is modified
+ // in a non-backwards-compatible way.
+ var hash = Hasher.init("eagVR1dYXoE7ARDP");
+
+ {
+ var it = self.files.first;
+ while (it) |node| : (it = node.next) {
+ hash.update(node.data.basename);
+ hash.update(node.data.bytes);
+ hash.update("|");
+ }
+ }
+ var digest: [16]u8 = undefined;
+ hash.final(&digest);
+ var hash_basename: [digest.len * 2]u8 = undefined;
+ _ = std.fmt.bufPrint(
+ &hash_basename,
+ "{s}",
+ .{std.fmt.fmtSliceHexLower(&digest)},
+ ) catch unreachable;
+
+ self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
+ self.builder.cache_root, "o", &hash_basename,
+ });
+ var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
+ std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
+ return err;
+ };
+ defer dir.close();
+ {
+ var it = self.files.first;
+ while (it) |node| : (it = node.next) {
+ dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
+ std.debug.print("unable to write {s} into {s}: {s}\n", .{
+ node.data.basename,
+ self.output_dir,
+ @errorName(err),
+ });
+ return err;
+ };
+ node.data.source.path = fs.path.join(
+ self.builder.allocator,
+ &[_][]const u8{ self.output_dir, node.data.basename },
+ ) catch unreachable;
+ }
+ }
+}
diff --git a/lib/std/build.zig b/lib/std/build.zig
deleted file mode 100644
index 4ee00a4710..0000000000
--- a/lib/std/build.zig
+++ /dev/null
@@ -1,1863 +0,0 @@
-const std = @import("std.zig");
-const builtin = @import("builtin");
-const io = std.io;
-const fs = std.fs;
-const mem = std.mem;
-const debug = std.debug;
-const panic = std.debug.panic;
-const assert = debug.assert;
-const log = std.log;
-const ArrayList = std.ArrayList;
-const StringHashMap = std.StringHashMap;
-const Allocator = mem.Allocator;
-const process = std.process;
-const EnvMap = std.process.EnvMap;
-const fmt_lib = std.fmt;
-const File = std.fs.File;
-const CrossTarget = std.zig.CrossTarget;
-const NativeTargetInfo = std.zig.system.NativeTargetInfo;
-const Sha256 = std.crypto.hash.sha2.Sha256;
-const ThisModule = @This();
-
-pub const CheckFileStep = @import("build/CheckFileStep.zig");
-pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
-pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig");
-pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig");
-pub const FmtStep = @import("build/FmtStep.zig");
-pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig");
-pub const InstallDirStep = @import("build/InstallDirStep.zig");
-pub const InstallFileStep = @import("build/InstallFileStep.zig");
-pub const InstallRawStep = @import("build/InstallRawStep.zig");
-pub const LibExeObjStep = @import("build/LibExeObjStep.zig");
-pub const LogStep = @import("build/LogStep.zig");
-pub const OptionsStep = @import("build/OptionsStep.zig");
-pub const RemoveDirStep = @import("build/RemoveDirStep.zig");
-pub const RunStep = @import("build/RunStep.zig");
-pub const TranslateCStep = @import("build/TranslateCStep.zig");
-pub const WriteFileStep = @import("build/WriteFileStep.zig");
-
-pub const Builder = struct {
- install_tls: TopLevelStep,
- uninstall_tls: TopLevelStep,
- allocator: Allocator,
- user_input_options: UserInputOptionsMap,
- available_options_map: AvailableOptionsMap,
- available_options_list: ArrayList(AvailableOption),
- verbose: bool,
- verbose_link: bool,
- verbose_cc: bool,
- verbose_air: bool,
- verbose_llvm_ir: bool,
- verbose_cimport: bool,
- verbose_llvm_cpu_features: bool,
- /// The purpose of executing the command is for a human to read compile errors from the terminal
- prominent_compile_errors: bool,
- color: enum { auto, on, off } = .auto,
- reference_trace: ?u32 = null,
- invalid_user_input: bool,
- zig_exe: []const u8,
- default_step: *Step,
- env_map: *EnvMap,
- top_level_steps: ArrayList(*TopLevelStep),
- install_prefix: []const u8,
- dest_dir: ?[]const u8,
- lib_dir: []const u8,
- exe_dir: []const u8,
- h_dir: []const u8,
- install_path: []const u8,
- sysroot: ?[]const u8 = null,
- search_prefixes: ArrayList([]const u8),
- libc_file: ?[]const u8 = null,
- installed_files: ArrayList(InstalledFile),
- /// Path to the directory containing build.zig.
- build_root: []const u8,
- cache_root: []const u8,
- global_cache_root: []const u8,
- /// zig lib dir
- override_lib_dir: ?[]const u8,
- vcpkg_root: VcpkgRoot = .unattempted,
- pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
- args: ?[][]const u8 = null,
- debug_log_scopes: []const []const u8 = &.{},
- debug_compile_errors: bool = false,
-
- /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
- enable_darling: bool = false,
- /// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
- enable_qemu: bool = false,
- /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
- enable_rosetta: bool = false,
- /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
- enable_wasmtime: bool = false,
- /// Use system Wine installation to run cross compiled Windows build artifacts.
- enable_wine: bool = false,
- /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
- /// this will be the directory $glibc-build-dir/install/glibcs
- /// Given the example of the aarch64 target, this is the directory
- /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
- glibc_runtimes_dir: ?[]const u8 = null,
-
- /// Information about the native target. Computed before build() is invoked.
- host: NativeTargetInfo,
-
- dep_prefix: []const u8 = "",
-
- pub const ExecError = error{
- ReadFailure,
- ExitCodeFailure,
- ProcessTerminated,
- ExecNotSupported,
- } || std.ChildProcess.SpawnError;
-
- pub const PkgConfigError = error{
- PkgConfigCrashed,
- PkgConfigFailed,
- PkgConfigNotInstalled,
- PkgConfigInvalidOutput,
- };
-
- pub const PkgConfigPkg = struct {
- name: []const u8,
- desc: []const u8,
- };
-
- pub const CStd = enum {
- C89,
- C99,
- C11,
- };
-
- const UserInputOptionsMap = StringHashMap(UserInputOption);
- const AvailableOptionsMap = StringHashMap(AvailableOption);
-
- const AvailableOption = struct {
- name: []const u8,
- type_id: TypeId,
- description: []const u8,
- /// If the `type_id` is `enum` this provides the list of enum options
- enum_options: ?[]const []const u8,
- };
-
- const UserInputOption = struct {
- name: []const u8,
- value: UserValue,
- used: bool,
- };
-
- const UserValue = union(enum) {
- flag: void,
- scalar: []const u8,
- list: ArrayList([]const u8),
- map: StringHashMap(*const UserValue),
- };
-
- const TypeId = enum {
- bool,
- int,
- float,
- @"enum",
- string,
- list,
- };
-
- const TopLevelStep = struct {
- pub const base_id = .top_level;
-
- step: Step,
- description: []const u8,
- };
-
- pub const DirList = struct {
- lib_dir: ?[]const u8 = null,
- exe_dir: ?[]const u8 = null,
- include_dir: ?[]const u8 = null,
- };
-
- pub fn create(
- allocator: Allocator,
- zig_exe: []const u8,
- build_root: []const u8,
- cache_root: []const u8,
- global_cache_root: []const u8,
- ) !*Builder {
- const env_map = try allocator.create(EnvMap);
- env_map.* = try process.getEnvMap(allocator);
-
- const host = try NativeTargetInfo.detect(.{});
-
- const self = try allocator.create(Builder);
- self.* = Builder{
- .zig_exe = zig_exe,
- .build_root = build_root,
- .cache_root = try fs.path.relative(allocator, build_root, cache_root),
- .global_cache_root = global_cache_root,
- .verbose = false,
- .verbose_link = false,
- .verbose_cc = false,
- .verbose_air = false,
- .verbose_llvm_ir = false,
- .verbose_cimport = false,
- .verbose_llvm_cpu_features = false,
- .prominent_compile_errors = false,
- .invalid_user_input = false,
- .allocator = allocator,
- .user_input_options = UserInputOptionsMap.init(allocator),
- .available_options_map = AvailableOptionsMap.init(allocator),
- .available_options_list = ArrayList(AvailableOption).init(allocator),
- .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
- .default_step = undefined,
- .env_map = env_map,
- .search_prefixes = ArrayList([]const u8).init(allocator),
- .install_prefix = undefined,
- .lib_dir = undefined,
- .exe_dir = undefined,
- .h_dir = undefined,
- .dest_dir = env_map.get("DESTDIR"),
- .installed_files = ArrayList(InstalledFile).init(allocator),
- .install_tls = TopLevelStep{
- .step = Step.initNoOp(.top_level, "install", allocator),
- .description = "Copy build artifacts to prefix path",
- },
- .uninstall_tls = TopLevelStep{
- .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
- .description = "Remove build artifacts from prefix path",
- },
- .override_lib_dir = null,
- .install_path = undefined,
- .args = null,
- .host = host,
- };
- try self.top_level_steps.append(&self.install_tls);
- try self.top_level_steps.append(&self.uninstall_tls);
- self.default_step = &self.install_tls.step;
- return self;
- }
-
- fn createChild(
- parent: *Builder,
- dep_name: []const u8,
- build_root: []const u8,
- args: anytype,
- ) !*Builder {
- const child = try createChildOnly(parent, dep_name, build_root);
- try applyArgs(child, args);
- return child;
- }
-
- fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
- const allocator = parent.allocator;
- const child = try allocator.create(Builder);
- child.* = .{
- .allocator = allocator,
- .install_tls = .{
- .step = Step.initNoOp(.top_level, "install", allocator),
- .description = "Copy build artifacts to prefix path",
- },
- .uninstall_tls = .{
- .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
- .description = "Remove build artifacts from prefix path",
- },
- .user_input_options = UserInputOptionsMap.init(allocator),
- .available_options_map = AvailableOptionsMap.init(allocator),
- .available_options_list = ArrayList(AvailableOption).init(allocator),
- .verbose = parent.verbose,
- .verbose_link = parent.verbose_link,
- .verbose_cc = parent.verbose_cc,
- .verbose_air = parent.verbose_air,
- .verbose_llvm_ir = parent.verbose_llvm_ir,
- .verbose_cimport = parent.verbose_cimport,
- .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
- .prominent_compile_errors = parent.prominent_compile_errors,
- .color = parent.color,
- .reference_trace = parent.reference_trace,
- .invalid_user_input = false,
- .zig_exe = parent.zig_exe,
- .default_step = undefined,
- .env_map = parent.env_map,
- .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
- .install_prefix = undefined,
- .dest_dir = parent.dest_dir,
- .lib_dir = parent.lib_dir,
- .exe_dir = parent.exe_dir,
- .h_dir = parent.h_dir,
- .install_path = parent.install_path,
- .sysroot = parent.sysroot,
- .search_prefixes = ArrayList([]const u8).init(allocator),
- .libc_file = parent.libc_file,
- .installed_files = ArrayList(InstalledFile).init(allocator),
- .build_root = build_root,
- .cache_root = parent.cache_root,
- .global_cache_root = parent.global_cache_root,
- .override_lib_dir = parent.override_lib_dir,
- .debug_log_scopes = parent.debug_log_scopes,
- .debug_compile_errors = parent.debug_compile_errors,
- .enable_darling = parent.enable_darling,
- .enable_qemu = parent.enable_qemu,
- .enable_rosetta = parent.enable_rosetta,
- .enable_wasmtime = parent.enable_wasmtime,
- .enable_wine = parent.enable_wine,
- .glibc_runtimes_dir = parent.glibc_runtimes_dir,
- .host = parent.host,
- .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
- };
- try child.top_level_steps.append(&child.install_tls);
- try child.top_level_steps.append(&child.uninstall_tls);
- child.default_step = &child.install_tls.step;
- return child;
- }
-
- fn applyArgs(b: *Builder, args: anytype) !void {
- inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
- const v = @field(args, field.name);
- const T = @TypeOf(v);
- switch (T) {
- CrossTarget => {
- try b.user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = try v.zigTriple(b.allocator) },
- .used = false,
- });
- try b.user_input_options.put("cpu", .{
- .name = "cpu",
- .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
- .used = false,
- });
- },
- []const u8 => {
- try b.user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = v },
- .used = false,
- });
- },
- else => switch (@typeInfo(T)) {
- .Bool => {
- try b.user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = if (v) "true" else "false" },
- .used = false,
- });
- },
- .Enum => {
- try b.user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = @tagName(v) },
- .used = false,
- });
- },
- .Int => {
- try b.user_input_options.put(field.name, .{
- .name = field.name,
- .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
- .used = false,
- });
- },
- else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
- },
- }
- }
- const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
- // Random bytes to make unique. Refresh this with new random bytes when
- // implementation is modified in a non-backwards-compatible way.
- var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
- hash.update(b.dep_prefix);
- // TODO additionally update the hash with `args`.
-
- var digest: [16]u8 = undefined;
- hash.final(&digest);
- var hash_basename: [digest.len * 2]u8 = undefined;
- _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
- unreachable;
-
- const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
- b.resolveInstallPrefix(install_prefix, .{});
- }
-
- pub fn destroy(self: *Builder) void {
- self.env_map.deinit();
- self.top_level_steps.deinit();
- self.allocator.destroy(self);
- }
-
- /// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
- pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void {
- if (self.dest_dir) |dest_dir| {
- self.install_prefix = install_prefix orelse "/usr";
- self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
- } else {
- self.install_prefix = install_prefix orelse
- (self.pathJoin(&.{ self.build_root, "zig-out" }));
- self.install_path = self.install_prefix;
- }
-
- var lib_list = [_][]const u8{ self.install_path, "lib" };
- var exe_list = [_][]const u8{ self.install_path, "bin" };
- var h_list = [_][]const u8{ self.install_path, "include" };
-
- if (dir_list.lib_dir) |dir| {
- if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
- lib_list[1] = dir;
- }
-
- if (dir_list.exe_dir) |dir| {
- if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
- exe_list[1] = dir;
- }
-
- if (dir_list.include_dir) |dir| {
- if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
- h_list[1] = dir;
- }
-
- self.lib_dir = self.pathJoin(&lib_list);
- self.exe_dir = self.pathJoin(&exe_list);
- self.h_dir = self.pathJoin(&h_list);
- }
-
- pub fn addOptions(self: *Builder) *OptionsStep {
- return OptionsStep.create(self);
- }
-
- pub const ExecutableOptions = struct {
- name: []const u8,
- root_source_file: ?FileSource = null,
- version: ?std.builtin.Version = null,
- target: CrossTarget = .{},
- optimize: std.builtin.Mode = .Debug,
- linkage: ?LibExeObjStep.Linkage = null,
- };
-
- pub fn addExecutable(b: *Builder, options: ExecutableOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
- .name = options.name,
- .root_source_file = options.root_source_file,
- .version = options.version,
- .target = options.target,
- .optimize = options.optimize,
- .kind = .exe,
- .linkage = options.linkage,
- });
- }
-
- pub const ObjectOptions = struct {
- name: []const u8,
- root_source_file: ?FileSource = null,
- target: CrossTarget,
- optimize: std.builtin.Mode,
- };
-
- pub fn addObject(b: *Builder, options: ObjectOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
- .name = options.name,
- .root_source_file = options.root_source_file,
- .target = options.target,
- .optimize = options.optimize,
- .kind = .obj,
- });
- }
-
- pub const SharedLibraryOptions = struct {
- name: []const u8,
- root_source_file: ?FileSource = null,
- version: ?std.builtin.Version = null,
- target: CrossTarget,
- optimize: std.builtin.Mode,
- };
-
- pub fn addSharedLibrary(b: *Builder, options: SharedLibraryOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
- .name = options.name,
- .root_source_file = options.root_source_file,
- .kind = .lib,
- .linkage = .dynamic,
- .version = options.version,
- .target = options.target,
- .optimize = options.optimize,
- });
- }
-
- pub const StaticLibraryOptions = struct {
- name: []const u8,
- root_source_file: ?FileSource = null,
- target: CrossTarget,
- optimize: std.builtin.Mode,
- version: ?std.builtin.Version = null,
- };
-
- pub fn addStaticLibrary(b: *Builder, options: StaticLibraryOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
- .name = options.name,
- .root_source_file = options.root_source_file,
- .kind = .lib,
- .linkage = .static,
- .version = options.version,
- .target = options.target,
- .optimize = options.optimize,
- });
- }
-
- pub const TestOptions = struct {
- name: []const u8 = "test",
- kind: LibExeObjStep.Kind = .@"test",
- root_source_file: FileSource,
- target: CrossTarget = .{},
- optimize: std.builtin.Mode = .Debug,
- version: ?std.builtin.Version = null,
- };
-
- pub fn addTest(b: *Builder, options: TestOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
- .name = options.name,
- .kind = options.kind,
- .root_source_file = options.root_source_file,
- .target = options.target,
- .optimize = options.optimize,
- });
- }
-
- pub const AssemblyOptions = struct {
- name: []const u8,
- source_file: FileSource,
- target: CrossTarget,
- optimize: std.builtin.Mode,
- };
-
- pub fn addAssembly(b: *Builder, options: AssemblyOptions) *LibExeObjStep {
- const obj_step = LibExeObjStep.create(b, .{
- .name = options.name,
- .root_source_file = null,
- .target = options.target,
- .optimize = options.optimize,
- });
- obj_step.addAssemblyFileSource(options.source_file.dupe(b));
- return obj_step;
- }
-
- /// Initializes a RunStep with argv, which must at least have the path to the
- /// executable. More command line arguments can be added with `addArg`,
- /// `addArgs`, and `addArtifactArg`.
- /// Be careful using this function, as it introduces a system dependency.
- /// To run an executable built with zig build, see `LibExeObjStep.run`.
- pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
- assert(argv.len >= 1);
- const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
- run_step.addArgs(argv);
- return run_step;
- }
-
- pub fn addConfigHeader(
- b: *Builder,
- source: FileSource,
- style: ConfigHeaderStep.Style,
- values: anytype,
- ) *ConfigHeaderStep {
- const config_header_step = ConfigHeaderStep.create(b, source, style);
- config_header_step.addValues(values);
- return config_header_step;
- }
-
- /// Allocator.dupe without the need to handle out of memory.
- pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
- return self.allocator.dupe(u8, bytes) catch unreachable;
- }
-
- /// Duplicates an array of strings without the need to handle out of memory.
- pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
- const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
- for (strings) |s, i| {
- array[i] = self.dupe(s);
- }
- return array;
- }
-
- /// Duplicates a path and converts all slashes to the OS's canonical path separator.
- pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
- const the_copy = self.dupe(bytes);
- for (the_copy) |*byte| {
- switch (byte.*) {
- '/', '\\' => byte.* = fs.path.sep,
- else => {},
- }
- }
- return the_copy;
- }
-
- /// Duplicates a package recursively.
- pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
- var the_copy = Pkg{
- .name = self.dupe(package.name),
- .source = package.source.dupe(self),
- };
-
- if (package.dependencies) |dependencies| {
- const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
- the_copy.dependencies = new_dependencies;
-
- for (dependencies) |dep_package, i| {
- new_dependencies[i] = self.dupePkg(dep_package);
- }
- }
- return the_copy;
- }
-
- pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
- const write_file_step = self.addWriteFiles();
- write_file_step.add(file_path, data);
- return write_file_step;
- }
-
- pub fn addWriteFiles(self: *Builder) *WriteFileStep {
- const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
- write_file_step.* = WriteFileStep.init(self);
- return write_file_step;
- }
-
- pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
- const data = self.fmt(format, args);
- const log_step = self.allocator.create(LogStep) catch unreachable;
- log_step.* = LogStep.init(self, data);
- return log_step;
- }
-
- pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
- const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
- remove_dir_step.* = RemoveDirStep.init(self, dir_path);
- return remove_dir_step;
- }
-
- pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
- return FmtStep.create(self, paths);
- }
-
- pub fn addTranslateC(self: *Builder, options: TranslateCStep.Options) *TranslateCStep {
- return TranslateCStep.create(self, options);
- }
-
- pub fn make(self: *Builder, step_names: []const []const u8) !void {
- try self.makePath(self.cache_root);
-
- var wanted_steps = ArrayList(*Step).init(self.allocator);
- defer wanted_steps.deinit();
-
- if (step_names.len == 0) {
- try wanted_steps.append(self.default_step);
- } else {
- for (step_names) |step_name| {
- const s = try self.getTopLevelStepByName(step_name);
- try wanted_steps.append(s);
- }
- }
-
- for (wanted_steps.items) |s| {
- try self.makeOneStep(s);
- }
- }
-
- pub fn getInstallStep(self: *Builder) *Step {
- return &self.install_tls.step;
- }
-
- pub fn getUninstallStep(self: *Builder) *Step {
- return &self.uninstall_tls.step;
- }
-
- fn makeUninstall(uninstall_step: *Step) anyerror!void {
- const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
- const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
-
- for (self.installed_files.items) |installed_file| {
- const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
- if (self.verbose) {
- log.info("rm {s}", .{full_path});
- }
- fs.cwd().deleteTree(full_path) catch {};
- }
-
- // TODO remove empty directories
- }
-
- fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
- if (s.loop_flag) {
- log.err("Dependency loop detected:\n {s}", .{s.name});
- return error.DependencyLoopDetected;
- }
- s.loop_flag = true;
-
- for (s.dependencies.items) |dep| {
- self.makeOneStep(dep) catch |err| {
- if (err == error.DependencyLoopDetected) {
- log.err(" {s}", .{s.name});
- }
- return err;
- };
- }
-
- s.loop_flag = false;
-
- try s.make();
- }
-
- fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
- for (self.top_level_steps.items) |top_level_step| {
- if (mem.eql(u8, top_level_step.step.name, name)) {
- return &top_level_step.step;
- }
- }
- log.err("Cannot run step '{s}' because it does not exist", .{name});
- return error.InvalidStepName;
- }
-
- pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
- const name = self.dupe(name_raw);
- const description = self.dupe(description_raw);
- const type_id = comptime typeToEnum(T);
- const enum_options = if (type_id == .@"enum") blk: {
- const fields = comptime std.meta.fields(T);
- var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
-
- inline for (fields) |field| {
- options.appendAssumeCapacity(field.name);
- }
-
- break :blk options.toOwnedSlice() catch unreachable;
- } else null;
- const available_option = AvailableOption{
- .name = name,
- .type_id = type_id,
- .description = description,
- .enum_options = enum_options,
- };
- if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
- panic("Option '{s}' declared twice", .{name});
- }
- self.available_options_list.append(available_option) catch unreachable;
-
- const option_ptr = self.user_input_options.getPtr(name) orelse return null;
- option_ptr.used = true;
- switch (type_id) {
- .bool => switch (option_ptr.value) {
- .flag => return true,
- .scalar => |s| {
- if (mem.eql(u8, s, "true")) {
- return true;
- } else if (mem.eql(u8, s, "false")) {
- return false;
- } else {
- log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
- self.markInvalidUserInput();
- return null;
- }
- },
- .list, .map => {
- log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
- name, @tagName(option_ptr.value),
- });
- self.markInvalidUserInput();
- return null;
- },
- },
- .int => switch (option_ptr.value) {
- .flag, .list, .map => {
- log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
- name, @tagName(option_ptr.value),
- });
- self.markInvalidUserInput();
- return null;
- },
- .scalar => |s| {
- const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
- error.Overflow => {
- log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
- self.markInvalidUserInput();
- return null;
- },
- else => {
- log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
- self.markInvalidUserInput();
- return null;
- },
- };
- return n;
- },
- },
- .float => switch (option_ptr.value) {
- .flag, .map, .list => {
- log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
- name, @tagName(option_ptr.value),
- });
- self.markInvalidUserInput();
- return null;
- },
- .scalar => |s| {
- const n = std.fmt.parseFloat(T, s) catch {
- log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
- self.markInvalidUserInput();
- return null;
- };
- return n;
- },
- },
- .@"enum" => switch (option_ptr.value) {
- .flag, .map, .list => {
- log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
- name, @tagName(option_ptr.value),
- });
- self.markInvalidUserInput();
- return null;
- },
- .scalar => |s| {
- if (std.meta.stringToEnum(T, s)) |enum_lit| {
- return enum_lit;
- } else {
- log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
- self.markInvalidUserInput();
- return null;
- }
- },
- },
- .string => switch (option_ptr.value) {
- .flag, .list, .map => {
- log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
- name, @tagName(option_ptr.value),
- });
- self.markInvalidUserInput();
- return null;
- },
- .scalar => |s| return s,
- },
- .list => switch (option_ptr.value) {
- .flag, .map => {
- log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
- name, @tagName(option_ptr.value),
- });
- self.markInvalidUserInput();
- return null;
- },
- .scalar => |s| {
- return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
- },
- .list => |lst| return lst.items,
- },
- }
- }
-
- pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
- const step_info = self.allocator.create(TopLevelStep) catch unreachable;
- step_info.* = TopLevelStep{
- .step = Step.initNoOp(.top_level, name, self.allocator),
- .description = self.dupe(description),
- };
- self.top_level_steps.append(step_info) catch unreachable;
- return &step_info.step;
- }
-
- pub const StandardOptimizeOptionOptions = struct {
- preferred_optimize_mode: ?std.builtin.Mode = null,
- };
-
- pub fn standardOptimizeOption(self: *Builder, options: StandardOptimizeOptionOptions) std.builtin.Mode {
- if (options.preferred_optimize_mode) |mode| {
- if (self.option(bool, "release", "optimize for end users") orelse false) {
- return mode;
- } else {
- return .Debug;
- }
- } else {
- return self.option(
- std.builtin.Mode,
- "optimize",
- "prioritize performance, safety, or binary size (-O flag)",
- ) orelse .Debug;
- }
- }
-
- pub const StandardTargetOptionsArgs = struct {
- whitelist: ?[]const CrossTarget = null,
-
- default_target: CrossTarget = CrossTarget{},
- };
-
- /// Exposes standard `zig build` options for choosing a target.
- pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
- const maybe_triple = self.option(
- []const u8,
- "target",
- "The CPU architecture, OS, and ABI to build for",
- );
- const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
-
- if (maybe_triple == null and mcpu == null) {
- return args.default_target;
- }
-
- const triple = maybe_triple orelse "native";
-
- var diags: CrossTarget.ParseOptions.Diagnostics = .{};
- const selected_target = CrossTarget.parse(.{
- .arch_os_abi = triple,
- .cpu_features = mcpu,
- .diagnostics = &diags,
- }) catch |err| switch (err) {
- error.UnknownCpuModel => {
- log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
- diags.cpu_name.?,
- @tagName(diags.arch.?),
- });
- for (diags.arch.?.allCpuModels()) |cpu| {
- log.err(" {s}", .{cpu.name});
- }
- self.markInvalidUserInput();
- return args.default_target;
- },
- error.UnknownCpuFeature => {
- log.err(
- \\Unknown CPU feature: '{s}'
- \\Available CPU features for architecture '{s}':
- \\
- , .{
- diags.unknown_feature_name.?,
- @tagName(diags.arch.?),
- });
- for (diags.arch.?.allFeaturesList()) |feature| {
- log.err(" {s}: {s}", .{ feature.name, feature.description });
- }
- self.markInvalidUserInput();
- return args.default_target;
- },
- error.UnknownOperatingSystem => {
- log.err(
- \\Unknown OS: '{s}'
- \\Available operating systems:
- \\
- , .{diags.os_name.?});
- inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
- log.err(" {s}", .{field.name});
- }
- self.markInvalidUserInput();
- return args.default_target;
- },
- else => |e| {
- log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
- self.markInvalidUserInput();
- return args.default_target;
- },
- };
-
- const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
-
- if (args.whitelist) |list| whitelist_check: {
- // Make sure it's a match of one of the list.
- var mismatch_triple = true;
- var mismatch_cpu_features = true;
- var whitelist_item = CrossTarget{};
- for (list) |t| {
- mismatch_cpu_features = true;
- mismatch_triple = true;
-
- const t_triple = t.zigTriple(self.allocator) catch unreachable;
- if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
- mismatch_triple = false;
- whitelist_item = t;
- if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
- mismatch_cpu_features = false;
- break :whitelist_check;
- } else {
- break;
- }
- }
- }
- if (mismatch_triple) {
- log.err("Chosen target '{s}' does not match one of the supported targets:", .{
- selected_canonicalized_triple,
- });
- for (list) |t| {
- const t_triple = t.zigTriple(self.allocator) catch unreachable;
- log.err(" {s}", .{t_triple});
- }
- } else {
- assert(mismatch_cpu_features);
- const whitelist_cpu = whitelist_item.getCpu();
- const selected_cpu = selected_target.getCpu();
- log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
- selected_cpu.model.name,
- });
- log.err(" Supported feature Set: ", .{});
- const all_features = whitelist_cpu.arch.allFeaturesList();
- var populated_cpu_features = whitelist_cpu.model.features;
- populated_cpu_features.populateDependencies(all_features);
- for (all_features) |feature, i_usize| {
- const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
- const in_cpu_set = populated_cpu_features.isEnabled(i);
- if (in_cpu_set) {
- log.err("{s} ", .{feature.name});
- }
- }
- log.err(" Remove: ", .{});
- for (all_features) |feature, i_usize| {
- const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
- const in_cpu_set = populated_cpu_features.isEnabled(i);
- const in_actual_set = selected_cpu.features.isEnabled(i);
- if (in_actual_set and !in_cpu_set) {
- log.err("{s} ", .{feature.name});
- }
- }
- }
- self.markInvalidUserInput();
- return args.default_target;
- }
-
- return selected_target;
- }
-
- pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
- const name = self.dupe(name_raw);
- const value = self.dupe(value_raw);
- const gop = try self.user_input_options.getOrPut(name);
- if (!gop.found_existing) {
- gop.value_ptr.* = UserInputOption{
- .name = name,
- .value = .{ .scalar = value },
- .used = false,
- };
- return false;
- }
-
- // option already exists
- switch (gop.value_ptr.value) {
- .scalar => |s| {
- // turn it into a list
- var list = ArrayList([]const u8).init(self.allocator);
- list.append(s) catch unreachable;
- list.append(value) catch unreachable;
- self.user_input_options.put(name, .{
- .name = name,
- .value = .{ .list = list },
- .used = false,
- }) catch unreachable;
- },
- .list => |*list| {
- // append to the list
- list.append(value) catch unreachable;
- self.user_input_options.put(name, .{
- .name = name,
- .value = .{ .list = list.* },
- .used = false,
- }) catch unreachable;
- },
- .flag => {
- log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
- return true;
- },
- .map => |*map| {
- _ = map;
- log.warn("TODO maps as command line arguments is not implemented yet.", .{});
- return true;
- },
- }
- return false;
- }
-
- pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
- const name = self.dupe(name_raw);
- const gop = try self.user_input_options.getOrPut(name);
- if (!gop.found_existing) {
- gop.value_ptr.* = .{
- .name = name,
- .value = .{ .flag = {} },
- .used = false,
- };
- return false;
- }
-
- // option already exists
- switch (gop.value_ptr.value) {
- .scalar => |s| {
- log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
- return true;
- },
- .list, .map => {
- log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
- return true;
- },
- .flag => {},
- }
- return false;
- }
-
- fn typeToEnum(comptime T: type) TypeId {
- return switch (@typeInfo(T)) {
- .Int => .int,
- .Float => .float,
- .Bool => .bool,
- .Enum => .@"enum",
- else => switch (T) {
- []const u8 => .string,
- []const []const u8 => .list,
- else => @compileError("Unsupported type: " ++ @typeName(T)),
- },
- };
- }
-
- fn markInvalidUserInput(self: *Builder) void {
- self.invalid_user_input = true;
- }
-
- pub fn validateUserInputDidItFail(self: *Builder) bool {
- // make sure all args are used
- var it = self.user_input_options.iterator();
- while (it.next()) |entry| {
- if (!entry.value_ptr.used) {
- log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
- self.markInvalidUserInput();
- }
- }
-
- return self.invalid_user_input;
- }
-
- pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
- return self.spawnChildEnvMap(null, self.env_map, argv);
- }
-
- fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
- if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
- for (argv) |arg| {
- std.debug.print("{s} ", .{arg});
- }
- std.debug.print("\n", .{});
- }
-
- pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
- if (self.verbose) {
- printCmd(cwd, argv);
- }
-
- if (!std.process.can_spawn)
- return error.ExecNotSupported;
-
- var child = std.ChildProcess.init(argv, self.allocator);
- child.cwd = cwd;
- child.env_map = env_map;
-
- const term = child.spawnAndWait() catch |err| {
- log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
- return err;
- };
-
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- log.err("The following command exited with error code {}:", .{code});
- printCmd(cwd, argv);
- return error.UncleanExit;
- }
- },
- else => {
- log.err("The following command terminated unexpectedly:", .{});
- printCmd(cwd, argv);
-
- return error.UncleanExit;
- },
- }
- }
-
- pub fn makePath(self: *Builder, path: []const u8) !void {
- fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
- log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
- return err;
- };
- }
-
- pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
- self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
- }
-
- pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
- return InstallArtifactStep.create(self, artifact);
- }
-
- ///`dest_rel_path` is relative to prefix path
- pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
- self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
- }
-
- pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
- self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
- }
-
- ///`dest_rel_path` is relative to bin path
- pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
- self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
- }
-
- ///`dest_rel_path` is relative to lib path
- pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
- self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
- }
-
- /// Output format (BIN vs Intel HEX) determined by filename
- pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
- const raw = self.addInstallRaw(artifact, dest_filename, options);
- self.getInstallStep().dependOn(&raw.step);
- return raw;
- }
-
- ///`dest_rel_path` is relative to install prefix path
- pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
- return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
- }
-
- ///`dest_rel_path` is relative to bin path
- pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
- return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
- }
-
- ///`dest_rel_path` is relative to lib path
- pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
- return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
- }
-
- pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
- return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
- }
-
- pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
- return InstallRawStep.create(self, artifact, dest_filename, options);
- }
-
- pub fn addInstallFileWithDir(
- self: *Builder,
- source: FileSource,
- install_dir: InstallDir,
- dest_rel_path: []const u8,
- ) *InstallFileStep {
- if (dest_rel_path.len == 0) {
- panic("dest_rel_path must be non-empty", .{});
- }
- const install_step = self.allocator.create(InstallFileStep) catch unreachable;
- install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
- return install_step;
- }
-
- pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
- const install_step = self.allocator.create(InstallDirStep) catch unreachable;
- install_step.* = InstallDirStep.init(self, options);
- return install_step;
- }
-
- pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
- const file = InstalledFile{
- .dir = dir,
- .path = dest_rel_path,
- };
- self.installed_files.append(file.dupe(self)) catch unreachable;
- }
-
- pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
- if (self.verbose) {
- log.info("cp {s} {s} ", .{ source_path, dest_path });
- }
- const cwd = fs.cwd();
- const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
- if (self.verbose) switch (prev_status) {
- .stale => log.info("# installed", .{}),
- .fresh => log.info("# up-to-date", .{}),
- };
- }
-
- pub fn truncateFile(self: *Builder, dest_path: []const u8) !void {
- if (self.verbose) {
- log.info("truncate {s}", .{dest_path});
- }
- const cwd = fs.cwd();
- var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
- error.FileNotFound => blk: {
- if (fs.path.dirname(dest_path)) |dirname| {
- try cwd.makePath(dirname);
- }
- break :blk try cwd.createFile(dest_path, .{});
- },
- else => |e| return e,
- };
- src_file.close();
- }
-
- pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
- return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
- }
-
- /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable`
- pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 {
- return fs.path.join(self.allocator, paths) catch unreachable;
- }
-
- pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
- return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
- }
-
- pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
- // TODO report error for ambiguous situations
- const exe_extension = @as(CrossTarget, .{}).exeFileExt();
- for (self.search_prefixes.items) |search_prefix| {
- for (names) |name| {
- if (fs.path.isAbsolute(name)) {
- return name;
- }
- const full_path = self.pathJoin(&.{
- search_prefix,
- "bin",
- self.fmt("{s}{s}", .{ name, exe_extension }),
- });
- return fs.realpathAlloc(self.allocator, full_path) catch continue;
- }
- }
- if (self.env_map.get("PATH")) |PATH| {
- for (names) |name| {
- if (fs.path.isAbsolute(name)) {
- return name;
- }
- var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
- while (it.next()) |path| {
- const full_path = self.pathJoin(&.{
- path,
- self.fmt("{s}{s}", .{ name, exe_extension }),
- });
- return fs.realpathAlloc(self.allocator, full_path) catch continue;
- }
- }
- }
- for (names) |name| {
- if (fs.path.isAbsolute(name)) {
- return name;
- }
- for (paths) |path| {
- const full_path = self.pathJoin(&.{
- path,
- self.fmt("{s}{s}", .{ name, exe_extension }),
- });
- return fs.realpathAlloc(self.allocator, full_path) catch continue;
- }
- }
- return error.FileNotFound;
- }
-
- pub fn execAllowFail(
- self: *Builder,
- argv: []const []const u8,
- out_code: *u8,
- stderr_behavior: std.ChildProcess.StdIo,
- ) ExecError![]u8 {
- assert(argv.len != 0);
-
- if (!std.process.can_spawn)
- return error.ExecNotSupported;
-
- const max_output_size = 400 * 1024;
- var child = std.ChildProcess.init(argv, self.allocator);
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = stderr_behavior;
- child.env_map = self.env_map;
-
- try child.spawn();
-
- const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
- return error.ReadFailure;
- };
- errdefer self.allocator.free(stdout);
-
- const term = try child.wait();
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- out_code.* = @truncate(u8, code);
- return error.ExitCodeFailure;
- }
- return stdout;
- },
- .Signal, .Stopped, .Unknown => |code| {
- out_code.* = @truncate(u8, code);
- return error.ProcessTerminated;
- },
- }
- }
-
- pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
- assert(argv.len != 0);
-
- if (self.verbose) {
- printCmd(null, argv);
- }
-
- if (!std.process.can_spawn) {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("Unable to spawn the following command: cannot spawn child process", .{});
- printCmd(null, argv);
- std.os.abort();
- }
-
- var code: u8 = undefined;
- return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
- error.ExecNotSupported => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("Unable to spawn the following command: cannot spawn child process", .{});
- printCmd(null, argv);
- std.os.abort();
- },
- error.FileNotFound => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("Unable to spawn the following command: file not found", .{});
- printCmd(null, argv);
- std.os.exit(@truncate(u8, code));
- },
- error.ExitCodeFailure => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- if (self.prominent_compile_errors) {
- log.err("The step exited with error code {d}", .{code});
- } else {
- log.err("The following command exited with error code {d}:", .{code});
- printCmd(null, argv);
- }
-
- std.os.exit(@truncate(u8, code));
- },
- error.ProcessTerminated => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("The following command terminated unexpectedly:", .{});
- printCmd(null, argv);
- std.os.exit(@truncate(u8, code));
- },
- else => |e| return e,
- };
- }
-
- pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
- return self.execFromStep(argv, null);
- }
-
- pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
- self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
- }
-
- pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
- assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
- const base_dir = switch (dir) {
- .prefix => self.install_path,
- .bin => self.exe_dir,
- .lib => self.lib_dir,
- .header => self.h_dir,
- .custom => |path| self.pathJoin(&.{ self.install_path, path }),
- };
- return fs.path.resolve(
- self.allocator,
- &[_][]const u8{ base_dir, dest_rel_path },
- ) catch unreachable;
- }
-
- pub const Dependency = struct {
- builder: *Builder,
-
- pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
- var found: ?*LibExeObjStep = null;
- for (d.builder.install_tls.step.dependencies.items) |dep_step| {
- const inst = dep_step.cast(InstallArtifactStep) orelse continue;
- if (mem.eql(u8, inst.artifact.name, name)) {
- if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
- found = inst.artifact;
- }
- }
- return found orelse {
- for (d.builder.install_tls.step.dependencies.items) |dep_step| {
- const inst = dep_step.cast(InstallArtifactStep) orelse continue;
- log.info("available artifact: '{s}'", .{inst.artifact.name});
- }
- panic("unable to find artifact '{s}'", .{name});
- };
- }
- };
-
- pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
- const build_runner = @import("root");
- const deps = build_runner.dependencies;
-
- inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
- if (mem.startsWith(u8, decl.name, b.dep_prefix) and
- mem.endsWith(u8, decl.name, name) and
- decl.name.len == b.dep_prefix.len + name.len)
- {
- const build_zig = @field(deps.imports, decl.name);
- const build_root = @field(deps.build_root, decl.name);
- return dependencyInner(b, name, build_root, build_zig, args);
- }
- }
-
- const full_path = b.pathFromRoot("build.zig.ini");
- std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
- std.process.exit(1);
- }
-
- fn dependencyInner(
- b: *Builder,
- name: []const u8,
- build_root: []const u8,
- comptime build_zig: type,
- args: anytype,
- ) *Dependency {
- const sub_builder = b.createChild(name, build_root, args) catch unreachable;
- sub_builder.runBuild(build_zig) catch unreachable;
-
- if (sub_builder.validateUserInputDidItFail()) {
- std.debug.dumpCurrentStackTrace(@returnAddress());
- }
-
- const dep = b.allocator.create(Dependency) catch unreachable;
- dep.* = .{ .builder = sub_builder };
- return dep;
- }
-
- pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
- switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
- .Void => build_zig.build(b),
- .ErrorUnion => try build_zig.build(b),
- else => @compileError("expected return type of build to be 'void' or '!void'"),
- }
- }
-};
-
-test "builder.findProgram compiles" {
- if (builtin.os.tag == .wasi) return error.SkipZigTest;
-
- var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena.deinit();
-
- const builder = try Builder.create(
- arena.allocator(),
- "zig",
- "zig-cache",
- "zig-cache",
- "zig-cache",
- );
- defer builder.destroy();
- _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
-}
-
-pub const Pkg = struct {
- name: []const u8,
- source: FileSource,
- dependencies: ?[]const Pkg = null,
-};
-
-/// A file that is generated by a build step.
-/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
-pub const GeneratedFile = struct {
- /// The step that generates the file
- step: *Step,
-
- /// The path to the generated file. Must be either absolute or relative to the build root.
- /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
- path: ?[]const u8 = null,
-
- pub fn getPath(self: GeneratedFile) []const u8 {
- return self.path orelse std.debug.panic(
- "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
- .{self.step.name},
- );
- }
-};
-
-/// A file source is a reference to an existing or future file.
-///
-pub const FileSource = union(enum) {
- /// A plain file path, relative to build root or absolute.
- path: []const u8,
-
- /// A file that is generated by an interface. Those files usually are
- /// not available until built by a build step.
- generated: *const GeneratedFile,
-
- /// Returns a new file source that will have a relative path to the build root guaranteed.
- /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
- pub fn relative(path: []const u8) FileSource {
- std.debug.assert(!std.fs.path.isAbsolute(path));
- return FileSource{ .path = path };
- }
-
- /// Returns a string that can be shown to represent the file source.
- /// Either returns the path or `"generated"`.
- pub fn getDisplayName(self: FileSource) []const u8 {
- return switch (self) {
- .path => self.path,
- .generated => "generated",
- };
- }
-
- /// Adds dependencies this file source implies to the given step.
- pub fn addStepDependencies(self: FileSource, step: *Step) void {
- switch (self) {
- .path => {},
- .generated => |gen| step.dependOn(gen.step),
- }
- }
-
- /// Should only be called during make(), returns a path relative to the build root or absolute.
- pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
- const path = switch (self) {
- .path => |p| builder.pathFromRoot(p),
- .generated => |gen| gen.getPath(),
- };
- return path;
- }
-
- /// Duplicates the file source for a given builder.
- pub fn dupe(self: FileSource, b: *Builder) FileSource {
- return switch (self) {
- .path => |p| .{ .path = b.dupePath(p) },
- .generated => |gen| .{ .generated = gen },
- };
- }
-};
-
-/// Allocates a new string for assigning a value to a named macro.
-/// If the value is omitted, it is set to 1.
-/// `name` and `value` need not live longer than the function call.
-pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
- var macro = allocator.alloc(
- u8,
- name.len + if (value) |value_slice| value_slice.len + 1 else 0,
- ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
- mem.copy(u8, macro, name);
- if (value) |value_slice| {
- macro[name.len] = '=';
- mem.copy(u8, macro[name.len + 1 ..], value_slice);
- }
- return macro;
-}
-
-/// deprecated: use `InstallDirStep.Options`
-pub const InstallDirectoryOptions = InstallDirStep.Options;
-
-pub const Step = struct {
- id: Id,
- name: []const u8,
- makeFn: MakeFn,
- dependencies: ArrayList(*Step),
- loop_flag: bool,
- done_flag: bool,
-
- const MakeFn = *const fn (self: *Step) anyerror!void;
-
- pub const Id = enum {
- top_level,
- lib_exe_obj,
- install_artifact,
- install_file,
- install_dir,
- log,
- remove_dir,
- fmt,
- translate_c,
- write_file,
- run,
- emulatable_run,
- check_file,
- check_object,
- config_header,
- install_raw,
- options,
- custom,
-
- pub fn Type(comptime id: Id) type {
- return switch (id) {
- .top_level => Builder.TopLevelStep,
- .lib_exe_obj => LibExeObjStep,
- .install_artifact => InstallArtifactStep,
- .install_file => InstallFileStep,
- .install_dir => InstallDirStep,
- .log => LogStep,
- .remove_dir => RemoveDirStep,
- .fmt => FmtStep,
- .translate_c => TranslateCStep,
- .write_file => WriteFileStep,
- .run => RunStep,
- .emulatable_run => EmulatableRunStep,
- .check_file => CheckFileStep,
- .check_object => CheckObjectStep,
- .config_header => ConfigHeaderStep,
- .install_raw => InstallRawStep,
- .options => OptionsStep,
- .custom => @compileError("no type available for custom step"),
- };
- }
- };
-
- pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step {
- return Step{
- .id = id,
- .name = allocator.dupe(u8, name) catch unreachable,
- .makeFn = makeFn,
- .dependencies = ArrayList(*Step).init(allocator),
- .loop_flag = false,
- .done_flag = false,
- };
- }
- pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
- return init(id, name, allocator, makeNoOp);
- }
-
- pub fn make(self: *Step) !void {
- if (self.done_flag) return;
-
- try self.makeFn(self);
- self.done_flag = true;
- }
-
- pub fn dependOn(self: *Step, other: *Step) void {
- self.dependencies.append(other) catch unreachable;
- }
-
- fn makeNoOp(self: *Step) anyerror!void {
- _ = self;
- }
-
- pub fn cast(step: *Step, comptime T: type) ?*T {
- if (step.id == T.base_id) {
- return @fieldParentPtr(T, "step", step);
- }
- return null;
- }
-};
-
-pub const VcpkgRoot = union(VcpkgRootStatus) {
- unattempted: void,
- not_found: void,
- found: []const u8,
-};
-
-pub const VcpkgRootStatus = enum {
- unattempted,
- not_found,
- found,
-};
-
-pub const InstallDir = union(enum) {
- prefix: void,
- lib: void,
- bin: void,
- header: void,
- /// A path relative to the prefix
- custom: []const u8,
-
- /// Duplicates the install directory including the path if set to custom.
- pub fn dupe(self: InstallDir, builder: *Builder) InstallDir {
- if (self == .custom) {
- // Written with this temporary to avoid RLS problems
- const duped_path = builder.dupe(self.custom);
- return .{ .custom = duped_path };
- } else {
- return self;
- }
- }
-};
-
-pub const InstalledFile = struct {
- dir: InstallDir,
- path: []const u8,
-
- /// Duplicates the installed file path and directory.
- pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
- return .{
- .dir = self.dir.dupe(builder),
- .path = builder.dupe(self.path),
- };
- }
-};
-
-pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
- // TODO this logic can disappear if cpu model + features becomes part of the target triple
- const all_features = cpu.arch.allFeaturesList();
- var populated_cpu_features = cpu.model.features;
- populated_cpu_features.populateDependencies(all_features);
-
- if (populated_cpu_features.eql(cpu.features)) {
- // The CPU name alone is sufficient.
- return cpu.model.name;
- } else {
- var mcpu_buffer = ArrayList(u8).init(allocator);
- try mcpu_buffer.appendSlice(cpu.model.name);
-
- for (all_features) |feature, i_usize| {
- const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
- const in_cpu_set = populated_cpu_features.isEnabled(i);
- const in_actual_set = cpu.features.isEnabled(i);
- if (in_cpu_set and !in_actual_set) {
- try mcpu_buffer.writer().print("-{s}", .{feature.name});
- } else if (!in_cpu_set and in_actual_set) {
- try mcpu_buffer.writer().print("+{s}", .{feature.name});
- }
- }
-
- return try mcpu_buffer.toOwnedSlice();
- }
-}
-
-test "dupePkg()" {
- if (builtin.os.tag == .wasi) return error.SkipZigTest;
-
- var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
- defer arena.deinit();
- var builder = try Builder.create(
- arena.allocator(),
- "test",
- "test",
- "test",
- "test",
- );
- defer builder.destroy();
-
- var pkg_dep = Pkg{
- .name = "pkg_dep",
- .source = .{ .path = "/not/a/pkg_dep.zig" },
- };
- var pkg_top = Pkg{
- .name = "pkg_top",
- .source = .{ .path = "/not/a/pkg_top.zig" },
- .dependencies = &[_]Pkg{pkg_dep},
- };
- const dupe = builder.dupePkg(pkg_top);
-
- const original_deps = pkg_top.dependencies.?;
- const dupe_deps = dupe.dependencies.?;
-
- // probably the same top level package details
- try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
-
- // probably the same dependencies
- try std.testing.expectEqual(original_deps.len, dupe_deps.len);
- try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
-
- // could segfault otherwise if pointers in duplicated package's fields are
- // the same as those in stack allocated package's fields
- try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
- try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
- try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr);
- try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
- try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
-}
-
-test {
- _ = CheckFileStep;
- _ = CheckObjectStep;
- _ = EmulatableRunStep;
- _ = FmtStep;
- _ = InstallArtifactStep;
- _ = InstallDirStep;
- _ = InstallFileStep;
- _ = InstallRawStep;
- _ = LibExeObjStep;
- _ = LogStep;
- _ = OptionsStep;
- _ = RemoveDirStep;
- _ = RunStep;
- _ = TranslateCStep;
- _ = WriteFileStep;
-}
diff --git a/lib/std/build/CheckFileStep.zig b/lib/std/build/CheckFileStep.zig
deleted file mode 100644
index 2c06ab9279..0000000000
--- a/lib/std/build/CheckFileStep.zig
+++ /dev/null
@@ -1,53 +0,0 @@
-const std = @import("../std.zig");
-const build = std.build;
-const Step = build.Step;
-const Builder = build.Builder;
-const fs = std.fs;
-const mem = std.mem;
-
-const CheckFileStep = @This();
-
-pub const base_id = .check_file;
-
-step: Step,
-builder: *Builder,
-expected_matches: []const []const u8,
-source: build.FileSource,
-max_bytes: usize = 20 * 1024 * 1024,
-
-pub fn create(
- builder: *Builder,
- source: build.FileSource,
- expected_matches: []const []const u8,
-) *CheckFileStep {
- const self = builder.allocator.create(CheckFileStep) catch unreachable;
- self.* = CheckFileStep{
- .builder = builder,
- .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
- .source = source.dupe(builder),
- .expected_matches = builder.dupeStrings(expected_matches),
- };
- self.source.addStepDependencies(&self.step);
- return self;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(CheckFileStep, "step", step);
-
- const src_path = self.source.getPath(self.builder);
- const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
-
- for (self.expected_matches) |expected_match| {
- if (mem.indexOf(u8, contents, expected_match) == null) {
- std.debug.print(
- \\
- \\========= Expected to find: ===================
- \\{s}
- \\========= But file does not contain it: =======
- \\{s}
- \\
- , .{ expected_match, contents });
- return error.TestFailed;
- }
- }
-}
diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig
deleted file mode 100644
index 4ef350b418..0000000000
--- a/lib/std/build/CheckObjectStep.zig
+++ /dev/null
@@ -1,1026 +0,0 @@
-const std = @import("../std.zig");
-const assert = std.debug.assert;
-const build = std.build;
-const fs = std.fs;
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const testing = std.testing;
-
-const CheckObjectStep = @This();
-
-const Allocator = mem.Allocator;
-const Builder = build.Builder;
-const Step = build.Step;
-const EmulatableRunStep = build.EmulatableRunStep;
-
-pub const base_id = .check_object;
-
-step: Step,
-builder: *Builder,
-source: build.FileSource,
-max_bytes: usize = 20 * 1024 * 1024,
-checks: std.ArrayList(Check),
-dump_symtab: bool = false,
-obj_format: std.Target.ObjectFormat,
-
-pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
- const gpa = builder.allocator;
- const self = gpa.create(CheckObjectStep) catch unreachable;
- self.* = .{
- .builder = builder,
- .step = Step.init(.check_file, "CheckObject", gpa, make),
- .source = source.dupe(builder),
- .checks = std.ArrayList(Check).init(gpa),
- .obj_format = obj_format,
- };
- self.source.addStepDependencies(&self.step);
- return self;
-}
-
-/// Runs and (optionally) compares the output of a binary.
-/// Asserts `self` was generated from an executable step.
-pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
- const dependencies_len = self.step.dependencies.items.len;
- assert(dependencies_len > 0);
- const exe_step = self.step.dependencies.items[dependencies_len - 1];
- const exe = exe_step.cast(std.build.LibExeObjStep).?;
- const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
- emulatable_step.step.dependOn(&self.step);
- return emulatable_step;
-}
-
-/// There two types of actions currently suported:
-/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
-/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
-/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
-/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
-/// it should be plenty useful in its current form.
-/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
-/// using the MatchAction. It currently only supports an addition. The operation is required
-/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
-/// to avoid any parsing really).
-/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
-/// they could then be added with this simple program `vmaddr entryoff +`.
-const Action = struct {
- tag: enum { match, not_present, compute_cmp },
- phrase: []const u8,
- expected: ?ComputeCompareExpected = null,
-
- /// Will return true if the `phrase` was found in the `haystack`.
- /// Some examples include:
- ///
- /// LC 0 => will match in its entirety
- /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
- /// and save under `vmaddr` global name (see `global_vars` param)
- /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
- /// in that order with other letters in between
- fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
- assert(act.tag == .match or act.tag == .not_present);
-
- var candidate_var: ?struct { name: []const u8, value: u64 } = null;
- var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
- var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
-
- while (needle_it.next()) |needle_tok| {
- const hay_tok = hay_it.next() orelse return false;
-
- if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
- // We have fuzzy matchers within the search pattern, so we match substrings.
- var start = index;
- var n_tok = needle_tok;
- var h_tok = hay_tok;
- while (true) {
- n_tok = n_tok[start + 3 ..];
- const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
- n_tok[0..sub_end]
- else
- n_tok;
- if (mem.indexOf(u8, h_tok, inner) == null) return false;
- start = mem.indexOf(u8, n_tok, "{*}") orelse break;
- }
- } else if (mem.startsWith(u8, needle_tok, "{")) {
- const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
- if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
-
- const name = needle_tok[1..closing_brace];
- if (name.len == 0) return error.MissingBraceValue;
- const value = try std.fmt.parseInt(u64, hay_tok, 16);
- candidate_var = .{
- .name = name,
- .value = value,
- };
- } else {
- if (!mem.eql(u8, hay_tok, needle_tok)) return false;
- }
- }
-
- if (candidate_var) |v| {
- try global_vars.putNoClobber(v.name, v.value);
- }
-
- return true;
- }
-
- /// Will return true if the `phrase` is correctly parsed into an RPN program and
- /// its reduced, computed value compares using `op` with the expected value, either
- /// a literal or another extracted variable.
- fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
- var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
- var values = std.ArrayList(u64).init(gpa);
-
- var it = mem.tokenize(u8, act.phrase, " ");
- while (it.next()) |next| {
- if (mem.eql(u8, next, "+")) {
- try op_stack.append(.add);
- } else if (mem.eql(u8, next, "-")) {
- try op_stack.append(.sub);
- } else if (mem.eql(u8, next, "%")) {
- try op_stack.append(.mod);
- } else if (mem.eql(u8, next, "*")) {
- try op_stack.append(.mul);
- } else {
- const val = std.fmt.parseInt(u64, next, 0) catch blk: {
- break :blk global_vars.get(next) orelse {
- std.debug.print(
- \\
- \\========= Variable was not extracted: ===========
- \\{s}
- \\
- , .{next});
- return error.UnknownVariable;
- };
- };
- try values.append(val);
- }
- }
-
- var op_i: usize = 1;
- var reduced: u64 = values.items[0];
- for (op_stack.items) |op| {
- const other = values.items[op_i];
- switch (op) {
- .add => {
- reduced += other;
- },
- .sub => {
- reduced -= other;
- },
- .mod => {
- reduced %= other;
- },
- .mul => {
- reduced *= other;
- },
- }
- op_i += 1;
- }
-
- const exp_value = switch (act.expected.?.value) {
- .variable => |name| global_vars.get(name) orelse {
- std.debug.print(
- \\
- \\========= Variable was not extracted: ===========
- \\{s}
- \\
- , .{name});
- return error.UnknownVariable;
- },
- .literal => |x| x,
- };
- return math.compare(reduced, act.expected.?.op, exp_value);
- }
-};
-
-const ComputeCompareExpected = struct {
- op: math.CompareOperator,
- value: union(enum) {
- variable: []const u8,
- literal: u64,
- },
-
- pub fn format(
- value: @This(),
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- writer: anytype,
- ) !void {
- if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
- _ = options;
- try writer.print("{s} ", .{@tagName(value.op)});
- switch (value.value) {
- .variable => |name| try writer.writeAll(name),
- .literal => |x| try writer.print("{x}", .{x}),
- }
- }
-};
-
-const Check = struct {
- builder: *Builder,
- actions: std.ArrayList(Action),
-
- fn create(b: *Builder) Check {
- return .{
- .builder = b,
- .actions = std.ArrayList(Action).init(b.allocator),
- };
- }
-
- fn match(self: *Check, phrase: []const u8) void {
- self.actions.append(.{
- .tag = .match,
- .phrase = self.builder.dupe(phrase),
- }) catch unreachable;
- }
-
- fn notPresent(self: *Check, phrase: []const u8) void {
- self.actions.append(.{
- .tag = .not_present,
- .phrase = self.builder.dupe(phrase),
- }) catch unreachable;
- }
-
- fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
- self.actions.append(.{
- .tag = .compute_cmp,
- .phrase = self.builder.dupe(phrase),
- .expected = expected,
- }) catch unreachable;
- }
-};
-
-/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
-pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
- var new_check = Check.create(self.builder);
- new_check.match(phrase);
- self.checks.append(new_check) catch unreachable;
-}
-
-/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
-/// Asserts at least one check already exists.
-pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
- assert(self.checks.items.len > 0);
- const last = &self.checks.items[self.checks.items.len - 1];
- last.match(phrase);
-}
-
-/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
-/// however ensures there is no matching phrase in the output.
-/// Asserts at least one check already exists.
-pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
- assert(self.checks.items.len > 0);
- const last = &self.checks.items[self.checks.items.len - 1];
- last.notPresent(phrase);
-}
-
-/// Creates a new check checking specifically symbol table parsed and dumped from the object
-/// file.
-/// Issuing this check will force parsing and dumping of the symbol table.
-pub fn checkInSymtab(self: *CheckObjectStep) void {
- self.dump_symtab = true;
- const symtab_label = switch (self.obj_format) {
- .macho => MachODumper.symtab_label,
- else => @panic("TODO other parsers"),
- };
- self.checkStart(symtab_label);
-}
-
-/// Creates a new standalone, singular check which allows running simple binary operations
-/// on the extracted variables. It will then compare the reduced program with the value of
-/// the expected variable.
-pub fn checkComputeCompare(
- self: *CheckObjectStep,
- program: []const u8,
- expected: ComputeCompareExpected,
-) void {
- var new_check = Check.create(self.builder);
- new_check.computeCmp(program, expected);
- self.checks.append(new_check) catch unreachable;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(CheckObjectStep, "step", step);
-
- const gpa = self.builder.allocator;
- const src_path = self.source.getPath(self.builder);
- const contents = try fs.cwd().readFileAllocOptions(
- gpa,
- src_path,
- self.max_bytes,
- null,
- @alignOf(u64),
- null,
- );
-
- const output = switch (self.obj_format) {
- .macho => try MachODumper.parseAndDump(contents, .{
- .gpa = gpa,
- .dump_symtab = self.dump_symtab,
- }),
- .elf => @panic("TODO elf parser"),
- .coff => @panic("TODO coff parser"),
- .wasm => try WasmDumper.parseAndDump(contents, .{
- .gpa = gpa,
- .dump_symtab = self.dump_symtab,
- }),
- else => unreachable,
- };
-
- var vars = std.StringHashMap(u64).init(gpa);
-
- for (self.checks.items) |chk| {
- var it = mem.tokenize(u8, output, "\r\n");
- for (chk.actions.items) |act| {
- switch (act.tag) {
- .match => {
- while (it.next()) |line| {
- if (try act.match(line, &vars)) break;
- } else {
- std.debug.print(
- \\
- \\========= Expected to find: ==========================
- \\{s}
- \\========= But parsed file does not contain it: =======
- \\{s}
- \\
- , .{ act.phrase, output });
- return error.TestFailed;
- }
- },
- .not_present => {
- while (it.next()) |line| {
- if (try act.match(line, &vars)) {
- std.debug.print(
- \\
- \\========= Expected not to find: ===================
- \\{s}
- \\========= But parsed file does contain it: ========
- \\{s}
- \\
- , .{ act.phrase, output });
- return error.TestFailed;
- }
- }
- },
- .compute_cmp => {
- const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
- error.UnknownVariable => {
- std.debug.print(
- \\========= From parsed file: =====================
- \\{s}
- \\
- , .{output});
- return error.TestFailed;
- },
- else => |e| return e,
- };
- if (!res) {
- std.debug.print(
- \\
- \\========= Comparison failed for action: ===========
- \\{s} {}
- \\========= From parsed file: =======================
- \\{s}
- \\
- , .{ act.phrase, act.expected.?, output });
- return error.TestFailed;
- }
- },
- }
- }
- }
-}
-
-const Opts = struct {
- gpa: ?Allocator = null,
- dump_symtab: bool = false,
-};
-
-const MachODumper = struct {
- const LoadCommandIterator = macho.LoadCommandIterator;
- const symtab_label = "symtab";
-
- fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
- const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
- var stream = std.io.fixedBufferStream(bytes);
- const reader = stream.reader();
-
- const hdr = try reader.readStruct(macho.mach_header_64);
- if (hdr.magic != macho.MH_MAGIC_64) {
- return error.InvalidMagicNumber;
- }
-
- var output = std.ArrayList(u8).init(gpa);
- const writer = output.writer();
-
- var symtab: []const macho.nlist_64 = undefined;
- var strtab: []const u8 = undefined;
- var sections = std.ArrayList(macho.section_64).init(gpa);
- var imports = std.ArrayList([]const u8).init(gpa);
-
- var it = LoadCommandIterator{
- .ncmds = hdr.ncmds,
- .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
- };
- var i: usize = 0;
- while (it.next()) |cmd| {
- switch (cmd.cmd()) {
- .SEGMENT_64 => {
- const seg = cmd.cast(macho.segment_command_64).?;
- try sections.ensureUnusedCapacity(seg.nsects);
- for (cmd.getSections()) |sect| {
- sections.appendAssumeCapacity(sect);
- }
- },
- .SYMTAB => if (opts.dump_symtab) {
- const lc = cmd.cast(macho.symtab_command).?;
- symtab = @ptrCast(
- [*]const macho.nlist_64,
- @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
- )[0..lc.nsyms];
- strtab = bytes[lc.stroff..][0..lc.strsize];
- },
- .LOAD_DYLIB,
- .LOAD_WEAK_DYLIB,
- .REEXPORT_DYLIB,
- => {
- try imports.append(cmd.getDylibPathName());
- },
- else => {},
- }
-
- try dumpLoadCommand(cmd, i, writer);
- try writer.writeByte('\n');
-
- i += 1;
- }
-
- if (opts.dump_symtab) {
- try writer.print("{s}\n", .{symtab_label});
- for (symtab) |sym| {
- if (sym.stab()) continue;
- const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
- if (sym.sect()) {
- const sect = sections.items[sym.n_sect - 1];
- try writer.print("{x} ({s},{s})", .{
- sym.n_value,
- sect.segName(),
- sect.sectName(),
- });
- if (sym.ext()) {
- try writer.writeAll(" external");
- }
- try writer.print(" {s}\n", .{sym_name});
- } else if (sym.undf()) {
- const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
- const import_name = blk: {
- if (ordinal <= 0) {
- if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
- break :blk "self import";
- if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
- break :blk "main executable";
- if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
- break :blk "flat lookup";
- unreachable;
- }
- const full_path = imports.items[@bitCast(u16, ordinal) - 1];
- const basename = fs.path.basename(full_path);
- assert(basename.len > 0);
- const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
- break :blk basename[0..ext];
- };
- try writer.writeAll("(undefined)");
- if (sym.weakRef()) {
- try writer.writeAll(" weak");
- }
- if (sym.ext()) {
- try writer.writeAll(" external");
- }
- try writer.print(" {s} (from {s})\n", .{
- sym_name,
- import_name,
- });
- } else unreachable;
- }
- }
-
- return output.toOwnedSlice();
- }
-
- fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
- // print header first
- try writer.print(
- \\LC {d}
- \\cmd {s}
- \\cmdsize {d}
- , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
-
- switch (lc.cmd()) {
- .SEGMENT_64 => {
- const seg = lc.cast(macho.segment_command_64).?;
- try writer.writeByte('\n');
- try writer.print(
- \\segname {s}
- \\vmaddr {x}
- \\vmsize {x}
- \\fileoff {x}
- \\filesz {x}
- , .{
- seg.segName(),
- seg.vmaddr,
- seg.vmsize,
- seg.fileoff,
- seg.filesize,
- });
-
- for (lc.getSections()) |sect| {
- try writer.writeByte('\n');
- try writer.print(
- \\sectname {s}
- \\addr {x}
- \\size {x}
- \\offset {x}
- \\align {x}
- , .{
- sect.sectName(),
- sect.addr,
- sect.size,
- sect.offset,
- sect.@"align",
- });
- }
- },
-
- .ID_DYLIB,
- .LOAD_DYLIB,
- .LOAD_WEAK_DYLIB,
- .REEXPORT_DYLIB,
- => {
- const dylib = lc.cast(macho.dylib_command).?;
- try writer.writeByte('\n');
- try writer.print(
- \\name {s}
- \\timestamp {d}
- \\current version {x}
- \\compatibility version {x}
- , .{
- lc.getDylibPathName(),
- dylib.dylib.timestamp,
- dylib.dylib.current_version,
- dylib.dylib.compatibility_version,
- });
- },
-
- .MAIN => {
- const main = lc.cast(macho.entry_point_command).?;
- try writer.writeByte('\n');
- try writer.print(
- \\entryoff {x}
- \\stacksize {x}
- , .{ main.entryoff, main.stacksize });
- },
-
- .RPATH => {
- try writer.writeByte('\n');
- try writer.print(
- \\path {s}
- , .{
- lc.getRpathPathName(),
- });
- },
-
- .UUID => {
- const uuid = lc.cast(macho.uuid_command).?;
- try writer.writeByte('\n');
- try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
- },
-
- .DATA_IN_CODE,
- .FUNCTION_STARTS,
- .CODE_SIGNATURE,
- => {
- const llc = lc.cast(macho.linkedit_data_command).?;
- try writer.writeByte('\n');
- try writer.print(
- \\dataoff {x}
- \\datasize {x}
- , .{ llc.dataoff, llc.datasize });
- },
-
- .DYLD_INFO_ONLY => {
- const dlc = lc.cast(macho.dyld_info_command).?;
- try writer.writeByte('\n');
- try writer.print(
- \\rebaseoff {x}
- \\rebasesize {x}
- \\bindoff {x}
- \\bindsize {x}
- \\weakbindoff {x}
- \\weakbindsize {x}
- \\lazybindoff {x}
- \\lazybindsize {x}
- \\exportoff {x}
- \\exportsize {x}
- , .{
- dlc.rebase_off,
- dlc.rebase_size,
- dlc.bind_off,
- dlc.bind_size,
- dlc.weak_bind_off,
- dlc.weak_bind_size,
- dlc.lazy_bind_off,
- dlc.lazy_bind_size,
- dlc.export_off,
- dlc.export_size,
- });
- },
-
- .SYMTAB => {
- const slc = lc.cast(macho.symtab_command).?;
- try writer.writeByte('\n');
- try writer.print(
- \\symoff {x}
- \\nsyms {x}
- \\stroff {x}
- \\strsize {x}
- , .{
- slc.symoff,
- slc.nsyms,
- slc.stroff,
- slc.strsize,
- });
- },
-
- .DYSYMTAB => {
- const dlc = lc.cast(macho.dysymtab_command).?;
- try writer.writeByte('\n');
- try writer.print(
- \\ilocalsym {x}
- \\nlocalsym {x}
- \\iextdefsym {x}
- \\nextdefsym {x}
- \\iundefsym {x}
- \\nundefsym {x}
- \\indirectsymoff {x}
- \\nindirectsyms {x}
- , .{
- dlc.ilocalsym,
- dlc.nlocalsym,
- dlc.iextdefsym,
- dlc.nextdefsym,
- dlc.iundefsym,
- dlc.nundefsym,
- dlc.indirectsymoff,
- dlc.nindirectsyms,
- });
- },
-
- else => {},
- }
- }
-};
-
-const WasmDumper = struct {
- const symtab_label = "symbols";
-
- fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
- const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
- if (opts.dump_symtab) {
- @panic("TODO: Implement symbol table parsing and dumping");
- }
-
- var fbs = std.io.fixedBufferStream(bytes);
- const reader = fbs.reader();
-
- const buf = try reader.readBytesNoEof(8);
- if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
- return error.InvalidMagicByte;
- }
- if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
- return error.UnsupportedWasmVersion;
- }
-
- var output = std.ArrayList(u8).init(gpa);
- errdefer output.deinit();
- const writer = output.writer();
-
- while (reader.readByte()) |current_byte| {
- const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
- std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
- return err;
- };
-
- const section_length = try std.leb.readULEB128(u32, reader);
- try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
- fbs.pos += section_length;
- } else |_| {} // reached end of stream
-
- return output.toOwnedSlice();
- }
-
- fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
-
- try writer.print(
- \\Section {s}
- \\size {d}
- , .{ @tagName(section), data.len });
-
- switch (section) {
- .type,
- .import,
- .function,
- .table,
- .memory,
- .global,
- .@"export",
- .element,
- .code,
- .data,
- => {
- const entries = try std.leb.readULEB128(u32, reader);
- try writer.print("\nentries {d}\n", .{entries});
- try dumpSection(section, data[fbs.pos..], entries, writer);
- },
- .custom => {
- const name_length = try std.leb.readULEB128(u32, reader);
- const name = data[fbs.pos..][0..name_length];
- fbs.pos += name_length;
- try writer.print("\nname {s}\n", .{name});
-
- if (mem.eql(u8, name, "name")) {
- try parseDumpNames(reader, writer, data);
- } else if (mem.eql(u8, name, "producers")) {
- try parseDumpProducers(reader, writer, data);
- } else if (mem.eql(u8, name, "target_features")) {
- try parseDumpFeatures(reader, writer, data);
- }
- // TODO: Implement parsing and dumping other custom sections (such as relocations)
- },
- .start => {
- const start = try std.leb.readULEB128(u32, reader);
- try writer.print("\nstart {d}\n", .{start});
- },
- else => {}, // skip unknown sections
- }
- }
-
- fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
- var fbs = std.io.fixedBufferStream(data);
- const reader = fbs.reader();
-
- switch (section) {
- .type => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- const func_type = try reader.readByte();
- if (func_type != std.wasm.function_type) {
- std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
- return error.UnexpectedByte;
- }
- const params = try std.leb.readULEB128(u32, reader);
- try writer.print("params {d}\n", .{params});
- var index: u32 = 0;
- while (index < params) : (index += 1) {
- try parseDumpType(std.wasm.Valtype, reader, writer);
- } else index = 0;
- const returns = try std.leb.readULEB128(u32, reader);
- try writer.print("returns {d}\n", .{returns});
- while (index < returns) : (index += 1) {
- try parseDumpType(std.wasm.Valtype, reader, writer);
- }
- }
- },
- .import => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- const module_name_len = try std.leb.readULEB128(u32, reader);
- const module_name = data[fbs.pos..][0..module_name_len];
- fbs.pos += module_name_len;
- const name_len = try std.leb.readULEB128(u32, reader);
- const name = data[fbs.pos..][0..name_len];
- fbs.pos += name_len;
-
- const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
- std.debug.print("Invalid import kind\n", .{});
- return err;
- };
-
- try writer.print(
- \\module {s}
- \\name {s}
- \\kind {s}
- , .{ module_name, name, @tagName(kind) });
- try writer.writeByte('\n');
- switch (kind) {
- .function => {
- try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
- },
- .memory => {
- try parseDumpLimits(reader, writer);
- },
- .global => {
- try parseDumpType(std.wasm.Valtype, reader, writer);
- try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
- },
- .table => {
- try parseDumpType(std.wasm.RefType, reader, writer);
- try parseDumpLimits(reader, writer);
- },
- }
- }
- },
- .function => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
- }
- },
- .table => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- try parseDumpType(std.wasm.RefType, reader, writer);
- try parseDumpLimits(reader, writer);
- }
- },
- .memory => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- try parseDumpLimits(reader, writer);
- }
- },
- .global => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- try parseDumpType(std.wasm.Valtype, reader, writer);
- try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
- try parseDumpInit(reader, writer);
- }
- },
- .@"export" => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- const name_len = try std.leb.readULEB128(u32, reader);
- const name = data[fbs.pos..][0..name_len];
- fbs.pos += name_len;
- const kind_byte = try std.leb.readULEB128(u8, reader);
- const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
- std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
- return err;
- };
- const index = try std.leb.readULEB128(u32, reader);
- try writer.print(
- \\name {s}
- \\kind {s}
- \\index {d}
- , .{ name, @tagName(kind), index });
- try writer.writeByte('\n');
- }
- },
- .element => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
- try parseDumpInit(reader, writer);
-
- const function_indexes = try std.leb.readULEB128(u32, reader);
- var function_index: u32 = 0;
- try writer.print("indexes {d}\n", .{function_indexes});
- while (function_index < function_indexes) : (function_index += 1) {
- try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
- }
- }
- },
- .code => {}, // code section is considered opaque to linker
- .data => {
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- const index = try std.leb.readULEB128(u32, reader);
- try writer.print("memory index 0x{x}\n", .{index});
- try parseDumpInit(reader, writer);
- const size = try std.leb.readULEB128(u32, reader);
- try writer.print("size {d}\n", .{size});
- try reader.skipBytes(size, .{}); // we do not care about the content of the segments
- }
- },
- else => unreachable,
- }
- }
-
- fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
- const type_byte = try reader.readByte();
- const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
- std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
- return err;
- };
- try writer.print("type {s}\n", .{@tagName(valtype)});
- }
-
- fn parseDumpLimits(reader: anytype, writer: anytype) !void {
- const flags = try std.leb.readULEB128(u8, reader);
- const min = try std.leb.readULEB128(u32, reader);
-
- try writer.print("min {x}\n", .{min});
- if (flags != 0) {
- try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
- }
- }
-
- fn parseDumpInit(reader: anytype, writer: anytype) !void {
- const byte = try std.leb.readULEB128(u8, reader);
- const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
- std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
- return err;
- };
- switch (opcode) {
- .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
- .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
- .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
- .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
- .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
- else => unreachable,
- }
- const end_opcode = try std.leb.readULEB128(u8, reader);
- if (end_opcode != std.wasm.opcode(.end)) {
- std.debug.print("expected 'end' opcode in init expression\n", .{});
- return error.MissingEndOpcode;
- }
- }
-
- fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
- while (reader.context.pos < data.len) {
- try parseDumpType(std.wasm.NameSubsection, reader, writer);
- const size = try std.leb.readULEB128(u32, reader);
- const entries = try std.leb.readULEB128(u32, reader);
- try writer.print(
- \\size {d}
- \\names {d}
- , .{ size, entries });
- try writer.writeByte('\n');
- var i: u32 = 0;
- while (i < entries) : (i += 1) {
- const index = try std.leb.readULEB128(u32, reader);
- const name_len = try std.leb.readULEB128(u32, reader);
- const pos = reader.context.pos;
- const name = data[pos..][0..name_len];
- reader.context.pos += name_len;
-
- try writer.print(
- \\index {d}
- \\name {s}
- , .{ index, name });
- try writer.writeByte('\n');
- }
- }
- }
-
- fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
- const field_count = try std.leb.readULEB128(u32, reader);
- try writer.print("fields {d}\n", .{field_count});
- var current_field: u32 = 0;
- while (current_field < field_count) : (current_field += 1) {
- const field_name_length = try std.leb.readULEB128(u32, reader);
- const field_name = data[reader.context.pos..][0..field_name_length];
- reader.context.pos += field_name_length;
-
- const value_count = try std.leb.readULEB128(u32, reader);
- try writer.print(
- \\field_name {s}
- \\values {d}
- , .{ field_name, value_count });
- try writer.writeByte('\n');
- var current_value: u32 = 0;
- while (current_value < value_count) : (current_value += 1) {
- const value_length = try std.leb.readULEB128(u32, reader);
- const value = data[reader.context.pos..][0..value_length];
- reader.context.pos += value_length;
-
- const version_length = try std.leb.readULEB128(u32, reader);
- const version = data[reader.context.pos..][0..version_length];
- reader.context.pos += version_length;
-
- try writer.print(
- \\value_name {s}
- \\version {s}
- , .{ value, version });
- try writer.writeByte('\n');
- }
- }
- }
-
- fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
- const feature_count = try std.leb.readULEB128(u32, reader);
- try writer.print("features {d}\n", .{feature_count});
-
- var index: u32 = 0;
- while (index < feature_count) : (index += 1) {
- const prefix_byte = try std.leb.readULEB128(u8, reader);
- const name_length = try std.leb.readULEB128(u32, reader);
- const feature_name = data[reader.context.pos..][0..name_length];
- reader.context.pos += name_length;
-
- try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
- }
- }
-};
diff --git a/lib/std/build/ConfigHeaderStep.zig b/lib/std/build/ConfigHeaderStep.zig
deleted file mode 100644
index 400c06525e..0000000000
--- a/lib/std/build/ConfigHeaderStep.zig
+++ /dev/null
@@ -1,288 +0,0 @@
-const std = @import("../std.zig");
-const ConfigHeaderStep = @This();
-const Step = std.build.Step;
-const Builder = std.build.Builder;
-
-pub const base_id: Step.Id = .config_header;
-
-pub const Style = enum {
- /// The configure format supported by autotools. It uses `#undef foo` to
- /// mark lines that can be substituted with different values.
- autoconf,
- /// The configure format supported by CMake. It uses `@@FOO@@` and
- /// `#cmakedefine` for template substitution.
- cmake,
-};
-
-pub const Value = union(enum) {
- undef,
- defined,
- boolean: bool,
- int: i64,
- ident: []const u8,
- string: []const u8,
-};
-
-step: Step,
-builder: *Builder,
-source: std.build.FileSource,
-style: Style,
-values: std.StringHashMap(Value),
-max_bytes: usize = 2 * 1024 * 1024,
-output_dir: []const u8,
-output_basename: []const u8,
-
-pub fn create(builder: *Builder, source: std.build.FileSource, style: Style) *ConfigHeaderStep {
- const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
- const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
- self.* = .{
- .builder = builder,
- .step = Step.init(base_id, name, builder.allocator, make),
- .source = source,
- .style = style,
- .values = std.StringHashMap(Value).init(builder.allocator),
- .output_dir = undefined,
- .output_basename = "config.h",
- };
- switch (source) {
- .path => |p| {
- const basename = std.fs.path.basename(p);
- if (std.mem.endsWith(u8, basename, ".h.in")) {
- self.output_basename = basename[0 .. basename.len - 3];
- }
- },
- else => {},
- }
- return self;
-}
-
-pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
- return addValuesInner(self, values) catch @panic("OOM");
-}
-
-fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
- inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
- switch (@typeInfo(field.type)) {
- .Null => {
- try self.values.put(field.name, .undef);
- },
- .Void => {
- try self.values.put(field.name, .defined);
- },
- .Bool => {
- try self.values.put(field.name, .{ .boolean = @field(values, field.name) });
- },
- .ComptimeInt => {
- try self.values.put(field.name, .{ .int = @field(values, field.name) });
- },
- .EnumLiteral => {
- try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) });
- },
- .Pointer => |ptr| {
- switch (@typeInfo(ptr.child)) {
- .Array => |array| {
- if (ptr.size == .One and array.child == u8) {
- try self.values.put(field.name, .{ .string = @field(values, field.name) });
- continue;
- }
- },
- else => {},
- }
-
- @compileError("unsupported ConfigHeaderStep value type: " ++
- @typeName(field.type));
- },
- else => @compileError("unsupported ConfigHeaderStep value type: " ++
- @typeName(field.type)),
- }
- }
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
- const gpa = self.builder.allocator;
- const src_path = self.source.getPath(self.builder);
- const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
-
- // The cache is used here not really as a way to speed things up - because writing
- // the data to a file would probably be very fast - but as a way to find a canonical
- // location to put build artifacts.
-
- // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
- // files, then two ConfigHeaderStep executing in parallel might clobber each other.
-
- // TODO port the cache system from the compiler to zig std lib. Until then
- // we construct the path directly, and no "cache hit" detection happens;
- // the files are always written.
- // Note there is very similar code over in WriteFileStep
- const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
- // Random bytes to make ConfigHeaderStep unique. Refresh this with new
- // random bytes when ConfigHeaderStep implementation is modified in a
- // non-backwards-compatible way.
- var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
- hash.update(self.source.getDisplayName());
- hash.update(contents);
-
- var digest: [16]u8 = undefined;
- hash.final(&digest);
- var hash_basename: [digest.len * 2]u8 = undefined;
- _ = std.fmt.bufPrint(
- &hash_basename,
- "{s}",
- .{std.fmt.fmtSliceHexLower(&digest)},
- ) catch unreachable;
-
- self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
- self.builder.cache_root, "o", &hash_basename,
- });
- var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
- std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
- return err;
- };
- defer dir.close();
-
- var values_copy = try self.values.clone();
- defer values_copy.deinit();
-
- var output = std.ArrayList(u8).init(gpa);
- defer output.deinit();
- try output.ensureTotalCapacity(contents.len);
-
- try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
-
- switch (self.style) {
- .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
- .cmake => try render_cmake(contents, &output, &values_copy, src_path),
- }
-
- try dir.writeFile(self.output_basename, output.items);
-}
-
-fn render_autoconf(
- contents: []const u8,
- output: *std.ArrayList(u8),
- values_copy: *std.StringHashMap(Value),
- src_path: []const u8,
-) !void {
- var any_errors = false;
- var line_index: u32 = 0;
- var line_it = std.mem.split(u8, contents, "\n");
- while (line_it.next()) |line| : (line_index += 1) {
- if (!std.mem.startsWith(u8, line, "#")) {
- try output.appendSlice(line);
- try output.appendSlice("\n");
- continue;
- }
- var it = std.mem.tokenize(u8, line[1..], " \t\r");
- const undef = it.next().?;
- if (!std.mem.eql(u8, undef, "undef")) {
- try output.appendSlice(line);
- try output.appendSlice("\n");
- continue;
- }
- const name = it.rest();
- const kv = values_copy.fetchRemove(name) orelse {
- std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
- src_path, line_index + 1, name,
- });
- any_errors = true;
- continue;
- };
- try renderValue(output, name, kv.value);
- }
-
- {
- var it = values_copy.iterator();
- while (it.next()) |entry| {
- const name = entry.key_ptr.*;
- std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
- }
- }
-
- if (any_errors) {
- return error.HeaderConfigFailed;
- }
-}
-
-fn render_cmake(
- contents: []const u8,
- output: *std.ArrayList(u8),
- values_copy: *std.StringHashMap(Value),
- src_path: []const u8,
-) !void {
- var any_errors = false;
- var line_index: u32 = 0;
- var line_it = std.mem.split(u8, contents, "\n");
- while (line_it.next()) |line| : (line_index += 1) {
- if (!std.mem.startsWith(u8, line, "#")) {
- try output.appendSlice(line);
- try output.appendSlice("\n");
- continue;
- }
- var it = std.mem.tokenize(u8, line[1..], " \t\r");
- const cmakedefine = it.next().?;
- if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
- try output.appendSlice(line);
- try output.appendSlice("\n");
- continue;
- }
- const name = it.next() orelse {
- std.debug.print("{s}:{d}: error: missing define name\n", .{
- src_path, line_index + 1,
- });
- any_errors = true;
- continue;
- };
- const kv = values_copy.fetchRemove(name) orelse {
- std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
- src_path, line_index + 1, name,
- });
- any_errors = true;
- continue;
- };
- try renderValue(output, name, kv.value);
- }
-
- {
- var it = values_copy.iterator();
- while (it.next()) |entry| {
- const name = entry.key_ptr.*;
- std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
- }
- }
-
- if (any_errors) {
- return error.HeaderConfigFailed;
- }
-}
-
-fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
- switch (value) {
- .undef => {
- try output.appendSlice("/* #undef ");
- try output.appendSlice(name);
- try output.appendSlice(" */\n");
- },
- .defined => {
- try output.appendSlice("#define ");
- try output.appendSlice(name);
- try output.appendSlice("\n");
- },
- .boolean => |b| {
- try output.appendSlice("#define ");
- try output.appendSlice(name);
- try output.appendSlice(" ");
- try output.appendSlice(if (b) "true\n" else "false\n");
- },
- .int => |i| {
- try output.writer().print("#define {s} {d}\n", .{ name, i });
- },
- .ident => |ident| {
- try output.writer().print("#define {s} {s}\n", .{ name, ident });
- },
- .string => |string| {
- // TODO: use C-specific escaping instead of zig string literals
- try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
- },
- }
-}
diff --git a/lib/std/build/EmulatableRunStep.zig b/lib/std/build/EmulatableRunStep.zig
deleted file mode 100644
index 52ce8edfac..0000000000
--- a/lib/std/build/EmulatableRunStep.zig
+++ /dev/null
@@ -1,215 +0,0 @@
-//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
-//! When a binary is foreign, but emulation for the target is disabled, the specified binary
-//! will not be run and therefore also not validated against its output.
-//! This step can be useful when wishing to run a built binary on multiple platforms,
-//! without having to verify if it's possible to be ran against.
-
-const std = @import("../std.zig");
-const build = std.build;
-const Step = std.build.Step;
-const Builder = std.build.Builder;
-const LibExeObjStep = std.build.LibExeObjStep;
-const RunStep = std.build.RunStep;
-
-const fs = std.fs;
-const process = std.process;
-const EnvMap = process.EnvMap;
-
-const EmulatableRunStep = @This();
-
-pub const base_id = .emulatable_run;
-
-const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
-
-step: Step,
-builder: *Builder,
-
-/// The artifact (executable) to be run by this step
-exe: *LibExeObjStep,
-
-/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
-expected_exit_code: ?u8 = 0,
-
-/// Override this field to modify the environment
-env_map: ?*EnvMap,
-
-/// Set this to modify the current working directory
-cwd: ?[]const u8,
-
-stdout_action: RunStep.StdIoAction = .inherit,
-stderr_action: RunStep.StdIoAction = .inherit,
-
-/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
-/// or through emulation.
-hide_foreign_binaries_warning: bool,
-
-/// Creates a step that will execute the given artifact. This step will allow running the
-/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
-/// When set to false, and the binary is foreign, running the executable is skipped.
-/// Asserts given artifact is an executable.
-pub fn create(builder: *Builder, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep {
- std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
- const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
-
- const option_name = "hide-foreign-warnings";
- const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
- break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
- } else false;
-
- self.* = .{
- .builder = builder,
- .step = Step.init(.emulatable_run, name, builder.allocator, make),
- .exe = artifact,
- .env_map = null,
- .cwd = null,
- .hide_foreign_binaries_warning = hide_warnings,
- };
- self.step.dependOn(&artifact.step);
-
- return self;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(EmulatableRunStep, "step", step);
- const host_info = self.builder.host;
-
- var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
- defer argv_list.deinit();
-
- const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
- switch (host_info.getExternalExecutor(self.exe.target_info, .{
- .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
- .link_libc = self.exe.is_linking_libc,
- })) {
- .native => {},
- .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
- .wine => |bin_name| if (self.builder.enable_wine) {
- try argv_list.append(bin_name);
- } else return,
- .qemu => |bin_name| if (self.builder.enable_qemu) {
- const glibc_dir_arg = if (need_cross_glibc)
- self.builder.glibc_runtimes_dir orelse return
- else
- null;
- try argv_list.append(bin_name);
- if (glibc_dir_arg) |dir| {
- // TODO look into making this a call to `linuxTriple`. This
- // needs the directory to be called "i686" rather than
- // "x86" which is why we do it manually here.
- const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
- const cpu_arch = self.exe.target.getCpuArch();
- const os_tag = self.exe.target.getOsTag();
- const abi = self.exe.target.getAbi();
- const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
- "i686"
- else
- @tagName(cpu_arch);
- const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
- dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
- });
-
- try argv_list.append("-L");
- try argv_list.append(full_dir);
- }
- } else return warnAboutForeignBinaries(self),
- .darling => |bin_name| if (self.builder.enable_darling) {
- try argv_list.append(bin_name);
- } else return warnAboutForeignBinaries(self),
- .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
- try argv_list.append(bin_name);
- try argv_list.append("--dir=.");
- } else return warnAboutForeignBinaries(self),
- else => return warnAboutForeignBinaries(self),
- }
-
- if (self.exe.target.isWindows()) {
- // On Windows we don't have rpaths so we have to add .dll search paths to PATH
- RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
- }
-
- const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
- try argv_list.append(executable_path);
-
- try RunStep.runCommand(
- argv_list.items,
- self.builder,
- self.expected_exit_code,
- self.stdout_action,
- self.stderr_action,
- .Inherit,
- self.env_map,
- self.cwd,
- false,
- );
-}
-
-pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
- self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
-}
-
-pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
- self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
-}
-
-fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
- if (step.hide_foreign_binaries_warning) return;
- const builder = step.builder;
- const artifact = step.exe;
-
- const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
- const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
- const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
- const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
- switch (builder.host.getExternalExecutor(target_info, .{
- .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
- .link_libc = artifact.is_linking_libc,
- })) {
- .native => unreachable,
- .bad_dl => |foreign_dl| {
- const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
- 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", .{
- host_dl, foreign_dl, host_dl,
- });
- },
- .bad_os_or_cpu => {
- std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
- host_name, foreign_name,
- });
- },
- .darling => if (!builder.enable_darling) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling darling.\n",
- .{ host_name, foreign_name },
- );
- },
- .rosetta => if (!builder.enable_rosetta) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling rosetta.\n",
- .{ host_name, foreign_name },
- );
- },
- .wine => if (!builder.enable_wine) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling wine.\n",
- .{ host_name, foreign_name },
- );
- },
- .qemu => if (!builder.enable_qemu) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling qemu.\n",
- .{ host_name, foreign_name },
- );
- },
- .wasmtime => {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling wasmtime.\n",
- .{ host_name, foreign_name },
- );
- },
- }
-}
diff --git a/lib/std/build/FmtStep.zig b/lib/std/build/FmtStep.zig
deleted file mode 100644
index 62923623f2..0000000000
--- a/lib/std/build/FmtStep.zig
+++ /dev/null
@@ -1,37 +0,0 @@
-const std = @import("../std.zig");
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const BufMap = std.BufMap;
-const mem = std.mem;
-
-const FmtStep = @This();
-
-pub const base_id = .fmt;
-
-step: Step,
-builder: *Builder,
-argv: [][]const u8,
-
-pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep {
- const self = builder.allocator.create(FmtStep) catch unreachable;
- const name = "zig fmt";
- self.* = FmtStep{
- .step = Step.init(.fmt, name, builder.allocator, make),
- .builder = builder,
- .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable,
- };
-
- self.argv[0] = builder.zig_exe;
- self.argv[1] = "fmt";
- for (paths) |path, i| {
- self.argv[2 + i] = builder.pathFromRoot(path);
- }
- return self;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(FmtStep, "step", step);
-
- return self.builder.spawnChild(self.argv);
-}
diff --git a/lib/std/build/InstallArtifactStep.zig b/lib/std/build/InstallArtifactStep.zig
deleted file mode 100644
index 537b8c8fd9..0000000000
--- a/lib/std/build/InstallArtifactStep.zig
+++ /dev/null
@@ -1,88 +0,0 @@
-const std = @import("../std.zig");
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const LibExeObjStep = std.build.LibExeObjStep;
-const InstallDir = std.build.InstallDir;
-
-pub const base_id = .install_artifact;
-
-step: Step,
-builder: *Builder,
-artifact: *LibExeObjStep,
-dest_dir: InstallDir,
-pdb_dir: ?InstallDir,
-h_dir: ?InstallDir,
-
-const Self = @This();
-
-pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self {
- if (artifact.install_step) |s| return s;
-
- const self = builder.allocator.create(Self) catch unreachable;
- self.* = Self{
- .builder = builder,
- .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
- .artifact = artifact,
- .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
- .obj => @panic("Cannot install a .obj build artifact."),
- .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
- .exe, .test_exe => InstallDir{ .bin = {} },
- .lib => InstallDir{ .lib = {} },
- },
- .pdb_dir = if (artifact.producesPdbFile()) blk: {
- if (artifact.kind == .exe or artifact.kind == .test_exe) {
- break :blk InstallDir{ .bin = {} };
- } else {
- break :blk InstallDir{ .lib = {} };
- }
- } else null,
- .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
- };
- self.step.dependOn(&artifact.step);
- artifact.install_step = self;
-
- builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
- if (self.artifact.isDynamicLibrary()) {
- if (artifact.major_only_filename) |name| {
- builder.pushInstalledFile(.lib, name);
- }
- if (artifact.name_only_filename) |name| {
- builder.pushInstalledFile(.lib, name);
- }
- if (self.artifact.target.isWindows()) {
- builder.pushInstalledFile(.lib, artifact.out_lib_filename);
- }
- }
- if (self.pdb_dir) |pdb_dir| {
- builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
- }
- if (self.h_dir) |h_dir| {
- builder.pushInstalledFile(h_dir, artifact.out_h_filename);
- }
- return self;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(Self, "step", step);
- const builder = self.builder;
-
- const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
- try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
- if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
- try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
- }
- if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
- const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
- try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
- }
- if (self.pdb_dir) |pdb_dir| {
- const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
- try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
- }
- if (self.h_dir) |h_dir| {
- const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
- try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
- }
- self.artifact.installed_path = full_dest_path;
-}
diff --git a/lib/std/build/InstallDirStep.zig b/lib/std/build/InstallDirStep.zig
deleted file mode 100644
index 0a41e1aaef..0000000000
--- a/lib/std/build/InstallDirStep.zig
+++ /dev/null
@@ -1,95 +0,0 @@
-const std = @import("../std.zig");
-const mem = std.mem;
-const fs = std.fs;
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const InstallDir = std.build.InstallDir;
-const InstallDirStep = @This();
-const log = std.log;
-
-step: Step,
-builder: *Builder,
-options: Options,
-/// This is used by the build system when a file being installed comes from one
-/// package but is being installed by another.
-override_source_builder: ?*Builder = null,
-
-pub const base_id = .install_dir;
-
-pub const Options = struct {
- source_dir: []const u8,
- install_dir: InstallDir,
- install_subdir: []const u8,
- /// File paths which end in any of these suffixes will be excluded
- /// from being installed.
- exclude_extensions: []const []const u8 = &.{},
- /// File paths which end in any of these suffixes will result in
- /// empty files being installed. This is mainly intended for large
- /// test.zig files in order to prevent needless installation bloat.
- /// However if the files were not present at all, then
- /// `@import("test.zig")` would be a compile error.
- blank_extensions: []const []const u8 = &.{},
-
- fn dupe(self: Options, b: *Builder) Options {
- return .{
- .source_dir = b.dupe(self.source_dir),
- .install_dir = self.install_dir.dupe(b),
- .install_subdir = b.dupe(self.install_subdir),
- .exclude_extensions = b.dupeStrings(self.exclude_extensions),
- .blank_extensions = b.dupeStrings(self.blank_extensions),
- };
- }
-};
-
-pub fn init(
- builder: *Builder,
- options: Options,
-) InstallDirStep {
- builder.pushInstalledFile(options.install_dir, options.install_subdir);
- return InstallDirStep{
- .builder = builder,
- .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
- .options = options.dupe(builder),
- };
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(InstallDirStep, "step", step);
- const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
- const src_builder = self.override_source_builder orelse self.builder;
- const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
- var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
- log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
- full_src_dir, @errorName(err),
- });
- return error.StepFailed;
- };
- defer src_dir.close();
- var it = try src_dir.walk(self.builder.allocator);
- next_entry: while (try it.next()) |entry| {
- for (self.options.exclude_extensions) |ext| {
- if (mem.endsWith(u8, entry.path, ext)) {
- continue :next_entry;
- }
- }
-
- const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
- const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
-
- switch (entry.kind) {
- .Directory => try fs.cwd().makePath(dest_path),
- .File => {
- for (self.options.blank_extensions) |ext| {
- if (mem.endsWith(u8, entry.path, ext)) {
- try self.builder.truncateFile(dest_path);
- continue :next_entry;
- }
- }
-
- try self.builder.updateFile(full_path, dest_path);
- },
- else => continue,
- }
- }
-}
diff --git a/lib/std/build/InstallFileStep.zig b/lib/std/build/InstallFileStep.zig
deleted file mode 100644
index 37203e64c5..0000000000
--- a/lib/std/build/InstallFileStep.zig
+++ /dev/null
@@ -1,42 +0,0 @@
-const std = @import("../std.zig");
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const FileSource = std.build.FileSource;
-const InstallDir = std.build.InstallDir;
-const InstallFileStep = @This();
-
-pub const base_id = .install_file;
-
-step: Step,
-builder: *Builder,
-source: FileSource,
-dir: InstallDir,
-dest_rel_path: []const u8,
-/// This is used by the build system when a file being installed comes from one
-/// package but is being installed by another.
-override_source_builder: ?*Builder = null,
-
-pub fn init(
- builder: *Builder,
- source: FileSource,
- dir: InstallDir,
- dest_rel_path: []const u8,
-) InstallFileStep {
- builder.pushInstalledFile(dir, dest_rel_path);
- return InstallFileStep{
- .builder = builder,
- .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
- .source = source.dupe(builder),
- .dir = dir.dupe(builder),
- .dest_rel_path = builder.dupePath(dest_rel_path),
- };
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(InstallFileStep, "step", step);
- const src_builder = self.override_source_builder orelse self.builder;
- const full_src_path = self.source.getPath(src_builder);
- const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
- try self.builder.updateFile(full_src_path, full_dest_path);
-}
diff --git a/lib/std/build/InstallRawStep.zig b/lib/std/build/InstallRawStep.zig
deleted file mode 100644
index e8266dff5a..0000000000
--- a/lib/std/build/InstallRawStep.zig
+++ /dev/null
@@ -1,106 +0,0 @@
-//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
-//! subcommand rather than containing an implementation directly.
-
-const std = @import("std");
-const InstallRawStep = @This();
-
-const Allocator = std.mem.Allocator;
-const ArenaAllocator = std.heap.ArenaAllocator;
-const ArrayListUnmanaged = std.ArrayListUnmanaged;
-const Builder = std.build.Builder;
-const File = std.fs.File;
-const InstallDir = std.build.InstallDir;
-const LibExeObjStep = std.build.LibExeObjStep;
-const Step = std.build.Step;
-const elf = std.elf;
-const fs = std.fs;
-const io = std.io;
-const sort = std.sort;
-
-pub const base_id = .install_raw;
-
-pub const RawFormat = enum {
- bin,
- hex,
-};
-
-step: Step,
-builder: *Builder,
-artifact: *LibExeObjStep,
-dest_dir: InstallDir,
-dest_filename: []const u8,
-options: CreateOptions,
-output_file: std.build.GeneratedFile,
-
-pub const CreateOptions = struct {
- format: ?RawFormat = null,
- dest_dir: ?InstallDir = null,
- only_section: ?[]const u8 = null,
- pad_to: ?u64 = null,
-};
-
-pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep {
- const self = builder.allocator.create(InstallRawStep) catch unreachable;
- self.* = InstallRawStep{
- .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
- .builder = builder,
- .artifact = artifact,
- .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
- .obj => unreachable,
- .@"test" => unreachable,
- .exe, .test_exe => .bin,
- .lib => unreachable,
- },
- .dest_filename = dest_filename,
- .options = options,
- .output_file = std.build.GeneratedFile{ .step = &self.step },
- };
- self.step.dependOn(&artifact.step);
-
- builder.pushInstalledFile(self.dest_dir, dest_filename);
- return self;
-}
-
-pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource {
- return std.build.FileSource{ .generated = &self.output_file };
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(InstallRawStep, "step", step);
- const b = self.builder;
-
- if (self.artifact.target.getObjectFormat() != .elf) {
- std.debug.print("InstallRawStep only works with ELF format.\n", .{});
- return error.InvalidObjectFormat;
- }
-
- const full_src_path = self.artifact.getOutputSource().getPath(b);
- const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
- self.output_file.path = full_dest_path;
-
- fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable;
-
- var argv_list = std.ArrayList([]const u8).init(b.allocator);
- try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
-
- if (self.options.only_section) |only_section| {
- try argv_list.appendSlice(&.{ "-j", only_section });
- }
- if (self.options.pad_to) |pad_to| {
- try argv_list.appendSlice(&.{
- "--pad-to",
- b.fmt("{d}", .{pad_to}),
- });
- }
- if (self.options.format) |format| switch (format) {
- .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
- .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
- };
-
- try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
- _ = try self.builder.execFromStep(argv_list.items, &self.step);
-}
-
-test {
- std.testing.refAllDecls(InstallRawStep);
-}
diff --git a/lib/std/build/LibExeObjStep.zig b/lib/std/build/LibExeObjStep.zig
deleted file mode 100644
index 6ee5205b50..0000000000
--- a/lib/std/build/LibExeObjStep.zig
+++ /dev/null
@@ -1,2047 +0,0 @@
-const builtin = @import("builtin");
-const std = @import("../std.zig");
-const mem = std.mem;
-const log = std.log;
-const fs = std.fs;
-const assert = std.debug.assert;
-const panic = std.debug.panic;
-const ArrayList = std.ArrayList;
-const StringHashMap = std.StringHashMap;
-const Sha256 = std.crypto.hash.sha2.Sha256;
-const Allocator = mem.Allocator;
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const CrossTarget = std.zig.CrossTarget;
-const NativeTargetInfo = std.zig.system.NativeTargetInfo;
-const FileSource = std.build.FileSource;
-const PkgConfigPkg = Builder.PkgConfigPkg;
-const PkgConfigError = Builder.PkgConfigError;
-const ExecError = Builder.ExecError;
-const Pkg = std.build.Pkg;
-const VcpkgRoot = std.build.VcpkgRoot;
-const InstallDir = std.build.InstallDir;
-const InstallArtifactStep = std.build.InstallArtifactStep;
-const GeneratedFile = std.build.GeneratedFile;
-const InstallRawStep = std.build.InstallRawStep;
-const EmulatableRunStep = std.build.EmulatableRunStep;
-const CheckObjectStep = std.build.CheckObjectStep;
-const RunStep = std.build.RunStep;
-const OptionsStep = std.build.OptionsStep;
-const ConfigHeaderStep = std.build.ConfigHeaderStep;
-const LibExeObjStep = @This();
-
-pub const base_id = .lib_exe_obj;
-
-step: Step,
-builder: *Builder,
-name: []const u8,
-target: CrossTarget,
-target_info: NativeTargetInfo,
-optimize: std.builtin.Mode,
-linker_script: ?FileSource = null,
-version_script: ?[]const u8 = null,
-out_filename: []const u8,
-linkage: ?Linkage = null,
-version: ?std.builtin.Version,
-kind: Kind,
-major_only_filename: ?[]const u8,
-name_only_filename: ?[]const u8,
-strip: ?bool,
-unwind_tables: ?bool,
-// keep in sync with src/link.zig:CompressDebugSections
-compress_debug_sections: enum { none, zlib } = .none,
-lib_paths: ArrayList([]const u8),
-rpaths: ArrayList([]const u8),
-framework_dirs: ArrayList([]const u8),
-frameworks: StringHashMap(FrameworkLinkInfo),
-verbose_link: bool,
-verbose_cc: bool,
-emit_analysis: EmitOption = .default,
-emit_asm: EmitOption = .default,
-emit_bin: EmitOption = .default,
-emit_docs: EmitOption = .default,
-emit_implib: EmitOption = .default,
-emit_llvm_bc: EmitOption = .default,
-emit_llvm_ir: EmitOption = .default,
-// Lots of things depend on emit_h having a consistent path,
-// so it is not an EmitOption for now.
-emit_h: bool = false,
-bundle_compiler_rt: ?bool = null,
-single_threaded: ?bool = null,
-stack_protector: ?bool = null,
-disable_stack_probing: bool,
-disable_sanitize_c: bool,
-sanitize_thread: bool,
-rdynamic: bool,
-import_memory: bool = false,
-/// For WebAssembly targets, this will allow for undefined symbols to
-/// be imported from the host environment.
-import_symbols: bool = false,
-import_table: bool = false,
-export_table: bool = false,
-initial_memory: ?u64 = null,
-max_memory: ?u64 = null,
-shared_memory: bool = false,
-global_base: ?u64 = null,
-c_std: Builder.CStd,
-override_lib_dir: ?[]const u8,
-main_pkg_path: ?[]const u8,
-exec_cmd_args: ?[]const ?[]const u8,
-name_prefix: []const u8,
-filter: ?[]const u8,
-test_evented_io: bool = false,
-test_runner: ?[]const u8,
-code_model: std.builtin.CodeModel = .default,
-wasi_exec_model: ?std.builtin.WasiExecModel = null,
-/// Symbols to be exported when compiling to wasm
-export_symbol_names: []const []const u8 = &.{},
-
-root_src: ?FileSource,
-out_h_filename: []const u8,
-out_lib_filename: []const u8,
-out_pdb_filename: []const u8,
-packages: ArrayList(Pkg),
-
-object_src: []const u8,
-
-link_objects: ArrayList(LinkObject),
-include_dirs: ArrayList(IncludeDir),
-c_macros: ArrayList([]const u8),
-installed_headers: ArrayList(*std.build.Step),
-output_dir: ?[]const u8,
-is_linking_libc: bool = false,
-is_linking_libcpp: bool = false,
-vcpkg_bin_path: ?[]const u8 = null,
-
-/// This may be set in order to override the default install directory
-override_dest_dir: ?InstallDir,
-installed_path: ?[]const u8,
-install_step: ?*InstallArtifactStep,
-
-/// Base address for an executable image.
-image_base: ?u64 = null,
-
-libc_file: ?FileSource = null,
-
-valgrind_support: ?bool = null,
-each_lib_rpath: ?bool = null,
-/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
-/// which can be used to coordinate a stripped binary with its debug symbols.
-/// As an example, the bloaty project refuses to work unless its inputs have
-/// build ids, in order to prevent accidental mismatches.
-/// The default is to not include this section because it slows down linking.
-build_id: ?bool = null,
-
-/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
-/// file.
-link_eh_frame_hdr: bool = false,
-link_emit_relocs: bool = false,
-
-/// Place every function in its own section so that unused ones may be
-/// safely garbage-collected during the linking phase.
-link_function_sections: bool = false,
-
-/// Remove functions and data that are unreachable by the entry point or
-/// exported symbols.
-link_gc_sections: ?bool = null,
-
-linker_allow_shlib_undefined: ?bool = null,
-
-/// Permit read-only relocations in read-only segments. Disallowed by default.
-link_z_notext: bool = false,
-
-/// Force all relocations to be read-only after processing.
-link_z_relro: bool = true,
-
-/// Allow relocations to be lazily processed after load.
-link_z_lazy: bool = false,
-
-/// Common page size
-link_z_common_page_size: ?u64 = null,
-
-/// Maximum page size
-link_z_max_page_size: ?u64 = null,
-
-/// (Darwin) Install name for the dylib
-install_name: ?[]const u8 = null,
-
-/// (Darwin) Path to entitlements file
-entitlements: ?[]const u8 = null,
-
-/// (Darwin) Size of the pagezero segment.
-pagezero_size: ?u64 = null,
-
-/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
-/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
-/// option.
-/// By default, if no option is specified, the linker assumes `paths_first` as the default
-/// search strategy.
-search_strategy: ?enum { paths_first, dylibs_first } = null,
-
-/// (Darwin) Set size of the padding between the end of load commands
-/// and start of `__TEXT,__text` section.
-headerpad_size: ?u32 = null,
-
-/// (Darwin) Automatically Set size of the padding between the end of load commands
-/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
-headerpad_max_install_names: bool = false,
-
-/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
-dead_strip_dylibs: bool = false,
-
-/// Position Independent Code
-force_pic: ?bool = null,
-
-/// Position Independent Executable
-pie: ?bool = null,
-
-red_zone: ?bool = null,
-
-omit_frame_pointer: ?bool = null,
-dll_export_fns: ?bool = null,
-
-subsystem: ?std.Target.SubSystem = null,
-
-entry_symbol_name: ?[]const u8 = null,
-
-/// Overrides the default stack size
-stack_size: ?u64 = null,
-
-want_lto: ?bool = null,
-use_llvm: ?bool = null,
-use_lld: ?bool = null,
-
-output_path_source: GeneratedFile,
-output_lib_path_source: GeneratedFile,
-output_h_path_source: GeneratedFile,
-output_pdb_path_source: GeneratedFile,
-
-pub const CSourceFiles = struct {
- files: []const []const u8,
- flags: []const []const u8,
-};
-
-pub const CSourceFile = struct {
- source: FileSource,
- args: []const []const u8,
-
- pub fn dupe(self: CSourceFile, b: *Builder) CSourceFile {
- return .{
- .source = self.source.dupe(b),
- .args = b.dupeStrings(self.args),
- };
- }
-};
-
-pub const LinkObject = union(enum) {
- static_path: FileSource,
- other_step: *LibExeObjStep,
- system_lib: SystemLib,
- assembly_file: FileSource,
- c_source_file: *CSourceFile,
- c_source_files: *CSourceFiles,
-};
-
-pub const SystemLib = struct {
- name: []const u8,
- needed: bool,
- weak: bool,
- use_pkg_config: enum {
- /// Don't use pkg-config, just pass -lfoo where foo is name.
- no,
- /// Try to get information on how to link the library from pkg-config.
- /// If that fails, fall back to passing -lfoo where foo is name.
- yes,
- /// Try to get information on how to link the library from pkg-config.
- /// If that fails, error out.
- force,
- },
-};
-
-const FrameworkLinkInfo = struct {
- needed: bool = false,
- weak: bool = false,
-};
-
-pub const IncludeDir = union(enum) {
- raw_path: []const u8,
- raw_path_system: []const u8,
- other_step: *LibExeObjStep,
- config_header_step: *ConfigHeaderStep,
-};
-
-pub const Options = struct {
- name: []const u8,
- root_source_file: ?FileSource = null,
- target: CrossTarget,
- optimize: std.builtin.Mode,
- kind: Kind,
- linkage: ?Linkage = null,
- version: ?std.builtin.Version = null,
-};
-
-pub const Kind = enum {
- exe,
- lib,
- obj,
- @"test",
- test_exe,
-};
-
-pub const Linkage = enum { dynamic, static };
-
-pub const EmitOption = union(enum) {
- default: void,
- no_emit: void,
- emit: void,
- emit_to: []const u8,
-
- fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 {
- return switch (self) {
- .no_emit => b.fmt("-fno-{s}", .{arg_name}),
- .default => null,
- .emit => b.fmt("-f{s}", .{arg_name}),
- .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
- };
- }
-};
-
-pub fn create(builder: *Builder, options: Options) *LibExeObjStep {
- const name = builder.dupe(options.name);
- const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
- if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
- panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
- }
-
- const self = builder.allocator.create(LibExeObjStep) catch unreachable;
- self.* = LibExeObjStep{
- .strip = null,
- .unwind_tables = null,
- .builder = builder,
- .verbose_link = false,
- .verbose_cc = false,
- .optimize = options.optimize,
- .target = options.target,
- .linkage = options.linkage,
- .kind = options.kind,
- .root_src = root_src,
- .name = name,
- .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
- .step = Step.init(base_id, name, builder.allocator, make),
- .version = options.version,
- .out_filename = undefined,
- .out_h_filename = builder.fmt("{s}.h", .{name}),
- .out_lib_filename = undefined,
- .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
- .major_only_filename = null,
- .name_only_filename = null,
- .packages = ArrayList(Pkg).init(builder.allocator),
- .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
- .link_objects = ArrayList(LinkObject).init(builder.allocator),
- .c_macros = ArrayList([]const u8).init(builder.allocator),
- .lib_paths = ArrayList([]const u8).init(builder.allocator),
- .rpaths = ArrayList([]const u8).init(builder.allocator),
- .framework_dirs = ArrayList([]const u8).init(builder.allocator),
- .installed_headers = ArrayList(*std.build.Step).init(builder.allocator),
- .object_src = undefined,
- .c_std = Builder.CStd.C99,
- .override_lib_dir = null,
- .main_pkg_path = null,
- .exec_cmd_args = null,
- .name_prefix = "",
- .filter = null,
- .test_runner = null,
- .disable_stack_probing = false,
- .disable_sanitize_c = false,
- .sanitize_thread = false,
- .rdynamic = false,
- .output_dir = null,
- .override_dest_dir = null,
- .installed_path = null,
- .install_step = null,
-
- .output_path_source = GeneratedFile{ .step = &self.step },
- .output_lib_path_source = GeneratedFile{ .step = &self.step },
- .output_h_path_source = GeneratedFile{ .step = &self.step },
- .output_pdb_path_source = GeneratedFile{ .step = &self.step },
-
- .target_info = undefined, // populated in computeOutFileNames
- };
- self.computeOutFileNames();
- if (root_src) |rs| rs.addStepDependencies(&self.step);
- return self;
-}
-
-fn computeOutFileNames(self: *LibExeObjStep) void {
- self.target_info = NativeTargetInfo.detect(self.target) catch
- unreachable;
-
- const target = self.target_info.target;
-
- self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
- .root_name = self.name,
- .target = target,
- .output_mode = switch (self.kind) {
- .lib => .Lib,
- .obj => .Obj,
- .exe, .@"test", .test_exe => .Exe,
- },
- .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
- .dynamic => .Dynamic,
- .static => .Static,
- }) else null,
- .version = self.version,
- }) catch unreachable;
-
- if (self.kind == .lib) {
- if (self.linkage != null and self.linkage.? == .static) {
- self.out_lib_filename = self.out_filename;
- } else if (self.version) |version| {
- if (target.isDarwin()) {
- self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
- self.name,
- version.major,
- });
- self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
- self.out_lib_filename = self.out_filename;
- } else if (target.os.tag == .windows) {
- self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
- } else {
- self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
- self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
- self.out_lib_filename = self.out_filename;
- }
- } else {
- if (target.isDarwin()) {
- self.out_lib_filename = self.out_filename;
- } else if (target.os.tag == .windows) {
- self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
- } else {
- self.out_lib_filename = self.out_filename;
- }
- }
- if (self.output_dir != null) {
- self.output_lib_path_source.path = self.builder.pathJoin(
- &.{ self.output_dir.?, self.out_lib_filename },
- );
- }
- }
-}
-
-pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
- self.output_dir = self.builder.dupePath(dir);
-}
-
-pub fn install(self: *LibExeObjStep) void {
- self.builder.installArtifact(self);
-}
-
-pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
- return self.builder.installRaw(self, dest_filename, options);
-}
-
-pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
- const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
- a.builder.getInstallStep().dependOn(&install_file.step);
- a.installed_headers.append(&install_file.step) catch unreachable;
-}
-
-pub fn installHeadersDirectory(
- a: *LibExeObjStep,
- src_dir_path: []const u8,
- dest_rel_path: []const u8,
-) void {
- return installHeadersDirectoryOptions(a, .{
- .source_dir = src_dir_path,
- .install_dir = .header,
- .install_subdir = dest_rel_path,
- });
-}
-
-pub fn installHeadersDirectoryOptions(
- a: *LibExeObjStep,
- options: std.build.InstallDirStep.Options,
-) void {
- const install_dir = a.builder.addInstallDirectory(options);
- a.builder.getInstallStep().dependOn(&install_dir.step);
- a.installed_headers.append(&install_dir.step) catch unreachable;
-}
-
-pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
- assert(l.kind == .lib);
- const install_step = a.builder.getInstallStep();
- // Copy each element from installed_headers, modifying the builder
- // to be the new parent's builder.
- for (l.installed_headers.items) |step| {
- const step_copy = switch (step.id) {
- inline .install_file, .install_dir => |id| blk: {
- const T = id.Type();
- const ptr = a.builder.allocator.create(T) catch unreachable;
- ptr.* = step.cast(T).?.*;
- ptr.override_source_builder = ptr.builder;
- ptr.builder = a.builder;
- break :blk &ptr.step;
- },
- else => unreachable,
- };
- a.installed_headers.append(step_copy) catch unreachable;
- install_step.dependOn(step_copy);
- }
- a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
-}
-
-/// Creates a `RunStep` with an executable built with `addExecutable`.
-/// Add command line arguments with `addArg`.
-pub fn run(exe: *LibExeObjStep) *RunStep {
- assert(exe.kind == .exe or exe.kind == .test_exe);
-
- // It doesn't have to be native. We catch that if you actually try to run it.
- // Consider that this is declarative; the run step may not be run unless a user
- // option is supplied.
- const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
- run_step.addArtifactArg(exe);
-
- if (exe.kind == .test_exe) {
- run_step.addArg(exe.builder.zig_exe);
- }
-
- if (exe.vcpkg_bin_path) |path| {
- run_step.addPathDir(path);
- }
-
- return run_step;
-}
-
-/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
-/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
-/// When a binary cannot be ran through emulation or the option is disabled, a warning
-/// will be printed and the binary will *NOT* be ran.
-pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
- assert(exe.kind == .exe or exe.kind == .test_exe);
-
- const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
- if (exe.vcpkg_bin_path) |path| {
- RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
- }
- return run_step;
-}
-
-pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
- return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
-}
-
-pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
- self.linker_script = source.dupe(self.builder);
- source.addStepDependencies(&self.step);
-}
-
-pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
-}
-
-pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{
- .needed = true,
- }) catch unreachable;
-}
-
-pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{
- .weak = true,
- }) catch unreachable;
-}
-
-/// Returns whether the library, executable, or object depends on a particular system library.
-pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
- if (isLibCLibrary(name)) {
- return self.is_linking_libc;
- }
- if (isLibCppLibrary(name)) {
- return self.is_linking_libcpp;
- }
- for (self.link_objects.items) |link_object| {
- switch (link_object) {
- .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
- else => continue,
- }
- }
- return false;
-}
-
-pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
- assert(lib.kind == .lib);
- self.linkLibraryOrObject(lib);
-}
-
-pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
- return self.kind == .lib and self.linkage == Linkage.dynamic;
-}
-
-pub fn isStaticLibrary(self: *LibExeObjStep) bool {
- return self.kind == .lib and self.linkage != Linkage.dynamic;
-}
-
-pub fn producesPdbFile(self: *LibExeObjStep) bool {
- if (!self.target.isWindows() and !self.target.isUefi()) return false;
- if (self.target.getObjectFormat() == .c) return false;
- if (self.strip == true) return false;
- return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
-}
-
-pub fn linkLibC(self: *LibExeObjStep) void {
- self.is_linking_libc = true;
-}
-
-pub fn linkLibCpp(self: *LibExeObjStep) void {
- self.is_linking_libcpp = true;
-}
-
-/// If the value is omitted, it is set to 1.
-/// `name` and `value` need not live longer than the function call.
-pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
- const macro = std.build.constructCMacro(self.builder.allocator, name, value);
- self.c_macros.append(macro) catch unreachable;
-}
-
-/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
-pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
- self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
-}
-
-/// This one has no integration with anything, it just puts -lname on the command line.
-/// Prefer to use `linkSystemLibrary` instead.
-pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = false,
- .weak = false,
- .use_pkg_config = .no,
- },
- }) catch unreachable;
-}
-
-/// This one has no integration with anything, it just puts -needed-lname on the command line.
-/// Prefer to use `linkSystemLibraryNeeded` instead.
-pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = true,
- .weak = false,
- .use_pkg_config = .no,
- },
- }) catch unreachable;
-}
-
-/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
-/// command line. Prefer to use `linkSystemLibraryWeak` instead.
-pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = false,
- .weak = true,
- .use_pkg_config = .no,
- },
- }) catch unreachable;
-}
-
-/// This links against a system library, exclusively using pkg-config to find the library.
-/// Prefer to use `linkSystemLibrary` instead.
-pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(lib_name),
- .needed = false,
- .weak = false,
- .use_pkg_config = .force,
- },
- }) catch unreachable;
-}
-
-/// This links against a system library, exclusively using pkg-config to find the library.
-/// Prefer to use `linkSystemLibraryNeeded` instead.
-pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(lib_name),
- .needed = true,
- .weak = false,
- .use_pkg_config = .force,
- },
- }) catch unreachable;
-}
-
-/// Run pkg-config for the given library name and parse the output, returning the arguments
-/// that should be passed to zig to link the given library.
-pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
- const pkg_name = match: {
- // First we have to map the library name to pkg config name. Unfortunately,
- // there are several examples where this is not straightforward:
- // -lSDL2 -> pkg-config sdl2
- // -lgdk-3 -> pkg-config gdk-3.0
- // -latk-1.0 -> pkg-config atk
- const pkgs = try getPkgConfigList(self.builder);
-
- // Exact match means instant winner.
- for (pkgs) |pkg| {
- if (mem.eql(u8, pkg.name, lib_name)) {
- break :match pkg.name;
- }
- }
-
- // Next we'll try ignoring case.
- for (pkgs) |pkg| {
- if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
- break :match pkg.name;
- }
- }
-
- // Now try appending ".0".
- for (pkgs) |pkg| {
- if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
- if (pos != 0) continue;
- if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
- break :match pkg.name;
- }
- }
- }
-
- // Trimming "-1.0".
- if (mem.endsWith(u8, lib_name, "-1.0")) {
- const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
- for (pkgs) |pkg| {
- if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
- break :match pkg.name;
- }
- }
- }
-
- return error.PackageNotFound;
- };
-
- var code: u8 = undefined;
- const stdout = if (self.builder.execAllowFail(&[_][]const u8{
- "pkg-config",
- pkg_name,
- "--cflags",
- "--libs",
- }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
- error.ProcessTerminated => return error.PkgConfigCrashed,
- error.ExecNotSupported => return error.PkgConfigFailed,
- error.ExitCodeFailure => return error.PkgConfigFailed,
- error.FileNotFound => return error.PkgConfigNotInstalled,
- error.ChildExecFailed => return error.PkgConfigFailed,
- else => return err,
- };
-
- var zig_args = ArrayList([]const u8).init(self.builder.allocator);
- defer zig_args.deinit();
-
- var it = mem.tokenize(u8, stdout, " \r\n\t");
- while (it.next()) |tok| {
- if (mem.eql(u8, tok, "-I")) {
- const dir = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
- } else if (mem.startsWith(u8, tok, "-I")) {
- try zig_args.append(tok);
- } else if (mem.eql(u8, tok, "-L")) {
- const dir = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
- } else if (mem.startsWith(u8, tok, "-L")) {
- try zig_args.append(tok);
- } else if (mem.eql(u8, tok, "-l")) {
- const lib = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
- } else if (mem.startsWith(u8, tok, "-l")) {
- try zig_args.append(tok);
- } else if (mem.eql(u8, tok, "-D")) {
- const macro = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
- } else if (mem.startsWith(u8, tok, "-D")) {
- try zig_args.append(tok);
- } else if (self.builder.verbose) {
- log.warn("Ignoring pkg-config flag '{s}'", .{tok});
- }
- }
-
- return zig_args.toOwnedSlice();
-}
-
-pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
- self.linkSystemLibraryInner(name, .{});
-}
-
-pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
- self.linkSystemLibraryInner(name, .{ .needed = true });
-}
-
-pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
- self.linkSystemLibraryInner(name, .{ .weak = true });
-}
-
-fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
- needed: bool = false,
- weak: bool = false,
-}) void {
- if (isLibCLibrary(name)) {
- self.linkLibC();
- return;
- }
- if (isLibCppLibrary(name)) {
- self.linkLibCpp();
- return;
- }
-
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = opts.needed,
- .weak = opts.weak,
- .use_pkg_config = .yes,
- },
- }) catch unreachable;
-}
-
-pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
- assert(self.kind == .@"test" or self.kind == .test_exe);
- self.name_prefix = self.builder.dupe(text);
-}
-
-pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
- assert(self.kind == .@"test" or self.kind == .test_exe);
- self.filter = if (text) |t| self.builder.dupe(t) else null;
-}
-
-pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
- assert(self.kind == .@"test" or self.kind == .test_exe);
- self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
-}
-
-/// Handy when you have many C/C++ source files and want them all to have the same flags.
-pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
- const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
-
- const files_copy = self.builder.dupeStrings(files);
- const flags_copy = self.builder.dupeStrings(flags);
-
- c_source_files.* = .{
- .files = files_copy,
- .flags = flags_copy,
- };
- self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
-}
-
-pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
- self.addCSourceFileSource(.{
- .args = flags,
- .source = .{ .path = file },
- });
-}
-
-pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
- const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
- c_source_file.* = source.dupe(self.builder);
- self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
- source.source.addStepDependencies(&self.step);
-}
-
-pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
- self.verbose_link = value;
-}
-
-pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
- self.verbose_cc = value;
-}
-
-pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
- self.override_lib_dir = self.builder.dupePath(dir_path);
-}
-
-pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
- self.main_pkg_path = self.builder.dupePath(dir_path);
-}
-
-pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
- self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
-}
-
-/// Returns the generated executable, library or object file.
-/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
-pub fn getOutputSource(self: *LibExeObjStep) FileSource {
- return FileSource{ .generated = &self.output_path_source };
-}
-
-/// Returns the generated import library. This function can only be called for libraries.
-pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
- assert(self.kind == .lib);
- return FileSource{ .generated = &self.output_lib_path_source };
-}
-
-/// Returns the generated header file.
-/// This function can only be called for libraries or object files which have `emit_h` set.
-pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
- assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
- assert(self.emit_h);
- return FileSource{ .generated = &self.output_h_path_source };
-}
-
-/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
-pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
- // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
- assert(self.target.isWindows() or self.target.isUefi());
- return FileSource{ .generated = &self.output_pdb_path_source };
-}
-
-pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
- self.link_objects.append(.{
- .assembly_file = .{ .path = self.builder.dupe(path) },
- }) catch unreachable;
-}
-
-pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
- const source_duped = source.dupe(self.builder);
- self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
- source_duped.addStepDependencies(&self.step);
-}
-
-pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
- self.addObjectFileSource(.{ .path = source_file });
-}
-
-pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
- self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
- source.addStepDependencies(&self.step);
-}
-
-pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
- assert(obj.kind == .obj);
- self.linkLibraryOrObject(obj);
-}
-
-pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
-pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
-pub const addLibPath = @compileError("deprecated, use addLibraryPath");
-pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
-
-pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
- self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
-}
-
-pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
- self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
-}
-
-pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
- self.step.dependOn(&config_header.step);
- self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
-}
-
-pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
- self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
-}
-
-pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
- self.rpaths.append(self.builder.dupe(path)) catch unreachable;
-}
-
-pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
- self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
-}
-
-pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
- self.packages.append(self.builder.dupePkg(package)) catch unreachable;
- self.addRecursiveBuildDeps(package);
-}
-
-pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
- self.addPackage(options.getPackage(package_name));
-}
-
-fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
- package.source.addStepDependencies(&self.step);
- if (package.dependencies) |deps| {
- for (deps) |dep| {
- self.addRecursiveBuildDeps(dep);
- }
- }
-}
-
-pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
- self.addPackage(Pkg{
- .name = self.builder.dupe(name),
- .source = .{ .path = self.builder.dupe(pkg_index_path) },
- });
-}
-
-/// If Vcpkg was found on the system, it will be added to include and lib
-/// paths for the specified target.
-pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
- // Ideally in the Unattempted case we would call the function recursively
- // after findVcpkgRoot and have only one switch statement, but the compiler
- // cannot resolve the error set.
- switch (self.builder.vcpkg_root) {
- .unattempted => {
- self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
- VcpkgRoot{ .found = root }
- else
- .not_found;
- },
- .not_found => return error.VcpkgNotFound,
- .found => {},
- }
-
- switch (self.builder.vcpkg_root) {
- .unattempted => unreachable,
- .not_found => return error.VcpkgNotFound,
- .found => |root| {
- const allocator = self.builder.allocator;
- const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
- defer self.builder.allocator.free(triplet);
-
- const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
- errdefer allocator.free(include_path);
- try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
-
- const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
- try self.lib_paths.append(lib_path);
-
- self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
- },
- }
-}
-
-pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
- assert(self.kind == .@"test");
- const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
- for (args) |arg, i| {
- duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
- }
- self.exec_cmd_args = duped_args;
-}
-
-fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
- self.step.dependOn(&other.step);
- self.link_objects.append(.{ .other_step = other }) catch unreachable;
- self.include_dirs.append(.{ .other_step = other }) catch unreachable;
-}
-
-fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
- const builder = self.builder;
-
- try zig_args.append("--pkg-begin");
- try zig_args.append(pkg.name);
- try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
-
- if (pkg.dependencies) |dependencies| {
- for (dependencies) |sub_pkg| {
- try self.makePackageCmd(sub_pkg, zig_args);
- }
- }
-
- try zig_args.append("--pkg-end");
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(LibExeObjStep, "step", step);
- const builder = self.builder;
-
- if (self.root_src == null and self.link_objects.items.len == 0) {
- log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
- return error.NeedAnObject;
- }
-
- var zig_args = ArrayList([]const u8).init(builder.allocator);
- defer zig_args.deinit();
-
- zig_args.append(builder.zig_exe) catch unreachable;
-
- const cmd = switch (self.kind) {
- .lib => "build-lib",
- .exe => "build-exe",
- .obj => "build-obj",
- .@"test" => "test",
- .test_exe => "test",
- };
- zig_args.append(cmd) catch unreachable;
-
- if (builder.color != .auto) {
- try zig_args.append("--color");
- try zig_args.append(@tagName(builder.color));
- }
-
- if (builder.reference_trace) |some| {
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
- }
-
- try addFlag(&zig_args, "LLVM", self.use_llvm);
- try addFlag(&zig_args, "LLD", self.use_lld);
-
- if (self.target.ofmt) |ofmt| {
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
- }
-
- if (self.entry_symbol_name) |entry| {
- try zig_args.append("--entry");
- try zig_args.append(entry);
- }
-
- if (self.stack_size) |stack_size| {
- try zig_args.append("--stack");
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
- }
-
- if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
-
- // We will add link objects from transitive dependencies, but we want to keep
- // all link objects in the same order provided.
- // This array is used to keep self.link_objects immutable.
- var transitive_deps: TransitiveDeps = .{
- .link_objects = ArrayList(LinkObject).init(builder.allocator),
- .seen_system_libs = StringHashMap(void).init(builder.allocator),
- .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
- .is_linking_libcpp = self.is_linking_libcpp,
- .is_linking_libc = self.is_linking_libc,
- .frameworks = &self.frameworks,
- };
-
- try transitive_deps.seen_steps.put(&self.step, {});
- try transitive_deps.add(self.link_objects.items);
-
- var prev_has_extra_flags = false;
-
- for (transitive_deps.link_objects.items) |link_object| {
- switch (link_object) {
- .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
-
- .other_step => |other| switch (other.kind) {
- .exe => @panic("Cannot link with an executable build artifact"),
- .test_exe => @panic("Cannot link with an executable build artifact"),
- .@"test" => @panic("Cannot link with a test"),
- .obj => {
- try zig_args.append(other.getOutputSource().getPath(builder));
- },
- .lib => l: {
- if (self.isStaticLibrary() and other.isStaticLibrary()) {
- // Avoid putting a static library inside a static library.
- break :l;
- }
-
- const full_path_lib = other.getOutputLibSource().getPath(builder);
- try zig_args.append(full_path_lib);
-
- if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
- if (fs.path.dirname(full_path_lib)) |dirname| {
- try zig_args.append("-rpath");
- try zig_args.append(dirname);
- }
- }
- },
- },
-
- .system_lib => |system_lib| {
- const prefix: []const u8 = prefix: {
- if (system_lib.needed) break :prefix "-needed-l";
- if (system_lib.weak) {
- if (self.target.isDarwin()) break :prefix "-weak-l";
- log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
- }
- break :prefix "-l";
- };
- switch (system_lib.use_pkg_config) {
- .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
- .yes, .force => {
- if (self.runPkgConfig(system_lib.name)) |args| {
- try zig_args.appendSlice(args);
- } else |err| switch (err) {
- error.PkgConfigInvalidOutput,
- error.PkgConfigCrashed,
- error.PkgConfigFailed,
- error.PkgConfigNotInstalled,
- error.PackageNotFound,
- => switch (system_lib.use_pkg_config) {
- .yes => {
- // pkg-config failed, so fall back to linking the library
- // by name directly.
- try zig_args.append(builder.fmt("{s}{s}", .{
- prefix,
- system_lib.name,
- }));
- },
- .force => {
- panic("pkg-config failed for library {s}", .{system_lib.name});
- },
- .no => unreachable,
- },
-
- else => |e| return e,
- }
- },
- }
- },
-
- .assembly_file => |asm_file| {
- if (prev_has_extra_flags) {
- try zig_args.append("-extra-cflags");
- try zig_args.append("--");
- prev_has_extra_flags = false;
- }
- try zig_args.append(asm_file.getPath(builder));
- },
-
- .c_source_file => |c_source_file| {
- if (c_source_file.args.len == 0) {
- if (prev_has_extra_flags) {
- try zig_args.append("-cflags");
- try zig_args.append("--");
- prev_has_extra_flags = false;
- }
- } else {
- try zig_args.append("-cflags");
- for (c_source_file.args) |arg| {
- try zig_args.append(arg);
- }
- try zig_args.append("--");
- }
- try zig_args.append(c_source_file.source.getPath(builder));
- },
-
- .c_source_files => |c_source_files| {
- if (c_source_files.flags.len == 0) {
- if (prev_has_extra_flags) {
- try zig_args.append("-cflags");
- try zig_args.append("--");
- prev_has_extra_flags = false;
- }
- } else {
- try zig_args.append("-cflags");
- for (c_source_files.flags) |flag| {
- try zig_args.append(flag);
- }
- try zig_args.append("--");
- }
- for (c_source_files.files) |file| {
- try zig_args.append(builder.pathFromRoot(file));
- }
- },
- }
- }
-
- if (transitive_deps.is_linking_libcpp) {
- try zig_args.append("-lc++");
- }
-
- if (transitive_deps.is_linking_libc) {
- try zig_args.append("-lc");
- }
-
- if (self.image_base) |image_base| {
- try zig_args.append("--image-base");
- try zig_args.append(builder.fmt("0x{x}", .{image_base}));
- }
-
- if (self.filter) |filter| {
- try zig_args.append("--test-filter");
- try zig_args.append(filter);
- }
-
- if (self.test_evented_io) {
- try zig_args.append("--test-evented-io");
- }
-
- if (self.name_prefix.len != 0) {
- try zig_args.append("--test-name-prefix");
- try zig_args.append(self.name_prefix);
- }
-
- if (self.test_runner) |test_runner| {
- try zig_args.append("--test-runner");
- try zig_args.append(builder.pathFromRoot(test_runner));
- }
-
- for (builder.debug_log_scopes) |log_scope| {
- try zig_args.append("--debug-log");
- try zig_args.append(log_scope);
- }
-
- if (builder.debug_compile_errors) {
- try zig_args.append("--debug-compile-errors");
- }
-
- if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
- if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
- if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
- if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
- if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
- if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
-
- if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
- if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
- if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
- if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
- if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
- if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
- if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
-
- if (self.emit_h) try zig_args.append("-femit-h");
-
- try addFlag(&zig_args, "strip", self.strip);
- try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
-
- switch (self.compress_debug_sections) {
- .none => {},
- .zlib => try zig_args.append("--compress-debug-sections=zlib"),
- }
-
- if (self.link_eh_frame_hdr) {
- try zig_args.append("--eh-frame-hdr");
- }
- if (self.link_emit_relocs) {
- try zig_args.append("--emit-relocs");
- }
- if (self.link_function_sections) {
- try zig_args.append("-ffunction-sections");
- }
- if (self.link_gc_sections) |x| {
- try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
- }
- if (self.linker_allow_shlib_undefined) |x| {
- try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
- }
- if (self.link_z_notext) {
- try zig_args.append("-z");
- try zig_args.append("notext");
- }
- if (!self.link_z_relro) {
- try zig_args.append("-z");
- try zig_args.append("norelro");
- }
- if (self.link_z_lazy) {
- try zig_args.append("-z");
- try zig_args.append("lazy");
- }
- if (self.link_z_common_page_size) |size| {
- try zig_args.append("-z");
- try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
- }
- if (self.link_z_max_page_size) |size| {
- try zig_args.append("-z");
- try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
- }
-
- if (self.libc_file) |libc_file| {
- try zig_args.append("--libc");
- try zig_args.append(libc_file.getPath(builder));
- } else if (builder.libc_file) |libc_file| {
- try zig_args.append("--libc");
- try zig_args.append(libc_file);
- }
-
- switch (self.optimize) {
- .Debug => {}, // Skip since it's the default.
- else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable,
- }
-
- try zig_args.append("--cache-dir");
- try zig_args.append(builder.pathFromRoot(builder.cache_root));
-
- try zig_args.append("--global-cache-dir");
- try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
-
- zig_args.append("--name") catch unreachable;
- zig_args.append(self.name) catch unreachable;
-
- if (self.linkage) |some| switch (some) {
- .dynamic => try zig_args.append("-dynamic"),
- .static => try zig_args.append("-static"),
- };
- if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
- if (self.version) |version| {
- zig_args.append("--version") catch unreachable;
- zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
- }
-
- if (self.target.isDarwin()) {
- const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
- self.target.libPrefix(),
- self.name,
- self.target.dynamicLibSuffix(),
- });
- try zig_args.append("-install_name");
- try zig_args.append(install_name);
- }
- }
-
- if (self.entitlements) |entitlements| {
- try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
- }
- if (self.pagezero_size) |pagezero_size| {
- const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
- try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
- }
- if (self.search_strategy) |strat| switch (strat) {
- .paths_first => try zig_args.append("-search_paths_first"),
- .dylibs_first => try zig_args.append("-search_dylibs_first"),
- };
- if (self.headerpad_size) |headerpad_size| {
- const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
- try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
- }
- if (self.headerpad_max_install_names) {
- try zig_args.append("-headerpad_max_install_names");
- }
- if (self.dead_strip_dylibs) {
- try zig_args.append("-dead_strip_dylibs");
- }
-
- try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
- try addFlag(&zig_args, "single-threaded", self.single_threaded);
- if (self.disable_stack_probing) {
- try zig_args.append("-fno-stack-check");
- }
- try addFlag(&zig_args, "stack-protector", self.stack_protector);
- if (self.red_zone) |red_zone| {
- if (red_zone) {
- try zig_args.append("-mred-zone");
- } else {
- try zig_args.append("-mno-red-zone");
- }
- }
- try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
- try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
-
- if (self.disable_sanitize_c) {
- try zig_args.append("-fno-sanitize-c");
- }
- if (self.sanitize_thread) {
- try zig_args.append("-fsanitize-thread");
- }
- if (self.rdynamic) {
- try zig_args.append("-rdynamic");
- }
- if (self.import_memory) {
- try zig_args.append("--import-memory");
- }
- if (self.import_symbols) {
- try zig_args.append("--import-symbols");
- }
- if (self.import_table) {
- try zig_args.append("--import-table");
- }
- if (self.export_table) {
- try zig_args.append("--export-table");
- }
- if (self.initial_memory) |initial_memory| {
- try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
- }
- if (self.max_memory) |max_memory| {
- try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
- }
- if (self.shared_memory) {
- try zig_args.append("--shared-memory");
- }
- if (self.global_base) |global_base| {
- try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
- }
-
- if (self.code_model != .default) {
- try zig_args.append("-mcmodel");
- try zig_args.append(@tagName(self.code_model));
- }
- if (self.wasi_exec_model) |model| {
- try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
- }
- for (self.export_symbol_names) |symbol_name| {
- try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
- }
-
- if (!self.target.isNative()) {
- try zig_args.appendSlice(&.{
- "-target", try self.target.zigTriple(builder.allocator),
- "-mcpu", try build.serializeCpu(builder.allocator, self.target.getCpu()),
- });
-
- if (self.target.dynamic_linker.get()) |dynamic_linker| {
- try zig_args.append("--dynamic-linker");
- try zig_args.append(dynamic_linker);
- }
- }
-
- if (self.linker_script) |linker_script| {
- try zig_args.append("--script");
- try zig_args.append(linker_script.getPath(builder));
- }
-
- if (self.version_script) |version_script| {
- try zig_args.append("--version-script");
- try zig_args.append(builder.pathFromRoot(version_script));
- }
-
- if (self.kind == .@"test") {
- if (self.exec_cmd_args) |exec_cmd_args| {
- for (exec_cmd_args) |cmd_arg| {
- if (cmd_arg) |arg| {
- try zig_args.append("--test-cmd");
- try zig_args.append(arg);
- } else {
- try zig_args.append("--test-cmd-bin");
- }
- }
- } else {
- const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
-
- switch (builder.host.getExternalExecutor(self.target_info, .{
- .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
- .link_libc = transitive_deps.is_linking_libc,
- })) {
- .native => {},
- .bad_dl, .bad_os_or_cpu => {
- try zig_args.append("--test-no-exec");
- },
- .rosetta => if (builder.enable_rosetta) {
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- .qemu => |bin_name| ok: {
- if (builder.enable_qemu) qemu: {
- const glibc_dir_arg = if (need_cross_glibc)
- builder.glibc_runtimes_dir orelse break :qemu
- else
- null;
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- if (glibc_dir_arg) |dir| {
- // TODO look into making this a call to `linuxTriple`. This
- // needs the directory to be called "i686" rather than
- // "x86" which is why we do it manually here.
- const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
- const cpu_arch = self.target.getCpuArch();
- const os_tag = self.target.getOsTag();
- const abi = self.target.getAbi();
- const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
- "i686"
- else
- @tagName(cpu_arch);
- const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
- dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
- });
-
- try zig_args.append("--test-cmd");
- try zig_args.append("-L");
- try zig_args.append("--test-cmd");
- try zig_args.append(full_dir);
- }
- try zig_args.append("--test-cmd-bin");
- break :ok;
- }
- try zig_args.append("--test-no-exec");
- },
- .wine => |bin_name| if (builder.enable_wine) {
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- .wasmtime => |bin_name| if (builder.enable_wasmtime) {
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- try zig_args.append("--test-cmd");
- try zig_args.append("--dir=.");
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- .darling => |bin_name| if (builder.enable_darling) {
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- }
- }
- } else if (self.kind == .test_exe) {
- try zig_args.append("--test-no-exec");
- }
-
- for (self.packages.items) |pkg| {
- try self.makePackageCmd(pkg, &zig_args);
- }
-
- for (self.include_dirs.items) |include_dir| {
- switch (include_dir) {
- .raw_path => |include_path| {
- try zig_args.append("-I");
- try zig_args.append(builder.pathFromRoot(include_path));
- },
- .raw_path_system => |include_path| {
- if (builder.sysroot != null) {
- try zig_args.append("-iwithsysroot");
- } else {
- try zig_args.append("-isystem");
- }
-
- const resolved_include_path = builder.pathFromRoot(include_path);
-
- const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
- // We need to check for disk designator and strip it out from dir path so
- // that zig/clang can concat resolved_include_path with sysroot.
- const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
-
- if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
- break :blk resolved_include_path[where + disk_designator.len ..];
- }
-
- break :blk resolved_include_path;
- } else resolved_include_path;
-
- try zig_args.append(common_include_path);
- },
- .other_step => |other| {
- if (other.emit_h) {
- const h_path = other.getOutputHSource().getPath(builder);
- try zig_args.append("-isystem");
- try zig_args.append(fs.path.dirname(h_path).?);
- }
- if (other.installed_headers.items.len > 0) {
- for (other.installed_headers.items) |install_step| {
- try install_step.make();
- }
- try zig_args.append("-I");
- try zig_args.append(builder.pathJoin(&.{
- other.builder.install_prefix, "include",
- }));
- }
- },
- .config_header_step => |config_header| {
- try zig_args.append("-I");
- try zig_args.append(config_header.output_dir);
- },
- }
- }
-
- for (self.lib_paths.items) |lib_path| {
- try zig_args.append("-L");
- try zig_args.append(lib_path);
- }
-
- for (self.rpaths.items) |rpath| {
- try zig_args.append("-rpath");
- try zig_args.append(rpath);
- }
-
- for (self.c_macros.items) |c_macro| {
- try zig_args.append("-D");
- try zig_args.append(c_macro);
- }
-
- if (self.target.isDarwin()) {
- for (self.framework_dirs.items) |dir| {
- if (builder.sysroot != null) {
- try zig_args.append("-iframeworkwithsysroot");
- } else {
- try zig_args.append("-iframework");
- }
- try zig_args.append(dir);
- try zig_args.append("-F");
- try zig_args.append(dir);
- }
-
- var it = self.frameworks.iterator();
- while (it.next()) |entry| {
- const name = entry.key_ptr.*;
- const info = entry.value_ptr.*;
- if (info.needed) {
- zig_args.append("-needed_framework") catch unreachable;
- } else if (info.weak) {
- zig_args.append("-weak_framework") catch unreachable;
- } else {
- zig_args.append("-framework") catch unreachable;
- }
- zig_args.append(name) catch unreachable;
- }
- } else {
- if (self.framework_dirs.items.len > 0) {
- log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
- }
-
- if (self.frameworks.count() > 0) {
- log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
- }
- }
-
- if (builder.sysroot) |sysroot| {
- try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
- }
-
- for (builder.search_prefixes.items) |search_prefix| {
- try zig_args.append("-L");
- try zig_args.append(builder.pathJoin(&.{
- search_prefix, "lib",
- }));
- try zig_args.append("-I");
- try zig_args.append(builder.pathJoin(&.{
- search_prefix, "include",
- }));
- }
-
- try addFlag(&zig_args, "valgrind", self.valgrind_support);
- try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
- try addFlag(&zig_args, "build-id", self.build_id);
-
- if (self.override_lib_dir) |dir| {
- try zig_args.append("--zig-lib-dir");
- try zig_args.append(builder.pathFromRoot(dir));
- } else if (builder.override_lib_dir) |dir| {
- try zig_args.append("--zig-lib-dir");
- try zig_args.append(builder.pathFromRoot(dir));
- }
-
- if (self.main_pkg_path) |dir| {
- try zig_args.append("--main-pkg-path");
- try zig_args.append(builder.pathFromRoot(dir));
- }
-
- try addFlag(&zig_args, "PIC", self.force_pic);
- try addFlag(&zig_args, "PIE", self.pie);
- try addFlag(&zig_args, "lto", self.want_lto);
-
- if (self.subsystem) |subsystem| {
- try zig_args.append("--subsystem");
- try zig_args.append(switch (subsystem) {
- .Console => "console",
- .Windows => "windows",
- .Posix => "posix",
- .Native => "native",
- .EfiApplication => "efi_application",
- .EfiBootServiceDriver => "efi_boot_service_driver",
- .EfiRom => "efi_rom",
- .EfiRuntimeDriver => "efi_runtime_driver",
- });
- }
-
- try zig_args.append("--enable-cache");
-
- // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
- // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
- // pass that to zig, e.g. via 'zig build-lib @args.rsp'
- // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
- var args_length: usize = 0;
- for (zig_args.items) |arg| {
- args_length += arg.len + 1; // +1 to account for null terminator
- }
- if (args_length >= 30 * 1024) {
- const args_dir = try fs.path.join(
- builder.allocator,
- &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
- );
- try std.fs.cwd().makePath(args_dir);
-
- var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
- defer args_arena.deinit();
-
- const args_to_escape = zig_args.items[2..];
- var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
-
- arg_blk: for (args_to_escape) |arg| {
- for (arg) |c, arg_idx| {
- if (c == '\\' or c == '"') {
- // Slow path for arguments that need to be escaped. We'll need to allocate and copy
- var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
- const writer = escaped.writer();
- writer.writeAll(arg[0..arg_idx]) catch unreachable;
- for (arg[arg_idx..]) |to_escape| {
- if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
- try writer.writeByte(to_escape);
- }
- escaped_args.appendAssumeCapacity(escaped.items);
- continue :arg_blk;
- }
- }
- escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
- }
-
- // Write the args to zig-cache/args/ to avoid conflicts with
- // other zig build commands running in parallel.
- const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
- const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
-
- var args_hash: [Sha256.digest_length]u8 = undefined;
- Sha256.hash(args, &args_hash, .{});
- var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
- _ = try std.fmt.bufPrint(
- &args_hex_hash,
- "{s}",
- .{std.fmt.fmtSliceHexLower(&args_hash)},
- );
-
- const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
- try std.fs.cwd().writeFile(args_file, args);
-
- zig_args.shrinkRetainingCapacity(2);
- try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
- }
-
- const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
- const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
-
- if (self.output_dir) |output_dir| {
- var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
- defer src_dir.close();
-
- // Create the output directory if it doesn't exist.
- try std.fs.cwd().makePath(output_dir);
-
- var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
- defer dest_dir.close();
-
- var it = src_dir.iterate();
- while (try it.next()) |entry| {
- // The compiler can put these files into the same directory, but we don't
- // want to copy them over.
- if (mem.eql(u8, entry.name, "llvm-ar.id") or
- mem.eql(u8, entry.name, "libs.txt") or
- mem.eql(u8, entry.name, "builtin.zig") or
- mem.eql(u8, entry.name, "zld.id") or
- mem.eql(u8, entry.name, "lld.id")) continue;
-
- _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
- }
- } else {
- self.output_dir = build_output_dir;
- }
-
- // This will ensure all output filenames will now have the output_dir available!
- self.computeOutFileNames();
-
- // Update generated files
- if (self.output_dir != null) {
- self.output_path_source.path = builder.pathJoin(
- &.{ self.output_dir.?, self.out_filename },
- );
-
- if (self.emit_h) {
- self.output_h_path_source.path = builder.pathJoin(
- &.{ self.output_dir.?, self.out_h_filename },
- );
- }
-
- if (self.target.isWindows() or self.target.isUefi()) {
- self.output_pdb_path_source.path = builder.pathJoin(
- &.{ self.output_dir.?, self.out_pdb_filename },
- );
- }
- }
-
- if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
- try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
- }
-}
-
-fn isLibCLibrary(name: []const u8) bool {
- const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
- for (libc_libraries) |libc_lib_name| {
- if (mem.eql(u8, name, libc_lib_name))
- return true;
- }
- return false;
-}
-
-fn isLibCppLibrary(name: []const u8) bool {
- const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
- for (libcpp_libraries) |libcpp_lib_name| {
- if (mem.eql(u8, name, libcpp_lib_name))
- return true;
- }
- return false;
-}
-
-/// Returned slice must be freed by the caller.
-fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
- const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
- defer allocator.free(appdata_path);
-
- const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
- defer allocator.free(path_file);
-
- const file = fs.cwd().openFile(path_file, .{}) catch return null;
- defer file.close();
-
- const size = @intCast(usize, try file.getEndPos());
- const vcpkg_path = try allocator.alloc(u8, size);
- const size_read = try file.read(vcpkg_path);
- std.debug.assert(size == size_read);
-
- return vcpkg_path;
-}
-
-pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
- const out_dir = fs.path.dirname(output_path) orelse ".";
- const out_basename = fs.path.basename(output_path);
- // sym link for libfoo.so.1 to libfoo.so.1.2.3
- const major_only_path = fs.path.join(
- allocator,
- &[_][]const u8{ out_dir, filename_major_only },
- ) catch unreachable;
- fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
- log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
- return err;
- };
- // sym link for libfoo.so to libfoo.so.1
- const name_only_path = fs.path.join(
- allocator,
- &[_][]const u8{ out_dir, filename_name_only },
- ) catch unreachable;
- fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
- log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
- return err;
- };
-}
-
-fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
- const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
- var list = ArrayList(PkgConfigPkg).init(self.allocator);
- errdefer list.deinit();
- var line_it = mem.tokenize(u8, stdout, "\r\n");
- while (line_it.next()) |line| {
- if (mem.trim(u8, line, " \t").len == 0) continue;
- var tok_it = mem.tokenize(u8, line, " \t");
- try list.append(PkgConfigPkg{
- .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
- .desc = tok_it.rest(),
- });
- }
- return list.toOwnedSlice();
-}
-
-fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
- if (self.pkg_config_pkg_list) |res| {
- return res;
- }
- var code: u8 = undefined;
- if (execPkgConfigList(self, &code)) |list| {
- self.pkg_config_pkg_list = list;
- return list;
- } else |err| {
- const result = switch (err) {
- error.ProcessTerminated => error.PkgConfigCrashed,
- error.ExecNotSupported => error.PkgConfigFailed,
- error.ExitCodeFailure => error.PkgConfigFailed,
- error.FileNotFound => error.PkgConfigNotInstalled,
- error.InvalidName => error.PkgConfigNotInstalled,
- error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
- error.ChildExecFailed => error.PkgConfigFailed,
- else => return err,
- };
- self.pkg_config_pkg_list = result;
- return result;
- }
-}
-
-test "addPackage" {
- if (builtin.os.tag == .wasi) return error.SkipZigTest;
-
- var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
- defer arena.deinit();
-
- var builder = try Builder.create(
- arena.allocator(),
- "test",
- "test",
- "test",
- "test",
- );
- defer builder.destroy();
-
- const pkg_dep = Pkg{
- .name = "pkg_dep",
- .source = .{ .path = "/not/a/pkg_dep.zig" },
- };
- const pkg_top = Pkg{
- .name = "pkg_dep",
- .source = .{ .path = "/not/a/pkg_top.zig" },
- .dependencies = &[_]Pkg{pkg_dep},
- };
-
- var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
- exe.addPackage(pkg_top);
-
- try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
-
- const dupe = exe.packages.items[0];
- try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
-}
-
-fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
- const cond = opt orelse return;
- try args.ensureUnusedCapacity(1);
- if (cond) {
- args.appendAssumeCapacity("-f" ++ name);
- } else {
- args.appendAssumeCapacity("-fno-" ++ name);
- }
-}
-
-const TransitiveDeps = struct {
- link_objects: ArrayList(LinkObject),
- seen_system_libs: StringHashMap(void),
- seen_steps: std.AutoHashMap(*const Step, void),
- is_linking_libcpp: bool,
- is_linking_libc: bool,
- frameworks: *StringHashMap(FrameworkLinkInfo),
-
- fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
- try td.link_objects.ensureUnusedCapacity(link_objects.len);
-
- for (link_objects) |link_object| {
- try td.link_objects.append(link_object);
- switch (link_object) {
- .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
- else => {},
- }
- }
- }
-
- fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
- // Inherit dependency on libc and libc++
- td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
- td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
-
- // Inherit dependencies on darwin frameworks
- if (!dyn) {
- var it = other.frameworks.iterator();
- while (it.next()) |framework| {
- try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
- }
- }
-
- // Inherit dependencies on system libraries and static libraries.
- for (other.link_objects.items) |other_link_object| {
- switch (other_link_object) {
- .system_lib => |system_lib| {
- if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
- continue;
-
- if (dyn)
- continue;
-
- try td.link_objects.append(other_link_object);
- },
- .other_step => |inner_other| {
- if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
- continue;
-
- if (!dyn)
- try td.link_objects.append(other_link_object);
-
- try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
- },
- else => continue,
- }
- }
- }
-};
diff --git a/lib/std/build/LogStep.zig b/lib/std/build/LogStep.zig
deleted file mode 100644
index fd937b00f9..0000000000
--- a/lib/std/build/LogStep.zig
+++ /dev/null
@@ -1,25 +0,0 @@
-const std = @import("../std.zig");
-const log = std.log;
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const LogStep = @This();
-
-pub const base_id = .log;
-
-step: Step,
-builder: *Builder,
-data: []const u8,
-
-pub fn init(builder: *Builder, data: []const u8) LogStep {
- return LogStep{
- .builder = builder,
- .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
- .data = builder.dupe(data),
- };
-}
-
-fn make(step: *Step) anyerror!void {
- const self = @fieldParentPtr(LogStep, "step", step);
- log.info("{s}", .{self.data});
-}
diff --git a/lib/std/build/OptionsStep.zig b/lib/std/build/OptionsStep.zig
deleted file mode 100644
index fb06cc2179..0000000000
--- a/lib/std/build/OptionsStep.zig
+++ /dev/null
@@ -1,365 +0,0 @@
-const std = @import("../std.zig");
-const builtin = @import("builtin");
-const build = std.build;
-const fs = std.fs;
-const Step = build.Step;
-const Builder = build.Builder;
-const GeneratedFile = build.GeneratedFile;
-const LibExeObjStep = build.LibExeObjStep;
-const FileSource = build.FileSource;
-
-const OptionsStep = @This();
-
-pub const base_id = .options;
-
-step: Step,
-generated_file: GeneratedFile,
-builder: *Builder,
-
-contents: std.ArrayList(u8),
-artifact_args: std.ArrayList(OptionArtifactArg),
-file_source_args: std.ArrayList(OptionFileSourceArg),
-
-pub fn create(builder: *Builder) *OptionsStep {
- const self = builder.allocator.create(OptionsStep) catch unreachable;
- self.* = .{
- .builder = builder,
- .step = Step.init(.options, "options", builder.allocator, make),
- .generated_file = undefined,
- .contents = std.ArrayList(u8).init(builder.allocator),
- .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
- .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
- };
- self.generated_file = .{ .step = &self.step };
-
- return self;
-}
-
-pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
- const out = self.contents.writer();
- switch (T) {
- []const []const u8 => {
- out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
- for (value) |slice| {
- out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
- }
- out.writeAll("};\n") catch unreachable;
- return;
- },
- [:0]const u8 => {
- out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
- return;
- },
- []const u8 => {
- out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
- return;
- },
- ?[:0]const u8 => {
- out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
- if (value) |payload| {
- out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
- } else {
- out.writeAll("null;\n") catch unreachable;
- }
- return;
- },
- ?[]const u8 => {
- out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
- if (value) |payload| {
- out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
- } else {
- out.writeAll("null;\n") catch unreachable;
- }
- return;
- },
- std.builtin.Version => {
- out.print(
- \\pub const {}: @import("std").builtin.Version = .{{
- \\ .major = {d},
- \\ .minor = {d},
- \\ .patch = {d},
- \\}};
- \\
- , .{
- std.zig.fmtId(name),
-
- value.major,
- value.minor,
- value.patch,
- }) catch unreachable;
- return;
- },
- std.SemanticVersion => {
- out.print(
- \\pub const {}: @import("std").SemanticVersion = .{{
- \\ .major = {d},
- \\ .minor = {d},
- \\ .patch = {d},
- \\
- , .{
- std.zig.fmtId(name),
-
- value.major,
- value.minor,
- value.patch,
- }) catch unreachable;
- if (value.pre) |some| {
- out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
- }
- if (value.build) |some| {
- out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
- }
- out.writeAll("};\n") catch unreachable;
- return;
- },
- else => {},
- }
- switch (@typeInfo(T)) {
- .Enum => |enum_info| {
- out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
- inline for (enum_info.fields) |field| {
- out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
- }
- out.writeAll("};\n") catch unreachable;
- out.print("pub const {}: {s} = {s}.{s};\n", .{
- std.zig.fmtId(name),
- std.zig.fmtId(@typeName(T)),
- std.zig.fmtId(@typeName(T)),
- std.zig.fmtId(@tagName(value)),
- }) catch unreachable;
- return;
- },
- else => {},
- }
- out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable;
- printLiteral(out, value, 0) catch unreachable;
- out.writeAll(";\n") catch unreachable;
-}
-
-// TODO: non-recursive?
-fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
- const T = @TypeOf(val);
- switch (@typeInfo(T)) {
- .Array => {
- try out.print("{s} {{\n", .{@typeName(T)});
- for (val) |item| {
- try out.writeByteNTimes(' ', indent + 4);
- try printLiteral(out, item, indent + 4);
- try out.writeAll(",\n");
- }
- try out.writeByteNTimes(' ', indent);
- try out.writeAll("}");
- },
- .Pointer => |p| {
- if (p.size != .Slice) {
- @compileError("Non-slice pointers are not yet supported in build options");
- }
- try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
- for (val) |item| {
- try out.writeByteNTimes(' ', indent + 4);
- try printLiteral(out, item, indent + 4);
- try out.writeAll(",\n");
- }
- try out.writeByteNTimes(' ', indent);
- try out.writeAll("}");
- },
- .Optional => {
- if (val) |inner| {
- return printLiteral(out, inner, indent);
- } else {
- return out.writeAll("null");
- }
- },
- .Void,
- .Bool,
- .Int,
- .ComptimeInt,
- .Float,
- .Null,
- => try out.print("{any}", .{val}),
- else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
- }
-}
-
-/// The value is the path in the cache dir.
-/// Adds a dependency automatically.
-pub fn addOptionFileSource(
- self: *OptionsStep,
- name: []const u8,
- source: FileSource,
-) void {
- self.file_source_args.append(.{
- .name = name,
- .source = source.dupe(self.builder),
- }) catch unreachable;
- source.addStepDependencies(&self.step);
-}
-
-/// The value is the path in the cache dir.
-/// Adds a dependency automatically.
-pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
- self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
- self.step.dependOn(&artifact.step);
-}
-
-pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg {
- return .{ .name = package_name, .source = self.getSource() };
-}
-
-pub fn getSource(self: *OptionsStep) FileSource {
- return .{ .generated = &self.generated_file };
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(OptionsStep, "step", step);
-
- for (self.artifact_args.items) |item| {
- self.addOption(
- []const u8,
- item.name,
- self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
- );
- }
-
- for (self.file_source_args.items) |item| {
- self.addOption(
- []const u8,
- item.name,
- item.source.getPath(self.builder),
- );
- }
-
- const options_directory = self.builder.pathFromRoot(
- try fs.path.join(
- self.builder.allocator,
- &[_][]const u8{ self.builder.cache_root, "options" },
- ),
- );
-
- try fs.cwd().makePath(options_directory);
-
- const options_file = try fs.path.join(
- self.builder.allocator,
- &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
- );
-
- try fs.cwd().writeFile(options_file, self.contents.items);
-
- self.generated_file.path = options_file;
-}
-
-fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
- // This implementation is copied from `WriteFileStep.make`
-
- var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
-
- // Random bytes to make OptionsStep unique. Refresh this with
- // new random bytes when OptionsStep implementation is modified
- // in a non-backwards-compatible way.
- hash.update("yL0Ya4KkmcCjBlP8");
- hash.update(self.contents.items);
-
- var digest: [48]u8 = undefined;
- hash.final(&digest);
- var hash_basename: [64]u8 = undefined;
- _ = fs.base64_encoder.encode(&hash_basename, &digest);
- return hash_basename;
-}
-
-const OptionArtifactArg = struct {
- name: []const u8,
- artifact: *LibExeObjStep,
-};
-
-const OptionFileSourceArg = struct {
- name: []const u8,
- source: FileSource,
-};
-
-test "OptionsStep" {
- if (builtin.os.tag == .wasi) return error.SkipZigTest;
-
- var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
- defer arena.deinit();
- var builder = try Builder.create(
- arena.allocator(),
- "test",
- "test",
- "test",
- "test",
- );
- defer builder.destroy();
-
- const options = builder.addOptions();
-
- // TODO this regressed at some point
- //const KeywordEnum = enum {
- // @"0.8.1",
- //};
-
- const nested_array = [2][2]u16{
- [2]u16{ 300, 200 },
- [2]u16{ 300, 200 },
- };
- const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
-
- options.addOption(usize, "option1", 1);
- options.addOption(?usize, "option2", null);
- options.addOption(?usize, "option3", 3);
- options.addOption(comptime_int, "option4", 4);
- options.addOption([]const u8, "string", "zigisthebest");
- options.addOption(?[]const u8, "optional_string", null);
- options.addOption([2][2]u16, "nested_array", nested_array);
- options.addOption([]const []const u16, "nested_slice", nested_slice);
- //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
- options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
- options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
-
- try std.testing.expectEqualStrings(
- \\pub const option1: usize = 1;
- \\pub const option2: ?usize = null;
- \\pub const option3: ?usize = 3;
- \\pub const option4: comptime_int = 4;
- \\pub const string: []const u8 = "zigisthebest";
- \\pub const optional_string: ?[]const u8 = null;
- \\pub const nested_array: [2][2]u16 = [2][2]u16 {
- \\ [2]u16 {
- \\ 300,
- \\ 200,
- \\ },
- \\ [2]u16 {
- \\ 300,
- \\ 200,
- \\ },
- \\};
- \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
- \\ &[_]u16 {
- \\ 300,
- \\ 200,
- \\ },
- \\ &[_]u16 {
- \\ 300,
- \\ 200,
- \\ },
- \\};
- //\\pub const KeywordEnum = enum {
- //\\ @"0.8.1",
- //\\};
- //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
- \\pub const version: @import("std").builtin.Version = .{
- \\ .major = 0,
- \\ .minor = 1,
- \\ .patch = 2,
- \\};
- \\pub const semantic_version: @import("std").SemanticVersion = .{
- \\ .major = 0,
- \\ .minor = 1,
- \\ .patch = 2,
- \\ .pre = "foo",
- \\ .build = "bar",
- \\};
- \\
- , options.contents.items);
-
- _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
-}
diff --git a/lib/std/build/RemoveDirStep.zig b/lib/std/build/RemoveDirStep.zig
deleted file mode 100644
index 959414e54f..0000000000
--- a/lib/std/build/RemoveDirStep.zig
+++ /dev/null
@@ -1,31 +0,0 @@
-const std = @import("../std.zig");
-const log = std.log;
-const fs = std.fs;
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const RemoveDirStep = @This();
-
-pub const base_id = .remove_dir;
-
-step: Step,
-builder: *Builder,
-dir_path: []const u8,
-
-pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
- return RemoveDirStep{
- .builder = builder,
- .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
- .dir_path = builder.dupePath(dir_path),
- };
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(RemoveDirStep, "step", step);
-
- const full_path = self.builder.pathFromRoot(self.dir_path);
- fs.cwd().deleteTree(full_path) catch |err| {
- log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
- return err;
- };
-}
diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig
deleted file mode 100644
index 5183a328cd..0000000000
--- a/lib/std/build/RunStep.zig
+++ /dev/null
@@ -1,378 +0,0 @@
-const std = @import("../std.zig");
-const builtin = @import("builtin");
-const build = std.build;
-const Step = build.Step;
-const Builder = build.Builder;
-const LibExeObjStep = build.LibExeObjStep;
-const WriteFileStep = build.WriteFileStep;
-const fs = std.fs;
-const mem = std.mem;
-const process = std.process;
-const ArrayList = std.ArrayList;
-const EnvMap = process.EnvMap;
-const Allocator = mem.Allocator;
-const ExecError = build.Builder.ExecError;
-
-const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
-
-const RunStep = @This();
-
-pub const base_id: Step.Id = .run;
-
-step: Step,
-builder: *Builder,
-
-/// See also addArg and addArgs to modifying this directly
-argv: ArrayList(Arg),
-
-/// Set this to modify the current working directory
-cwd: ?[]const u8,
-
-/// Override this field to modify the environment, or use setEnvironmentVariable
-env_map: ?*EnvMap,
-
-stdout_action: StdIoAction = .inherit,
-stderr_action: StdIoAction = .inherit,
-
-stdin_behavior: std.ChildProcess.StdIo = .Inherit,
-
-/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
-expected_exit_code: ?u8 = 0,
-
-/// Print the command before running it
-print: bool,
-
-pub const StdIoAction = union(enum) {
- inherit,
- ignore,
- expect_exact: []const u8,
- expect_matches: []const []const u8,
-};
-
-pub const Arg = union(enum) {
- artifact: *LibExeObjStep,
- file_source: build.FileSource,
- bytes: []u8,
-};
-
-pub fn create(builder: *Builder, name: []const u8) *RunStep {
- const self = builder.allocator.create(RunStep) catch unreachable;
- self.* = RunStep{
- .builder = builder,
- .step = Step.init(base_id, name, builder.allocator, make),
- .argv = ArrayList(Arg).init(builder.allocator),
- .cwd = null,
- .env_map = null,
- .print = builder.verbose,
- };
- return self;
-}
-
-pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
- self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
- self.step.dependOn(&artifact.step);
-}
-
-pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void {
- self.argv.append(Arg{
- .file_source = file_source.dupe(self.builder),
- }) catch unreachable;
- file_source.addStepDependencies(&self.step);
-}
-
-pub fn addArg(self: *RunStep, arg: []const u8) void {
- self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
-}
-
-pub fn addArgs(self: *RunStep, args: []const []const u8) void {
- for (args) |arg| {
- self.addArg(arg);
- }
-}
-
-pub fn clearEnvironment(self: *RunStep) void {
- const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable;
- new_env_map.* = EnvMap.init(self.builder.allocator);
- self.env_map = new_env_map;
-}
-
-pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
- addPathDirInternal(&self.step, self.builder, search_path);
-}
-
-/// For internal use only, users of `RunStep` should use `addPathDir` directly.
-pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u8) void {
- const env_map = getEnvMapInternal(step, builder.allocator);
-
- const key = "PATH";
- var prev_path = env_map.get(key);
-
- if (prev_path) |pp| {
- const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
- env_map.put(key, new_path) catch unreachable;
- } else {
- env_map.put(key, builder.dupePath(search_path)) catch unreachable;
- }
-}
-
-pub fn getEnvMap(self: *RunStep) *EnvMap {
- return getEnvMapInternal(&self.step, self.builder.allocator);
-}
-
-fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
- const maybe_env_map = switch (step.id) {
- .run => step.cast(RunStep).?.env_map,
- .emulatable_run => step.cast(build.EmulatableRunStep).?.env_map,
- else => unreachable,
- };
- return maybe_env_map orelse {
- const env_map = allocator.create(EnvMap) catch unreachable;
- env_map.* = process.getEnvMap(allocator) catch unreachable;
- switch (step.id) {
- .run => step.cast(RunStep).?.env_map = env_map,
- .emulatable_run => step.cast(RunStep).?.env_map = env_map,
- else => unreachable,
- }
- return env_map;
- };
-}
-
-pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
- const env_map = self.getEnvMap();
- env_map.put(
- self.builder.dupe(key),
- self.builder.dupe(value),
- ) catch unreachable;
-}
-
-pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
- self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
-}
-
-pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
- self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
-}
-
-fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
- return switch (action) {
- .ignore => .Ignore,
- .inherit => .Inherit,
- .expect_exact, .expect_matches => .Pipe,
- };
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(RunStep, "step", step);
-
- var argv_list = ArrayList([]const u8).init(self.builder.allocator);
- for (self.argv.items) |arg| {
- switch (arg) {
- .bytes => |bytes| try argv_list.append(bytes),
- .file_source => |file| try argv_list.append(file.getPath(self.builder)),
- .artifact => |artifact| {
- if (artifact.target.isWindows()) {
- // On Windows we don't have rpaths so we have to add .dll search paths to PATH
- self.addPathForDynLibs(artifact);
- }
- const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
- try argv_list.append(executable_path);
- },
- }
- }
-
- try runCommand(
- argv_list.items,
- self.builder,
- self.expected_exit_code,
- self.stdout_action,
- self.stderr_action,
- self.stdin_behavior,
- self.env_map,
- self.cwd,
- self.print,
- );
-}
-
-pub fn runCommand(
- argv: []const []const u8,
- builder: *Builder,
- expected_exit_code: ?u8,
- stdout_action: StdIoAction,
- stderr_action: StdIoAction,
- stdin_behavior: std.ChildProcess.StdIo,
- env_map: ?*EnvMap,
- maybe_cwd: ?[]const u8,
- print: bool,
-) !void {
- const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
-
- if (!std.process.can_spawn) {
- const cmd = try std.mem.join(builder.allocator, " ", argv);
- std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
- builder.allocator.free(cmd);
- return ExecError.ExecNotSupported;
- }
-
- var child = std.ChildProcess.init(argv, builder.allocator);
- child.cwd = cwd;
- child.env_map = env_map orelse builder.env_map;
-
- child.stdin_behavior = stdin_behavior;
- child.stdout_behavior = stdIoActionToBehavior(stdout_action);
- child.stderr_behavior = stdIoActionToBehavior(stderr_action);
-
- if (print)
- printCmd(cwd, argv);
-
- child.spawn() catch |err| {
- std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
- return err;
- };
-
- // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
-
- var stdout: ?[]const u8 = null;
- defer if (stdout) |s| builder.allocator.free(s);
-
- switch (stdout_action) {
- .expect_exact, .expect_matches => {
- stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
- },
- .inherit, .ignore => {},
- }
-
- var stderr: ?[]const u8 = null;
- defer if (stderr) |s| builder.allocator.free(s);
-
- switch (stderr_action) {
- .expect_exact, .expect_matches => {
- stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
- },
- .inherit, .ignore => {},
- }
-
- const term = child.wait() catch |err| {
- std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
- return err;
- };
-
- switch (term) {
- .Exited => |code| blk: {
- const expected_code = expected_exit_code orelse break :blk;
-
- if (code != expected_code) {
- if (builder.prominent_compile_errors) {
- std.debug.print("Run step exited with error code {} (expected {})\n", .{
- code,
- expected_code,
- });
- } else {
- std.debug.print("The following command exited with error code {} (expected {}):\n", .{
- code,
- expected_code,
- });
- printCmd(cwd, argv);
- }
-
- return error.UnexpectedExitCode;
- }
- },
- else => {
- std.debug.print("The following command terminated unexpectedly:\n", .{});
- printCmd(cwd, argv);
- return error.UncleanExit;
- },
- }
-
- switch (stderr_action) {
- .inherit, .ignore => {},
- .expect_exact => |expected_bytes| {
- if (!mem.eql(u8, expected_bytes, stderr.?)) {
- std.debug.print(
- \\
- \\========= Expected this stderr: =========
- \\{s}
- \\========= But found: ====================
- \\{s}
- \\
- , .{ expected_bytes, stderr.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
- },
- .expect_matches => |matches| for (matches) |match| {
- if (mem.indexOf(u8, stderr.?, match) == null) {
- std.debug.print(
- \\
- \\========= Expected to find in stderr: =========
- \\{s}
- \\========= But stderr does not contain it: =====
- \\{s}
- \\
- , .{ match, stderr.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
- },
- }
-
- switch (stdout_action) {
- .inherit, .ignore => {},
- .expect_exact => |expected_bytes| {
- if (!mem.eql(u8, expected_bytes, stdout.?)) {
- std.debug.print(
- \\
- \\========= Expected this stdout: =========
- \\{s}
- \\========= But found: ====================
- \\{s}
- \\
- , .{ expected_bytes, stdout.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
- },
- .expect_matches => |matches| for (matches) |match| {
- if (mem.indexOf(u8, stdout.?, match) == null) {
- std.debug.print(
- \\
- \\========= Expected to find in stdout: =========
- \\{s}
- \\========= But stdout does not contain it: =====
- \\{s}
- \\
- , .{ match, stdout.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
- },
- }
-}
-
-fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
- if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
- for (argv) |arg| {
- std.debug.print("{s} ", .{arg});
- }
- std.debug.print("\n", .{});
-}
-
-fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
- addPathForDynLibsInternal(&self.step, self.builder, artifact);
-}
-
-/// This should only be used for internal usage, this is called automatically
-/// for the user.
-pub fn addPathForDynLibsInternal(step: *Step, builder: *Builder, artifact: *LibExeObjStep) void {
- for (artifact.link_objects.items) |link_object| {
- switch (link_object) {
- .other_step => |other| {
- if (other.target.isWindows() and other.isDynamicLibrary()) {
- addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
- addPathForDynLibsInternal(step, builder, other);
- }
- },
- else => {},
- }
- }
-}
diff --git a/lib/std/build/TranslateCStep.zig b/lib/std/build/TranslateCStep.zig
deleted file mode 100644
index 9f45d606a1..0000000000
--- a/lib/std/build/TranslateCStep.zig
+++ /dev/null
@@ -1,138 +0,0 @@
-const std = @import("../std.zig");
-const build = std.build;
-const Step = build.Step;
-const Builder = build.Builder;
-const LibExeObjStep = build.LibExeObjStep;
-const CheckFileStep = build.CheckFileStep;
-const fs = std.fs;
-const mem = std.mem;
-const CrossTarget = std.zig.CrossTarget;
-
-const TranslateCStep = @This();
-
-pub const base_id = .translate_c;
-
-step: Step,
-builder: *Builder,
-source: build.FileSource,
-include_dirs: std.ArrayList([]const u8),
-c_macros: std.ArrayList([]const u8),
-output_dir: ?[]const u8,
-out_basename: []const u8,
-target: CrossTarget,
-optimize: std.builtin.OptimizeMode,
-output_file: build.GeneratedFile,
-
-pub const Options = struct {
- source_file: build.FileSource,
- target: CrossTarget,
- optimize: std.builtin.OptimizeMode,
-};
-
-pub fn create(builder: *Builder, options: Options) *TranslateCStep {
- const self = builder.allocator.create(TranslateCStep) catch unreachable;
- const source = options.source_file.dupe(builder);
- self.* = TranslateCStep{
- .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
- .builder = builder,
- .source = source,
- .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
- .c_macros = std.ArrayList([]const u8).init(builder.allocator),
- .output_dir = null,
- .out_basename = undefined,
- .target = options.target,
- .optimize = options.optimize,
- .output_file = build.GeneratedFile{ .step = &self.step },
- };
- source.addStepDependencies(&self.step);
- return self;
-}
-
-pub const AddExecutableOptions = struct {
- name: ?[]const u8 = null,
- version: ?std.builtin.Version = null,
- target: ?CrossTarget = null,
- optimize: ?std.builtin.Mode = null,
- linkage: ?LibExeObjStep.Linkage = null,
-};
-
-/// Creates a step to build an executable from the translated source.
-pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *LibExeObjStep {
- return self.builder.addExecutable(.{
- .root_source_file = .{ .generated = &self.output_file },
- .name = options.name orelse "translated_c",
- .version = options.version,
- .target = options.target orelse self.target,
- .optimize = options.optimize orelse self.optimize,
- .linkage = options.linkage,
- });
-}
-
-pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
- self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
-}
-
-pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
- return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
-}
-
-/// If the value is omitted, it is set to 1.
-/// `name` and `value` need not live longer than the function call.
-pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
- const macro = build.constructCMacro(self.builder.allocator, name, value);
- self.c_macros.append(macro) catch unreachable;
-}
-
-/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
-pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
- self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(TranslateCStep, "step", step);
-
- var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
- try argv_list.append(self.builder.zig_exe);
- try argv_list.append("translate-c");
- try argv_list.append("-lc");
-
- try argv_list.append("--enable-cache");
-
- if (!self.target.isNative()) {
- try argv_list.append("-target");
- try argv_list.append(try self.target.zigTriple(self.builder.allocator));
- }
-
- switch (self.optimize) {
- .Debug => {}, // Skip since it's the default.
- else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
- }
-
- for (self.include_dirs.items) |include_dir| {
- try argv_list.append("-I");
- try argv_list.append(include_dir);
- }
-
- for (self.c_macros.items) |c_macro| {
- try argv_list.append("-D");
- try argv_list.append(c_macro);
- }
-
- try argv_list.append(self.source.getPath(self.builder));
-
- const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
- const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
-
- self.out_basename = fs.path.basename(output_path);
- if (self.output_dir) |output_dir| {
- const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
- try self.builder.updateFile(output_path, full_dest);
- } else {
- self.output_dir = fs.path.dirname(output_path).?;
- }
-
- self.output_file.path = fs.path.join(
- self.builder.allocator,
- &[_][]const u8{ self.output_dir.?, self.out_basename },
- ) catch unreachable;
-}
diff --git a/lib/std/build/WriteFileStep.zig b/lib/std/build/WriteFileStep.zig
deleted file mode 100644
index 4faae8f74e..0000000000
--- a/lib/std/build/WriteFileStep.zig
+++ /dev/null
@@ -1,117 +0,0 @@
-const std = @import("../std.zig");
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const fs = std.fs;
-const ArrayList = std.ArrayList;
-
-const WriteFileStep = @This();
-
-pub const base_id = .write_file;
-
-step: Step,
-builder: *Builder,
-output_dir: []const u8,
-files: std.TailQueue(File),
-
-pub const File = struct {
- source: build.GeneratedFile,
- basename: []const u8,
- bytes: []const u8,
-};
-
-pub fn init(builder: *Builder) WriteFileStep {
- return WriteFileStep{
- .builder = builder,
- .step = Step.init(.write_file, "writefile", builder.allocator, make),
- .files = .{},
- .output_dir = undefined,
- };
-}
-
-pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
- const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
- node.* = .{
- .data = .{
- .source = build.GeneratedFile{ .step = &self.step },
- .basename = self.builder.dupePath(basename),
- .bytes = self.builder.dupe(bytes),
- },
- };
-
- self.files.append(node);
-}
-
-/// Gets a file source for the given basename. If the file does not exist, returns `null`.
-pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource {
- var it = step.files.first;
- while (it) |node| : (it = node.next) {
- if (std.mem.eql(u8, node.data.basename, basename))
- return build.FileSource{ .generated = &node.data.source };
- }
- return null;
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(WriteFileStep, "step", step);
-
- // The cache is used here not really as a way to speed things up - because writing
- // the data to a file would probably be very fast - but as a way to find a canonical
- // location to put build artifacts.
-
- // If, for example, a hard-coded path was used as the location to put WriteFileStep
- // files, then two WriteFileSteps executing in parallel might clobber each other.
-
- // TODO port the cache system from the compiler to zig std lib. Until then
- // we directly construct the path, and no "cache hit" detection happens;
- // the files are always written.
- // Note there is similar code over in ConfigHeaderStep.
- const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
- // Random bytes to make WriteFileStep unique. Refresh this with
- // new random bytes when WriteFileStep implementation is modified
- // in a non-backwards-compatible way.
- var hash = Hasher.init("eagVR1dYXoE7ARDP");
-
- {
- var it = self.files.first;
- while (it) |node| : (it = node.next) {
- hash.update(node.data.basename);
- hash.update(node.data.bytes);
- hash.update("|");
- }
- }
- var digest: [16]u8 = undefined;
- hash.final(&digest);
- var hash_basename: [digest.len * 2]u8 = undefined;
- _ = std.fmt.bufPrint(
- &hash_basename,
- "{s}",
- .{std.fmt.fmtSliceHexLower(&digest)},
- ) catch unreachable;
-
- self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
- self.builder.cache_root, "o", &hash_basename,
- });
- var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
- std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
- return err;
- };
- defer dir.close();
- {
- var it = self.files.first;
- while (it) |node| : (it = node.next) {
- dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
- std.debug.print("unable to write {s} into {s}: {s}\n", .{
- node.data.basename,
- self.output_dir,
- @errorName(err),
- });
- return err;
- };
- node.data.source.path = fs.path.join(
- self.builder.allocator,
- &[_][]const u8{ self.output_dir, node.data.basename },
- ) catch unreachable;
- }
- }
-}
diff --git a/lib/std/std.zig b/lib/std/std.zig
index ba52784b45..e0318ceb43 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
pub const AutoHashMap = hash_map.AutoHashMap;
pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
+pub const Build = @import("Build.zig");
pub const BufMap = @import("buf_map.zig").BufMap;
pub const BufSet = @import("buf_set.zig").BufSet;
pub const ChildProcess = @import("child_process.zig").ChildProcess;
@@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig");
pub const atomic = @import("atomic.zig");
pub const base64 = @import("base64.zig");
pub const bit_set = @import("bit_set.zig");
-pub const build = @import("build.zig");
pub const builtin = @import("builtin.zig");
pub const c = @import("c.zig");
pub const coff = @import("coff.zig");
@@ -96,6 +96,12 @@ pub const wasm = @import("wasm.zig");
pub const zig = @import("zig.zig");
pub const start = @import("start.zig");
+///// Deprecated. Use `std.Build` instead.
+//pub const build = struct {
+// /// Deprecated. Use `std.Build` instead.
+// pub const Builder = Build;
+//};
+
const root = @import("root");
const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
diff --git a/test/link/bss/build.zig b/test/link/bss/build.zig
index c31fa7faf5..0df9c1d323 100644
--- a/test/link/bss/build.zig
+++ b/test/link/bss/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test");
diff --git a/test/link/common_symbols/build.zig b/test/link/common_symbols/build.zig
index 068c3f9c57..ee9dd94ebd 100644
--- a/test/link/common_symbols/build.zig
+++ b/test/link/common_symbols/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const lib_a = b.addStaticLibrary(.{
diff --git a/test/link/common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig
index b6dd39801c..f6efdc784b 100644
--- a/test/link/common_symbols_alignment/build.zig
+++ b/test/link/common_symbols_alignment/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/link/interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig
index 50a214490d..d8962a8e08 100644
--- a/test/link/interdependent_static_c_libs/build.zig
+++ b/test/link/interdependent_static_c_libs/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/link/macho/bugs/13056/build.zig b/test/link/macho/bugs/13056/build.zig
index a65cd60766..662fd25c92 100644
--- a/test/link/macho/bugs/13056/build.zig
+++ b/test/link/macho/bugs/13056/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/bugs/13457/build.zig b/test/link/macho/bugs/13457/build.zig
index 4c1ce89261..6ca1f31b86 100644
--- a/test/link/macho/bugs/13457/build.zig
+++ b/test/link/macho/bugs/13457/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/dead_strip/build.zig b/test/link/macho/dead_strip/build.zig
index a4c3575e45..b6c3002492 100644
--- a/test/link/macho/dead_strip/build.zig
+++ b/test/link/macho/dead_strip/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
@@ -37,7 +36,7 @@ pub fn build(b: *Builder) void {
}
}
-fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
const exe = b.addExecutable(.{
.name = "test",
.optimize = optimize,
diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig
index 0127b575fc..f61b30ca4a 100644
--- a/test/link/macho/dead_strip_dylibs/build.zig
+++ b/test/link/macho/dead_strip_dylibs/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test the program");
@@ -36,7 +35,7 @@ pub fn build(b: *Builder) void {
}
}
-fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
+fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
const exe = b.addExecutable(.{
.name = "test",
.optimize = optimize,
diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig
index acd27a507f..7a1e2d862c 100644
--- a/test/link/macho/dylib/build.zig
+++ b/test/link/macho/dylib/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/empty/build.zig b/test/link/macho/empty/build.zig
index 8b2d047371..586da1511b 100644
--- a/test/link/macho/empty/build.zig
+++ b/test/link/macho/empty/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
@@ -17,7 +16,7 @@ pub fn build(b: *Builder) void {
exe.addCSourceFile("empty.c", &[0][]const u8{});
exe.linkLibC();
- const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
+ const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
run_cmd.expectStdOutEqual("Hello!\n");
test_step.dependOn(&run_cmd.step);
}
diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig
index 87e4d1b5da..4504da9c6c 100644
--- a/test/link/macho/entry/build.zig
+++ b/test/link/macho/entry/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test");
diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig
index 74efb5d580..2b3c6abb8a 100644
--- a/test/link/macho/headerpad/build.zig
+++ b/test/link/macho/headerpad/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test");
@@ -94,7 +93,7 @@ pub fn build(b: *Builder) void {
}
}
-fn simpleExe(b: *Builder, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
+fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
const exe = b.addExecutable(.{
.name = "main",
.optimize = optimize,
diff --git a/test/link/macho/linksection/build.zig b/test/link/macho/linksection/build.zig
index eebb31a21e..227d4eeb63 100644
--- a/test/link/macho/linksection/build.zig
+++ b/test/link/macho/linksection/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = std.zig.CrossTarget{ .os_tag = .macos };
diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig
index 33965a9272..62b70b21f1 100644
--- a/test/link/macho/needed_framework/build.zig
+++ b/test/link/macho/needed_framework/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test the program");
diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig
index 137239d292..cdad94357b 100644
--- a/test/link/macho/needed_library/build.zig
+++ b/test/link/macho/needed_library/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/objc/build.zig b/test/link/macho/objc/build.zig
index 9c38739a5c..10d293baab 100644
--- a/test/link/macho/objc/build.zig
+++ b/test/link/macho/objc/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test the program");
@@ -18,6 +17,6 @@ pub fn build(b: *Builder) void {
// populate paths to the sysroot here.
exe.linkFramework("Foundation");
- const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
+ const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
test_step.dependOn(&run_cmd.step);
}
diff --git a/test/link/macho/objcpp/build.zig b/test/link/macho/objcpp/build.zig
index f4c88b2862..2a3459be50 100644
--- a/test/link/macho/objcpp/build.zig
+++ b/test/link/macho/objcpp/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test the program");
diff --git a/test/link/macho/pagezero/build.zig b/test/link/macho/pagezero/build.zig
index f61aa34a93..0a8471b919 100644
--- a/test/link/macho/pagezero/build.zig
+++ b/test/link/macho/pagezero/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig
index db894b6ae3..eeda89446b 100644
--- a/test/link/macho/search_strategy/build.zig
+++ b/test/link/macho/search_strategy/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
@@ -29,14 +28,14 @@ pub fn build(b: *Builder) void {
const exe = createScenario(b, optimize, target);
exe.search_strategy = .paths_first;
- const run = std.build.EmulatableRunStep.create(b, "run", exe);
+ const run = std.Build.EmulatableRunStep.create(b, "run", exe);
run.cwd = b.pathFromRoot(".");
run.expectStdOutEqual("Hello world");
test_step.dependOn(&run.step);
}
}
-fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
const static = b.addStaticLibrary(.{
.name = "a",
.optimize = optimize,
@@ -44,7 +43,7 @@ fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.z
});
static.addCSourceFile("a.c", &.{});
static.linkLibC();
- static.override_dest_dir = std.build.InstallDir{
+ static.override_dest_dir = std.Build.InstallDir{
.custom = "static",
};
static.install();
@@ -57,7 +56,7 @@ fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.z
});
dylib.addCSourceFile("a.c", &.{});
dylib.linkLibC();
- dylib.override_dest_dir = std.build.InstallDir{
+ dylib.override_dest_dir = std.Build.InstallDir{
.custom = "dynamic",
};
dylib.install();
diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig
index 74e9a86e94..3529a134eb 100644
--- a/test/link/macho/stack_size/build.zig
+++ b/test/link/macho/stack_size/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig
index b6baf63c11..6eabc02b5f 100644
--- a/test/link/macho/strict_validation/build.zig
+++ b/test/link/macho/strict_validation/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/tls/build.zig b/test/link/macho/tls/build.zig
index 9b2fe952bf..c77588cb5d 100644
--- a/test/link/macho/tls/build.zig
+++ b/test/link/macho/tls/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig
index dbbdbb3e51..e43c002e2e 100644
--- a/test/link/macho/unwind_info/build.zig
+++ b/test/link/macho/unwind_info/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
@@ -14,8 +13,8 @@ pub fn build(b: *Builder) void {
}
fn testUnwindInfo(
- b: *Builder,
- test_step: *std.build.Step,
+ b: *std.Build,
+ test_step: *std.Build.Step,
optimize: std.builtin.OptimizeMode,
target: std.zig.CrossTarget,
dead_strip: bool,
@@ -52,7 +51,7 @@ fn testUnwindInfo(
test_step.dependOn(&run_cmd.step);
}
-fn createScenario(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
const exe = b.addExecutable(.{
.name = "test",
.optimize = optimize,
diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig
index 86ff99e8b1..62c288f1a0 100644
--- a/test/link/macho/uuid/build.zig
+++ b/test/link/macho/uuid/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
@@ -27,8 +26,8 @@ pub fn build(b: *Builder) void {
}
fn testUuid(
- b: *Builder,
- test_step: *std.build.Step,
+ b: *std.Build,
+ test_step: *std.Build.Step,
optimize: std.builtin.OptimizeMode,
target: std.zig.CrossTarget,
comptime exp: []const u8,
@@ -52,7 +51,7 @@ fn testUuid(
}
}
-fn simpleDylib(b: *Builder, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn simpleDylib(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
const dylib = b.addSharedLibrary(.{
.name = "test",
.version = .{ .major = 1, .minor = 0 },
diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig
index f8460c4e82..5be66991dd 100644
--- a/test/link/macho/weak_framework/build.zig
+++ b/test/link/macho/weak_framework/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test the program");
diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig
index 229d965e48..505ab5ae96 100644
--- a/test/link/macho/weak_library/build.zig
+++ b/test/link/macho/weak_library/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
-const Builder = std.build.Builder;
-const LibExeObjectStep = std.build.LibExeObjStep;
+const LibExeObjectStep = std.Build.LibExeObjStep;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target: std.zig.CrossTarget = .{ .os_tag = .macos };
diff --git a/test/link/static_lib_as_system_lib/build.zig b/test/link/static_lib_as_system_lib/build.zig
index 895cdcf316..b6cf32d711 100644
--- a/test/link/static_lib_as_system_lib/build.zig
+++ b/test/link/static_lib_as_system_lib/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig
index 7401ba22dc..342c4c08d1 100644
--- a/test/link/wasm/archive/build.zig
+++ b/test/link/wasm/archive/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig
index 69e88aefae..9f57066518 100644
--- a/test/link/wasm/basic-features/build.zig
+++ b/test/link/wasm/basic-features/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
// Library with explicitly set cpu features
const lib = b.addSharedLibrary(.{
.name = "lib",
diff --git a/test/link/wasm/bss/build.zig b/test/link/wasm/bss/build.zig
index 6b29fd0dc3..1017e70a71 100644
--- a/test/link/wasm/bss/build.zig
+++ b/test/link/wasm/bss/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig
index 8eab283ec2..95caf42dd0 100644
--- a/test/link/wasm/export-data/build.zig
+++ b/test/link/wasm/export-data/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig
index 2b9a91d728..69c34a320e 100644
--- a/test/link/wasm/export/build.zig
+++ b/test/link/wasm/export/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const no_export = b.addSharedLibrary(.{
diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig
index 71bb986dff..19913e6eca 100644
--- a/test/link/wasm/extern-mangle/build.zig
+++ b/test/link/wasm/extern-mangle/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig
index 800c76a31c..569d94091a 100644
--- a/test/link/wasm/extern/build.zig
+++ b/test/link/wasm/extern/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "extern",
.root_source_file = .{ .path = "main.zig" },
diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig
index 804aaf0b09..4c25d0d860 100644
--- a/test/link/wasm/function-table/build.zig
+++ b/test/link/wasm/function-table/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const test_step = b.step("test", "Test");
diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig
index 147fb55fda..d6d706a33d 100644
--- a/test/link/wasm/infer-features/build.zig
+++ b/test/link/wasm/infer-features/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
// Wasm Object file which we will use to infer the features from
diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig
index 57ee6acd18..2589b0dfcf 100644
--- a/test/link/wasm/producers/build.zig
+++ b/test/link/wasm/producers/build.zig
@@ -1,8 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/segments/build.zig b/test/link/wasm/segments/build.zig
index 8f7d9e0583..76160e905f 100644
--- a/test/link/wasm/segments/build.zig
+++ b/test/link/wasm/segments/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig
index 42971c607d..95c7643880 100644
--- a/test/link/wasm/stack_pointer/build.zig
+++ b/test/link/wasm/stack_pointer/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig
index 7fa3849083..816b57ccab 100644
--- a/test/link/wasm/type/build.zig
+++ b/test/link/wasm/type/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
test_step.dependOn(b.getInstallStep());
diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig
index a885faaadf..edd48321c9 100644
--- a/test/src/compare_output.zig
+++ b/test/src/compare_output.zig
@@ -1,7 +1,6 @@
// This is the implementation of the test harness.
// For the actual test cases, see test/compare_output.zig.
const std = @import("std");
-const build = std.build;
const ArrayList = std.ArrayList;
const fmt = std.fmt;
const mem = std.mem;
@@ -9,8 +8,8 @@ const fs = std.fs;
const OptimizeMode = std.builtin.OptimizeMode;
pub const CompareOutputContext = struct {
- b: *build.Builder,
- step: *build.Step,
+ b: *std.Build,
+ step: *std.Build.Step,
test_index: usize,
test_filter: ?[]const u8,
optimize_modes: []const OptimizeMode,
diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig
index 0c54655b32..2103172ed6 100644
--- a/test/src/run_translated_c.zig
+++ b/test/src/run_translated_c.zig
@@ -1,15 +1,14 @@
// This is the implementation of the test harness for running translated
// C code. For the actual test cases, see test/run_translated_c.zig.
const std = @import("std");
-const build = std.build;
const ArrayList = std.ArrayList;
const fmt = std.fmt;
const mem = std.mem;
const fs = std.fs;
pub const RunTranslatedCContext = struct {
- b: *build.Builder,
- step: *build.Step,
+ b: *std.Build,
+ step: *std.Build.Step,
test_index: usize,
test_filter: ?[]const u8,
target: std.zig.CrossTarget,
diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig
index ad5fbb7091..e275ee57ee 100644
--- a/test/src/translate_c.zig
+++ b/test/src/translate_c.zig
@@ -1,7 +1,6 @@
// This is the implementation of the test harness.
// For the actual test cases, see test/translate_c.zig.
const std = @import("std");
-const build = std.build;
const ArrayList = std.ArrayList;
const fmt = std.fmt;
const mem = std.mem;
@@ -9,8 +8,8 @@ const fs = std.fs;
const CrossTarget = std.zig.CrossTarget;
pub const TranslateCContext = struct {
- b: *build.Builder,
- step: *build.Step,
+ b: *std.Build,
+ step: *std.Build.Step,
test_index: usize,
test_filter: ?[]const u8,
diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig
index 89250ff96f..7c32a09bef 100644
--- a/test/standalone/brace_expansion/build.zig
+++ b/test/standalone/brace_expansion/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const main = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
.optimize = b.standardOptimizeOption(.{}),
diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig
index 6959f810d6..dce999d4a2 100644
--- a/test/standalone/c_compiler/build.zig
+++ b/test/standalone/c_compiler/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
-// TODO integrate this with the std.build executor API
+// TODO integrate this with the std.Build executor API
fn isRunnableTarget(t: CrossTarget) bool {
if (t.isNative()) return true;
@@ -11,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
t.getCpuArch() == builtin.cpu.arch);
}
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/standalone/emit_asm_and_bin/build.zig b/test/standalone/emit_asm_and_bin/build.zig
index b8cbd5fc17..5345f0f538 100644
--- a/test/standalone/emit_asm_and_bin/build.zig
+++ b/test/standalone/emit_asm_and_bin/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const main = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
.optimize = b.standardOptimizeOption(.{}),
diff --git a/test/standalone/empty_env/build.zig b/test/standalone/empty_env/build.zig
index ecdd74aa90..c4b4846141 100644
--- a/test/standalone/empty_env/build.zig
+++ b/test/standalone/empty_env/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const main = b.addExecutable(.{
.name = "main",
.root_source_file = .{ .path = "main.zig" },
diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig
index 3064c6cc08..9f79c80fcf 100644
--- a/test/standalone/global_linkage/build.zig
+++ b/test/standalone/global_linkage/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const obj1 = b.addStaticLibrary(.{
diff --git a/test/standalone/install_raw_hex/build.zig b/test/standalone/install_raw_hex/build.zig
index 94016b1d74..b0f938a344 100644
--- a/test/standalone/install_raw_hex/build.zig
+++ b/test/standalone/install_raw_hex/build.zig
@@ -1,8 +1,8 @@
const builtin = @import("builtin");
const std = @import("std");
-const CheckFileStep = std.build.CheckFileStep;
+const CheckFileStep = std.Build.CheckFileStep;
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
const target = .{
.cpu_arch = .thumb,
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
diff --git a/test/standalone/issue_11595/build.zig b/test/standalone/issue_11595/build.zig
index b0310947f6..c335fb73da 100644
--- a/test/standalone/issue_11595/build.zig
+++ b/test/standalone/issue_11595/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
-// TODO integrate this with the std.build executor API
+// TODO integrate this with the std.Build executor API
fn isRunnableTarget(t: CrossTarget) bool {
if (t.isNative()) return true;
@@ -11,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
t.getCpuArch() == builtin.cpu.arch);
}
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/standalone/issue_12588/build.zig b/test/standalone/issue_12588/build.zig
index 27a23d5a76..9f14c53e38 100644
--- a/test/standalone/issue_12588/build.zig
+++ b/test/standalone/issue_12588/build.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const Builder = std.build.Builder;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig
index e3c40d34c6..9d616477a2 100644
--- a/test/standalone/issue_12706/build.zig
+++ b/test/standalone/issue_12706/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
-// TODO integrate this with the std.build executor API
+// TODO integrate this with the std.Build executor API
fn isRunnableTarget(t: CrossTarget) bool {
if (t.isNative()) return true;
@@ -11,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
t.getCpuArch() == builtin.cpu.arch);
}
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/standalone/issue_13030/build.zig b/test/standalone/issue_13030/build.zig
index 510c7610d9..258d9b7db8 100644
--- a/test/standalone/issue_13030/build.zig
+++ b/test/standalone/issue_13030/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig
index 34c555cfdb..62ac128aab 100644
--- a/test/standalone/issue_339/build.zig
+++ b/test/standalone/issue_339/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const obj = b.addObject(.{
.name = "test",
.root_source_file = .{ .path = "test.zig" },
diff --git a/test/standalone/issue_5825/build.zig b/test/standalone/issue_5825/build.zig
index 8d7acc3e9a..89272280d4 100644
--- a/test/standalone/issue_5825/build.zig
+++ b/test/standalone/issue_5825/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const target = .{
.cpu_arch = .x86_64,
.os_tag = .windows,
diff --git a/test/standalone/issue_7030/build.zig b/test/standalone/issue_7030/build.zig
index 41a646abe8..dc535318cc 100644
--- a/test/standalone/issue_7030/build.zig
+++ b/test/standalone/issue_7030/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "issue_7030",
.root_source_file = .{ .path = "main.zig" },
diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig
index 59ff7ea9ab..3089a28fd0 100644
--- a/test/standalone/issue_794/build.zig
+++ b/test/standalone/issue_794/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_artifact = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
});
diff --git a/test/standalone/issue_8550/build.zig b/test/standalone/issue_8550/build.zig
index 233f701661..c3303d55db 100644
--- a/test/standalone/issue_8550/build.zig
+++ b/test/standalone/issue_8550/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) !void {
+pub fn build(b: *std.Build) !void {
const target = std.zig.CrossTarget{
.os_tag = .freestanding,
.cpu_arch = .arm,
diff --git a/test/standalone/issue_9812/build.zig b/test/standalone/issue_9812/build.zig
index 50eefe846c..4ca55ce999 100644
--- a/test/standalone/issue_9812/build.zig
+++ b/test/standalone/issue_9812/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) !void {
+pub fn build(b: *std.Build) !void {
const optimize = b.standardOptimizeOption(.{});
const zip_add = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig
index 1aca02bc71..44fc37893c 100644
--- a/test/standalone/load_dynamic_library/build.zig
+++ b/test/standalone/load_dynamic_library/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/standalone/main_pkg_path/build.zig b/test/standalone/main_pkg_path/build.zig
index baee74052e..f9919d5ab5 100644
--- a/test/standalone/main_pkg_path/build.zig
+++ b/test/standalone/main_pkg_path/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_exe = b.addTest(.{
.root_source_file = .{ .path = "a/test.zig" },
});
diff --git a/test/standalone/mix_c_files/build.zig b/test/standalone/mix_c_files/build.zig
index ad69f05ff6..f2dfb2093f 100644
--- a/test/standalone/mix_c_files/build.zig
+++ b/test/standalone/mix_c_files/build.zig
@@ -1,9 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
-// TODO integrate this with the std.build executor API
+// TODO integrate this with the std.Build executor API
fn isRunnableTarget(t: CrossTarget) bool {
if (t.isNative()) return true;
@@ -11,7 +10,7 @@ fn isRunnableTarget(t: CrossTarget) bool {
t.getCpuArch() == builtin.cpu.arch);
}
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
diff --git a/test/standalone/mix_o_files/build.zig b/test/standalone/mix_o_files/build.zig
index de37265388..2708343aa5 100644
--- a/test/standalone/mix_o_files/build.zig
+++ b/test/standalone/mix_o_files/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const obj = b.addObject(.{
diff --git a/test/standalone/options/build.zig b/test/standalone/options/build.zig
index 87a584a887..3f1e823359 100644
--- a/test/standalone/options/build.zig
+++ b/test/standalone/options/build.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn build(b: *std.build.Builder) void {
+pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig
index 3f0b8b9f2f..d51ea27328 100644
--- a/test/standalone/pie/build.zig
+++ b/test/standalone/pie/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const main = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
.optimize = b.standardOptimizeOption(.{}),
diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig
index 8dcfaeded0..5fbc8a67ae 100644
--- a/test/standalone/pkg_import/build.zig
+++ b/test/standalone/pkg_import/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig
index 135be095bc..91f7c8a06a 100644
--- a/test/standalone/shared_library/build.zig
+++ b/test/standalone/shared_library/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
const lib = b.addSharedLibrary(.{
diff --git a/test/standalone/static_c_lib/build.zig b/test/standalone/static_c_lib/build.zig
index 81b4349e20..9937888843 100644
--- a/test/standalone/static_c_lib/build.zig
+++ b/test/standalone/static_c_lib/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const foo = b.addStaticLibrary(.{
diff --git a/test/standalone/test_runner_path/build.zig b/test/standalone/test_runner_path/build.zig
index 9b02da50c1..f073c55d4a 100644
--- a/test/standalone/test_runner_path/build.zig
+++ b/test/standalone/test_runner_path/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const test_exe = b.addTest(.{
.root_source_file = .{ .path = "test.zig" },
.kind = .test_exe,
diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig
index d2ca90f3ab..89e07efb22 100644
--- a/test/standalone/use_alias/build.zig
+++ b/test/standalone/use_alias/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const main = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
.optimize = b.standardOptimizeOption(.{}),
diff --git a/test/standalone/windows_spawn/build.zig b/test/standalone/windows_spawn/build.zig
index de58a602c3..3ebde5a50c 100644
--- a/test/standalone/windows_spawn/build.zig
+++ b/test/standalone/windows_spawn/build.zig
@@ -1,6 +1,6 @@
-const Builder = @import("std").build.Builder;
+const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const hello = b.addExecutable(.{
diff --git a/test/tests.zig b/test/tests.zig
index 575550be02..de25528dde 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -1,7 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const debug = std.debug;
-const build = std.build;
const CrossTarget = std.zig.CrossTarget;
const io = std.io;
const fs = std.fs;
@@ -9,9 +8,10 @@ const mem = std.mem;
const fmt = std.fmt;
const ArrayList = std.ArrayList;
const OptimizeMode = std.builtin.OptimizeMode;
-const LibExeObjStep = build.LibExeObjStep;
+const LibExeObjStep = std.Build.LibExeObjStep;
const Allocator = mem.Allocator;
-const ExecError = build.Builder.ExecError;
+const ExecError = std.Build.ExecError;
+const Step = std.Build.Step;
// Cases
const compare_output = @import("compare_output.zig");
@@ -462,7 +462,7 @@ const test_targets = blk: {
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
-pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *build.Step {
+pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
const cases = b.allocator.create(CompareOutputContext) catch unreachable;
cases.* = CompareOutputContext{
.b = b,
@@ -477,7 +477,7 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, optimi
return cases.step;
}
-pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *build.Step {
+pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
const cases = b.allocator.create(StackTracesContext) catch unreachable;
cases.* = StackTracesContext{
.b = b,
@@ -493,7 +493,7 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, optimize_
}
pub fn addStandaloneTests(
- b: *build.Builder,
+ b: *std.Build,
test_filter: ?[]const u8,
optimize_modes: []const OptimizeMode,
skip_non_native: bool,
@@ -506,7 +506,7 @@ pub fn addStandaloneTests(
enable_wasmtime: bool,
enable_wine: bool,
enable_symlinks_windows: bool,
-) *build.Step {
+) *Step {
const cases = b.allocator.create(StandaloneContext) catch unreachable;
cases.* = StandaloneContext{
.b = b,
@@ -532,13 +532,13 @@ pub fn addStandaloneTests(
}
pub fn addLinkTests(
- b: *build.Builder,
+ b: *std.Build,
test_filter: ?[]const u8,
optimize_modes: []const OptimizeMode,
enable_macos_sdk: bool,
omit_stage2: bool,
enable_symlinks_windows: bool,
-) *build.Step {
+) *Step {
const cases = b.allocator.create(StandaloneContext) catch unreachable;
cases.* = StandaloneContext{
.b = b,
@@ -556,7 +556,7 @@ pub fn addLinkTests(
return cases.step;
}
-pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *build.Step {
+pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
_ = test_filter;
_ = optimize_modes;
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:
return step;
}
-pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *build.Step {
+pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
const cases = b.allocator.create(CompareOutputContext) catch unreachable;
cases.* = CompareOutputContext{
.b = b,
@@ -592,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, opti
return cases.step;
}
-pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
+pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
const cases = b.allocator.create(TranslateCContext) catch unreachable;
cases.* = TranslateCContext{
.b = b,
@@ -607,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
}
pub fn addRunTranslatedCTests(
- b: *build.Builder,
+ b: *std.Build,
test_filter: ?[]const u8,
target: std.zig.CrossTarget,
-) *build.Step {
+) *Step {
const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
cases.* = .{
.b = b,
@@ -625,7 +625,7 @@ pub fn addRunTranslatedCTests(
return cases.step;
}
-pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
+pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {
const cases = b.allocator.create(GenHContext) catch unreachable;
cases.* = GenHContext{
.b = b,
@@ -640,7 +640,7 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
}
pub fn addPkgTests(
- b: *build.Builder,
+ b: *std.Build,
test_filter: ?[]const u8,
root_src: []const u8,
name: []const u8,
@@ -651,7 +651,7 @@ pub fn addPkgTests(
skip_libc: bool,
skip_stage1: bool,
skip_stage2: bool,
-) *build.Step {
+) *Step {
const step = b.step(b.fmt("test-{s}", .{name}), desc);
for (test_targets) |test_target| {
@@ -742,8 +742,8 @@ pub fn addPkgTests(
}
pub const StackTracesContext = struct {
- b: *build.Builder,
- step: *build.Step,
+ b: *std.Build,
+ step: *Step,
test_index: usize,
test_filter: ?[]const u8,
optimize_modes: []const OptimizeMode,
@@ -840,7 +840,7 @@ pub const StackTracesContext = struct {
const RunAndCompareStep = struct {
pub const base_id = .custom;
- step: build.Step,
+ step: Step,
context: *StackTracesContext,
exe: *LibExeObjStep,
name: []const u8,
@@ -858,7 +858,7 @@ pub const StackTracesContext = struct {
const allocator = context.b.allocator;
const ptr = allocator.create(RunAndCompareStep) catch unreachable;
ptr.* = RunAndCompareStep{
- .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
+ .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
.context = context,
.exe = exe,
.name = name,
@@ -871,7 +871,7 @@ pub const StackTracesContext = struct {
return ptr;
}
- fn make(step: *build.Step) !void {
+ fn make(step: *Step) !void {
const self = @fieldParentPtr(RunAndCompareStep, "step", step);
const b = self.context.b;
@@ -1014,8 +1014,8 @@ pub const StackTracesContext = struct {
};
pub const StandaloneContext = struct {
- b: *build.Builder,
- step: *build.Step,
+ b: *std.Build,
+ step: *Step,
test_index: usize,
test_filter: ?[]const u8,
optimize_modes: []const OptimizeMode,
@@ -1150,8 +1150,8 @@ pub const StandaloneContext = struct {
};
pub const GenHContext = struct {
- b: *build.Builder,
- step: *build.Step,
+ b: *std.Build,
+ step: *Step,
test_index: usize,
test_filter: ?[]const u8,
@@ -1178,7 +1178,7 @@ pub const GenHContext = struct {
};
const GenHCmpOutputStep = struct {
- step: build.Step,
+ step: Step,
context: *GenHContext,
obj: *LibExeObjStep,
name: []const u8,
@@ -1194,7 +1194,7 @@ pub const GenHContext = struct {
const allocator = context.b.allocator;
const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
ptr.* = GenHCmpOutputStep{
- .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make),
+ .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make),
.context = context,
.obj = obj,
.name = name,
@@ -1206,7 +1206,7 @@ pub const GenHContext = struct {
return ptr;
}
- fn make(step: *build.Step) !void {
+ fn make(step: *Step) !void {
const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
const b = self.context.b;
@@ -1348,7 +1348,7 @@ const c_abi_targets = [_]CrossTarget{
},
};
-pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool) *build.Step {
+pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
const step = b.step("test-c-abi", "Run the C ABI tests");
const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };
--
cgit v1.2.3
From 60c4befad39a1e1689872bc78dd1b8f8d5c34887 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 31 Jan 2023 00:20:37 -0700
Subject: build.zig: remove dead list of unused source files
---
build.zig | 160 --------------------------------------------------------------
1 file changed, 160 deletions(-)
(limited to 'build.zig')
diff --git a/build.zig b/build.zig
index 6b6c384fc4..713eac6517 100644
--- a/build.zig
+++ b/build.zig
@@ -870,166 +870,6 @@ fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
return duplicated;
}
-const softfloat_sources = [_][]const u8{
- "deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c",
- "deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c",
- "deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c",
- "deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c",
- "deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c",
- "deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c",
- "deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c",
- "deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c",
- "deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c",
- "deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c",
- "deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c",
- "deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c",
- "deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c",
- "deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c",
- "deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c",
- "deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c",
- "deps/SoftFloat-3e/source/f128M_add.c",
- "deps/SoftFloat-3e/source/f128M_div.c",
- "deps/SoftFloat-3e/source/f128M_eq.c",
- "deps/SoftFloat-3e/source/f128M_eq_signaling.c",
- "deps/SoftFloat-3e/source/f128M_le.c",
- "deps/SoftFloat-3e/source/f128M_le_quiet.c",
- "deps/SoftFloat-3e/source/f128M_lt.c",
- "deps/SoftFloat-3e/source/f128M_lt_quiet.c",
- "deps/SoftFloat-3e/source/f128M_mul.c",
- "deps/SoftFloat-3e/source/f128M_mulAdd.c",
- "deps/SoftFloat-3e/source/f128M_rem.c",
- "deps/SoftFloat-3e/source/f128M_roundToInt.c",
- "deps/SoftFloat-3e/source/f128M_sqrt.c",
- "deps/SoftFloat-3e/source/f128M_sub.c",
- "deps/SoftFloat-3e/source/f128M_to_f16.c",
- "deps/SoftFloat-3e/source/f128M_to_f32.c",
- "deps/SoftFloat-3e/source/f128M_to_f64.c",
- "deps/SoftFloat-3e/source/f128M_to_extF80M.c",
- "deps/SoftFloat-3e/source/f128M_to_i32.c",
- "deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c",
- "deps/SoftFloat-3e/source/f128M_to_i64.c",
- "deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c",
- "deps/SoftFloat-3e/source/f128M_to_ui32.c",
- "deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c",
- "deps/SoftFloat-3e/source/f128M_to_ui64.c",
- "deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c",
- "deps/SoftFloat-3e/source/extF80M_add.c",
- "deps/SoftFloat-3e/source/extF80M_div.c",
- "deps/SoftFloat-3e/source/extF80M_eq.c",
- "deps/SoftFloat-3e/source/extF80M_le.c",
- "deps/SoftFloat-3e/source/extF80M_lt.c",
- "deps/SoftFloat-3e/source/extF80M_mul.c",
- "deps/SoftFloat-3e/source/extF80M_rem.c",
- "deps/SoftFloat-3e/source/extF80M_roundToInt.c",
- "deps/SoftFloat-3e/source/extF80M_sqrt.c",
- "deps/SoftFloat-3e/source/extF80M_sub.c",
- "deps/SoftFloat-3e/source/extF80M_to_f16.c",
- "deps/SoftFloat-3e/source/extF80M_to_f32.c",
- "deps/SoftFloat-3e/source/extF80M_to_f64.c",
- "deps/SoftFloat-3e/source/extF80M_to_f128M.c",
- "deps/SoftFloat-3e/source/f16_add.c",
- "deps/SoftFloat-3e/source/f16_div.c",
- "deps/SoftFloat-3e/source/f16_eq.c",
- "deps/SoftFloat-3e/source/f16_isSignalingNaN.c",
- "deps/SoftFloat-3e/source/f16_lt.c",
- "deps/SoftFloat-3e/source/f16_mul.c",
- "deps/SoftFloat-3e/source/f16_mulAdd.c",
- "deps/SoftFloat-3e/source/f16_rem.c",
- "deps/SoftFloat-3e/source/f16_roundToInt.c",
- "deps/SoftFloat-3e/source/f16_sqrt.c",
- "deps/SoftFloat-3e/source/f16_sub.c",
- "deps/SoftFloat-3e/source/f16_to_extF80M.c",
- "deps/SoftFloat-3e/source/f16_to_f128M.c",
- "deps/SoftFloat-3e/source/f16_to_f64.c",
- "deps/SoftFloat-3e/source/f32_to_extF80M.c",
- "deps/SoftFloat-3e/source/f32_to_f128M.c",
- "deps/SoftFloat-3e/source/f64_to_extF80M.c",
- "deps/SoftFloat-3e/source/f64_to_f128M.c",
- "deps/SoftFloat-3e/source/f64_to_f16.c",
- "deps/SoftFloat-3e/source/i32_to_f128M.c",
- "deps/SoftFloat-3e/source/s_add256M.c",
- "deps/SoftFloat-3e/source/s_addCarryM.c",
- "deps/SoftFloat-3e/source/s_addComplCarryM.c",
- "deps/SoftFloat-3e/source/s_addF128M.c",
- "deps/SoftFloat-3e/source/s_addExtF80M.c",
- "deps/SoftFloat-3e/source/s_addM.c",
- "deps/SoftFloat-3e/source/s_addMagsF16.c",
- "deps/SoftFloat-3e/source/s_addMagsF32.c",
- "deps/SoftFloat-3e/source/s_addMagsF64.c",
- "deps/SoftFloat-3e/source/s_approxRecip32_1.c",
- "deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c",
- "deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c",
- "deps/SoftFloat-3e/source/s_approxRecip_1Ks.c",
- "deps/SoftFloat-3e/source/s_compare128M.c",
- "deps/SoftFloat-3e/source/s_compare96M.c",
- "deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c",
- "deps/SoftFloat-3e/source/s_countLeadingZeros16.c",
- "deps/SoftFloat-3e/source/s_countLeadingZeros32.c",
- "deps/SoftFloat-3e/source/s_countLeadingZeros64.c",
- "deps/SoftFloat-3e/source/s_countLeadingZeros8.c",
- "deps/SoftFloat-3e/source/s_eq128.c",
- "deps/SoftFloat-3e/source/s_invalidF128M.c",
- "deps/SoftFloat-3e/source/s_invalidExtF80M.c",
- "deps/SoftFloat-3e/source/s_isNaNF128M.c",
- "deps/SoftFloat-3e/source/s_le128.c",
- "deps/SoftFloat-3e/source/s_lt128.c",
- "deps/SoftFloat-3e/source/s_mul128MTo256M.c",
- "deps/SoftFloat-3e/source/s_mul64To128M.c",
- "deps/SoftFloat-3e/source/s_mulAddF128M.c",
- "deps/SoftFloat-3e/source/s_mulAddF16.c",
- "deps/SoftFloat-3e/source/s_mulAddF32.c",
- "deps/SoftFloat-3e/source/s_mulAddF64.c",
- "deps/SoftFloat-3e/source/s_negXM.c",
- "deps/SoftFloat-3e/source/s_normExtF80SigM.c",
- "deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c",
- "deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c",
- "deps/SoftFloat-3e/source/s_normRoundPackToF16.c",
- "deps/SoftFloat-3e/source/s_normRoundPackToF32.c",
- "deps/SoftFloat-3e/source/s_normRoundPackToF64.c",
- "deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c",
- "deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c",
- "deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c",
- "deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c",
- "deps/SoftFloat-3e/source/s_remStepMBy32.c",
- "deps/SoftFloat-3e/source/s_roundMToI64.c",
- "deps/SoftFloat-3e/source/s_roundMToUI64.c",
- "deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c",
- "deps/SoftFloat-3e/source/s_roundPackMToF128M.c",
- "deps/SoftFloat-3e/source/s_roundPackToF16.c",
- "deps/SoftFloat-3e/source/s_roundPackToF32.c",
- "deps/SoftFloat-3e/source/s_roundPackToF64.c",
- "deps/SoftFloat-3e/source/s_roundToI32.c",
- "deps/SoftFloat-3e/source/s_roundToI64.c",
- "deps/SoftFloat-3e/source/s_roundToUI32.c",
- "deps/SoftFloat-3e/source/s_roundToUI64.c",
- "deps/SoftFloat-3e/source/s_shiftLeftM.c",
- "deps/SoftFloat-3e/source/s_shiftNormSigF128M.c",
- "deps/SoftFloat-3e/source/s_shiftRightJam256M.c",
- "deps/SoftFloat-3e/source/s_shiftRightJam32.c",
- "deps/SoftFloat-3e/source/s_shiftRightJam64.c",
- "deps/SoftFloat-3e/source/s_shiftRightJamM.c",
- "deps/SoftFloat-3e/source/s_shiftRightM.c",
- "deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c",
- "deps/SoftFloat-3e/source/s_shortShiftLeftM.c",
- "deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c",
- "deps/SoftFloat-3e/source/s_shortShiftRightJam64.c",
- "deps/SoftFloat-3e/source/s_shortShiftRightJamM.c",
- "deps/SoftFloat-3e/source/s_shortShiftRightM.c",
- "deps/SoftFloat-3e/source/s_sub1XM.c",
- "deps/SoftFloat-3e/source/s_sub256M.c",
- "deps/SoftFloat-3e/source/s_subM.c",
- "deps/SoftFloat-3e/source/s_subMagsF16.c",
- "deps/SoftFloat-3e/source/s_subMagsF32.c",
- "deps/SoftFloat-3e/source/s_subMagsF64.c",
- "deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c",
- "deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c",
- "deps/SoftFloat-3e/source/softfloat_state.c",
- "deps/SoftFloat-3e/source/ui32_to_f128M.c",
- "deps/SoftFloat-3e/source/ui64_to_f128M.c",
- "deps/SoftFloat-3e/source/ui32_to_extF80M.c",
- "deps/SoftFloat-3e/source/ui64_to_extF80M.c",
-};
-
const zig_cpp_sources = [_][]const u8{
// These are planned to stay even when we are self-hosted.
"src/zig_llvm.cpp",
--
cgit v1.2.3
From 16cdd1297ebfac534615eaeb8439a4e1de71837c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 31 Jan 2023 13:44:06 -0700
Subject: rename std.Build.LibExeObjStep to std.Build.CompileStep
This matches the nomenclature internally: a Compilation is the main type
that represents a single invokation of the compiler.
---
build.zig | 12 +-
lib/std/Build.zig | 49 +-
lib/std/Build/CheckObjectStep.zig | 2 +-
lib/std/Build/CompileStep.zig | 2045 +++++++++++++++++++++++++++
lib/std/Build/EmulatableRunStep.zig | 6 +-
lib/std/Build/InstallArtifactStep.zig | 8 +-
lib/std/Build/InstallRawStep.zig | 6 +-
lib/std/Build/LibExeObjStep.zig | 2045 ---------------------------
lib/std/Build/OptionsStep.zig | 6 +-
lib/std/Build/RunStep.zig | 10 +-
lib/std/Build/Step.zig | 4 +-
lib/std/Build/TranslateCStep.zig | 6 +-
lib/std/std.zig | 2 +
test/link/macho/bugs/13457/build.zig | 1 -
test/link/macho/dead_strip/build.zig | 7 +-
test/link/macho/dead_strip_dylibs/build.zig | 3 +-
test/link/macho/headerpad/build.zig | 3 +-
test/link/macho/needed_framework/build.zig | 1 -
test/link/macho/needed_library/build.zig | 1 -
test/link/macho/search_strategy/build.zig | 7 +-
test/link/macho/strict_validation/build.zig | 1 -
test/link/macho/unwind_info/build.zig | 7 +-
test/link/macho/uuid/build.zig | 7 +-
test/link/macho/weak_framework/build.zig | 1 -
test/link/macho/weak_library/build.zig | 1 -
test/tests.zig | 10 +-
26 files changed, 2130 insertions(+), 2121 deletions(-)
create mode 100644 lib/std/Build/CompileStep.zig
delete mode 100644 lib/std/Build/LibExeObjStep.zig
(limited to 'build.zig')
diff --git a/build.zig b/build.zig
index 713eac6517..ecf51fe316 100644
--- a/build.zig
+++ b/build.zig
@@ -516,7 +516,7 @@ fn addCompilerStep(
b: *std.Build,
optimize: std.builtin.OptimizeMode,
target: std.zig.CrossTarget,
-) *std.Build.LibExeObjStep {
+) *std.Build.CompileStep {
const exe = b.addExecutable(.{
.name = "zig",
.root_source_file = .{ .path = "src/main.zig" },
@@ -544,7 +544,7 @@ const exe_cflags = [_][]const u8{
fn addCmakeCfgOptionsToExe(
b: *std.Build,
cfg: CMakeConfig,
- exe: *std.Build.LibExeObjStep,
+ exe: *std.Build.CompileStep,
use_zig_libcxx: bool,
) !void {
if (exe.target.isDarwin()) {
@@ -623,7 +623,7 @@ fn addCmakeCfgOptionsToExe(
}
}
-fn addStaticLlvmOptionsToExe(exe: *std.Build.LibExeObjStep) !void {
+fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void {
// Adds the Zig C++ sources which both stage1 and stage2 need.
//
// We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
@@ -662,7 +662,7 @@ fn addStaticLlvmOptionsToExe(exe: *std.Build.LibExeObjStep) !void {
fn addCxxKnownPath(
b: *std.Build,
ctx: CMakeConfig,
- exe: *std.Build.LibExeObjStep,
+ exe: *std.Build.CompileStep,
objname: []const u8,
errtxt: ?[]const u8,
need_cpp_includes: bool,
@@ -695,7 +695,7 @@ fn addCxxKnownPath(
}
}
-fn addCMakeLibraryList(exe: *std.Build.LibExeObjStep, list: []const u8) void {
+fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void {
var it = mem.tokenize(u8, list, ";");
while (it.next()) |lib| {
if (mem.startsWith(u8, lib, "-l")) {
@@ -709,7 +709,7 @@ fn addCMakeLibraryList(exe: *std.Build.LibExeObjStep, list: []const u8) void {
}
const CMakeConfig = struct {
- llvm_linkage: std.Build.LibExeObjStep.Linkage,
+ llvm_linkage: std.Build.CompileStep.Linkage,
cmake_binary_dir: []const u8,
cmake_prefix_path: []const u8,
cmake_static_library_prefix: []const u8,
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index c0152ef6f1..4647baa398 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -19,6 +19,9 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo;
const Sha256 = std.crypto.hash.sha2.Sha256;
const Build = @This();
+///// deprecated: use `CompileStep` instead.
+//pub const LibExeObjStep = CompileStep;
+
pub const Step = @import("Build/Step.zig");
pub const CheckFileStep = @import("Build/CheckFileStep.zig");
pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
@@ -29,7 +32,7 @@ pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
pub const InstallDirStep = @import("Build/InstallDirStep.zig");
pub const InstallFileStep = @import("Build/InstallFileStep.zig");
pub const InstallRawStep = @import("Build/InstallRawStep.zig");
-pub const LibExeObjStep = @import("Build/LibExeObjStep.zig");
+pub const CompileStep = @import("Build/CompileStep.zig");
pub const LogStep = @import("Build/LogStep.zig");
pub const OptionsStep = @import("Build/OptionsStep.zig");
pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
@@ -423,11 +426,11 @@ pub const ExecutableOptions = struct {
version: ?std.builtin.Version = null,
target: CrossTarget = .{},
optimize: std.builtin.Mode = .Debug,
- linkage: ?LibExeObjStep.Linkage = null,
+ linkage: ?CompileStep.Linkage = null,
};
-pub fn addExecutable(b: *Build, options: ExecutableOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
+pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
+ return CompileStep.create(b, .{
.name = options.name,
.root_source_file = options.root_source_file,
.version = options.version,
@@ -445,8 +448,8 @@ pub const ObjectOptions = struct {
optimize: std.builtin.Mode,
};
-pub fn addObject(b: *Build, options: ObjectOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
+pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
+ return CompileStep.create(b, .{
.name = options.name,
.root_source_file = options.root_source_file,
.target = options.target,
@@ -463,8 +466,8 @@ pub const SharedLibraryOptions = struct {
optimize: std.builtin.Mode,
};
-pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
+pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
+ return CompileStep.create(b, .{
.name = options.name,
.root_source_file = options.root_source_file,
.kind = .lib,
@@ -483,8 +486,8 @@ pub const StaticLibraryOptions = struct {
version: ?std.builtin.Version = null,
};
-pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
+pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
+ return CompileStep.create(b, .{
.name = options.name,
.root_source_file = options.root_source_file,
.kind = .lib,
@@ -497,15 +500,15 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *LibExeObjStep
pub const TestOptions = struct {
name: []const u8 = "test",
- kind: LibExeObjStep.Kind = .@"test",
+ kind: CompileStep.Kind = .@"test",
root_source_file: FileSource,
target: CrossTarget = .{},
optimize: std.builtin.Mode = .Debug,
version: ?std.builtin.Version = null,
};
-pub fn addTest(b: *Build, options: TestOptions) *LibExeObjStep {
- return LibExeObjStep.create(b, .{
+pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
+ return CompileStep.create(b, .{
.name = options.name,
.kind = options.kind,
.root_source_file = options.root_source_file,
@@ -521,8 +524,8 @@ pub const AssemblyOptions = struct {
optimize: std.builtin.Mode,
};
-pub fn addAssembly(b: *Build, options: AssemblyOptions) *LibExeObjStep {
- const obj_step = LibExeObjStep.create(b, .{
+pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
+ const obj_step = CompileStep.create(b, .{
.name = options.name,
.root_source_file = null,
.target = options.target,
@@ -536,7 +539,7 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *LibExeObjStep {
/// executable. More command line arguments can be added with `addArg`,
/// `addArgs`, and `addArtifactArg`.
/// Be careful using this function, as it introduces a system dependency.
-/// To run an executable built with zig build, see `LibExeObjStep.run`.
+/// To run an executable built with zig build, see `CompileStep.run`.
pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
assert(argv.len >= 1);
const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
@@ -1167,11 +1170,11 @@ pub fn makePath(self: *Build, path: []const u8) !void {
};
}
-pub fn installArtifact(self: *Build, artifact: *LibExeObjStep) void {
+pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
}
-pub fn addInstallArtifact(self: *Build, artifact: *LibExeObjStep) *InstallArtifactStep {
+pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep {
return InstallArtifactStep.create(self, artifact);
}
@@ -1195,7 +1198,7 @@ pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const
}
/// Output format (BIN vs Intel HEX) determined by filename
-pub fn installRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
+pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
const raw = self.addInstallRaw(artifact, dest_filename, options);
self.getInstallStep().dependOn(&raw.step);
return raw;
@@ -1220,7 +1223,7 @@ pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []co
return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
}
-pub fn addInstallRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
+pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
return InstallRawStep.create(self, artifact, dest_filename, options);
}
@@ -1456,8 +1459,8 @@ pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8)
pub const Dependency = struct {
builder: *Build,
- pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
- var found: ?*LibExeObjStep = null;
+ pub fn artifact(d: *Dependency, name: []const u8) *CompileStep {
+ var found: ?*CompileStep = null;
for (d.builder.install_tls.step.dependencies.items) |dep_step| {
const inst = dep_step.cast(InstallArtifactStep) orelse continue;
if (mem.eql(u8, inst.artifact.name, name)) {
@@ -1767,7 +1770,7 @@ test {
_ = InstallDirStep;
_ = InstallFileStep;
_ = InstallRawStep;
- _ = LibExeObjStep;
+ _ = CompileStep;
_ = LogStep;
_ = OptionsStep;
_ = RemoveDirStep;
diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig
index 7907be1787..bfa5338927 100644
--- a/lib/std/Build/CheckObjectStep.zig
+++ b/lib/std/Build/CheckObjectStep.zig
@@ -42,7 +42,7 @@ pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
const dependencies_len = self.step.dependencies.items.len;
assert(dependencies_len > 0);
const exe_step = self.step.dependencies.items[dependencies_len - 1];
- const exe = exe_step.cast(std.Build.LibExeObjStep).?;
+ const exe = exe_step.cast(std.Build.CompileStep).?;
const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
emulatable_step.step.dependOn(&self.step);
return emulatable_step;
diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig
new file mode 100644
index 0000000000..5257c85a5c
--- /dev/null
+++ b/lib/std/Build/CompileStep.zig
@@ -0,0 +1,2045 @@
+const builtin = @import("builtin");
+const std = @import("../std.zig");
+const mem = std.mem;
+const log = std.log;
+const fs = std.fs;
+const assert = std.debug.assert;
+const panic = std.debug.panic;
+const ArrayList = std.ArrayList;
+const StringHashMap = std.StringHashMap;
+const Sha256 = std.crypto.hash.sha2.Sha256;
+const Allocator = mem.Allocator;
+const Step = std.Build.Step;
+const CrossTarget = std.zig.CrossTarget;
+const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+const FileSource = std.Build.FileSource;
+const PkgConfigPkg = std.Build.PkgConfigPkg;
+const PkgConfigError = std.Build.PkgConfigError;
+const ExecError = std.Build.ExecError;
+const Pkg = std.Build.Pkg;
+const VcpkgRoot = std.Build.VcpkgRoot;
+const InstallDir = std.Build.InstallDir;
+const InstallArtifactStep = std.Build.InstallArtifactStep;
+const GeneratedFile = std.Build.GeneratedFile;
+const InstallRawStep = std.Build.InstallRawStep;
+const EmulatableRunStep = std.Build.EmulatableRunStep;
+const CheckObjectStep = std.Build.CheckObjectStep;
+const RunStep = std.Build.RunStep;
+const OptionsStep = std.Build.OptionsStep;
+const ConfigHeaderStep = std.Build.ConfigHeaderStep;
+const CompileStep = @This();
+
+pub const base_id: Step.Id = .compile;
+
+step: Step,
+builder: *std.Build,
+name: []const u8,
+target: CrossTarget,
+target_info: NativeTargetInfo,
+optimize: std.builtin.Mode,
+linker_script: ?FileSource = null,
+version_script: ?[]const u8 = null,
+out_filename: []const u8,
+linkage: ?Linkage = null,
+version: ?std.builtin.Version,
+kind: Kind,
+major_only_filename: ?[]const u8,
+name_only_filename: ?[]const u8,
+strip: ?bool,
+unwind_tables: ?bool,
+// keep in sync with src/link.zig:CompressDebugSections
+compress_debug_sections: enum { none, zlib } = .none,
+lib_paths: ArrayList([]const u8),
+rpaths: ArrayList([]const u8),
+framework_dirs: ArrayList([]const u8),
+frameworks: StringHashMap(FrameworkLinkInfo),
+verbose_link: bool,
+verbose_cc: bool,
+emit_analysis: EmitOption = .default,
+emit_asm: EmitOption = .default,
+emit_bin: EmitOption = .default,
+emit_docs: EmitOption = .default,
+emit_implib: EmitOption = .default,
+emit_llvm_bc: EmitOption = .default,
+emit_llvm_ir: EmitOption = .default,
+// Lots of things depend on emit_h having a consistent path,
+// so it is not an EmitOption for now.
+emit_h: bool = false,
+bundle_compiler_rt: ?bool = null,
+single_threaded: ?bool = null,
+stack_protector: ?bool = null,
+disable_stack_probing: bool,
+disable_sanitize_c: bool,
+sanitize_thread: bool,
+rdynamic: bool,
+import_memory: bool = false,
+/// For WebAssembly targets, this will allow for undefined symbols to
+/// be imported from the host environment.
+import_symbols: bool = false,
+import_table: bool = false,
+export_table: bool = false,
+initial_memory: ?u64 = null,
+max_memory: ?u64 = null,
+shared_memory: bool = false,
+global_base: ?u64 = null,
+c_std: std.Build.CStd,
+override_lib_dir: ?[]const u8,
+main_pkg_path: ?[]const u8,
+exec_cmd_args: ?[]const ?[]const u8,
+name_prefix: []const u8,
+filter: ?[]const u8,
+test_evented_io: bool = false,
+test_runner: ?[]const u8,
+code_model: std.builtin.CodeModel = .default,
+wasi_exec_model: ?std.builtin.WasiExecModel = null,
+/// Symbols to be exported when compiling to wasm
+export_symbol_names: []const []const u8 = &.{},
+
+root_src: ?FileSource,
+out_h_filename: []const u8,
+out_lib_filename: []const u8,
+out_pdb_filename: []const u8,
+packages: ArrayList(Pkg),
+
+object_src: []const u8,
+
+link_objects: ArrayList(LinkObject),
+include_dirs: ArrayList(IncludeDir),
+c_macros: ArrayList([]const u8),
+installed_headers: ArrayList(*Step),
+output_dir: ?[]const u8,
+is_linking_libc: bool = false,
+is_linking_libcpp: bool = false,
+vcpkg_bin_path: ?[]const u8 = null,
+
+/// This may be set in order to override the default install directory
+override_dest_dir: ?InstallDir,
+installed_path: ?[]const u8,
+install_step: ?*InstallArtifactStep,
+
+/// Base address for an executable image.
+image_base: ?u64 = null,
+
+libc_file: ?FileSource = null,
+
+valgrind_support: ?bool = null,
+each_lib_rpath: ?bool = null,
+/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
+/// which can be used to coordinate a stripped binary with its debug symbols.
+/// As an example, the bloaty project refuses to work unless its inputs have
+/// build ids, in order to prevent accidental mismatches.
+/// The default is to not include this section because it slows down linking.
+build_id: ?bool = null,
+
+/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
+/// file.
+link_eh_frame_hdr: bool = false,
+link_emit_relocs: bool = false,
+
+/// Place every function in its own section so that unused ones may be
+/// safely garbage-collected during the linking phase.
+link_function_sections: bool = false,
+
+/// Remove functions and data that are unreachable by the entry point or
+/// exported symbols.
+link_gc_sections: ?bool = null,
+
+linker_allow_shlib_undefined: ?bool = null,
+
+/// Permit read-only relocations in read-only segments. Disallowed by default.
+link_z_notext: bool = false,
+
+/// Force all relocations to be read-only after processing.
+link_z_relro: bool = true,
+
+/// Allow relocations to be lazily processed after load.
+link_z_lazy: bool = false,
+
+/// Common page size
+link_z_common_page_size: ?u64 = null,
+
+/// Maximum page size
+link_z_max_page_size: ?u64 = null,
+
+/// (Darwin) Install name for the dylib
+install_name: ?[]const u8 = null,
+
+/// (Darwin) Path to entitlements file
+entitlements: ?[]const u8 = null,
+
+/// (Darwin) Size of the pagezero segment.
+pagezero_size: ?u64 = null,
+
+/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
+/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
+/// option.
+/// By default, if no option is specified, the linker assumes `paths_first` as the default
+/// search strategy.
+search_strategy: ?enum { paths_first, dylibs_first } = null,
+
+/// (Darwin) Set size of the padding between the end of load commands
+/// and start of `__TEXT,__text` section.
+headerpad_size: ?u32 = null,
+
+/// (Darwin) Automatically Set size of the padding between the end of load commands
+/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
+headerpad_max_install_names: bool = false,
+
+/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
+dead_strip_dylibs: bool = false,
+
+/// Position Independent Code
+force_pic: ?bool = null,
+
+/// Position Independent Executable
+pie: ?bool = null,
+
+red_zone: ?bool = null,
+
+omit_frame_pointer: ?bool = null,
+dll_export_fns: ?bool = null,
+
+subsystem: ?std.Target.SubSystem = null,
+
+entry_symbol_name: ?[]const u8 = null,
+
+/// Overrides the default stack size
+stack_size: ?u64 = null,
+
+want_lto: ?bool = null,
+use_llvm: ?bool = null,
+use_lld: ?bool = null,
+
+output_path_source: GeneratedFile,
+output_lib_path_source: GeneratedFile,
+output_h_path_source: GeneratedFile,
+output_pdb_path_source: GeneratedFile,
+
+pub const CSourceFiles = struct {
+ files: []const []const u8,
+ flags: []const []const u8,
+};
+
+pub const CSourceFile = struct {
+ source: FileSource,
+ args: []const []const u8,
+
+ pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile {
+ return .{
+ .source = self.source.dupe(b),
+ .args = b.dupeStrings(self.args),
+ };
+ }
+};
+
+pub const LinkObject = union(enum) {
+ static_path: FileSource,
+ other_step: *CompileStep,
+ system_lib: SystemLib,
+ assembly_file: FileSource,
+ c_source_file: *CSourceFile,
+ c_source_files: *CSourceFiles,
+};
+
+pub const SystemLib = struct {
+ name: []const u8,
+ needed: bool,
+ weak: bool,
+ use_pkg_config: enum {
+ /// Don't use pkg-config, just pass -lfoo where foo is name.
+ no,
+ /// Try to get information on how to link the library from pkg-config.
+ /// If that fails, fall back to passing -lfoo where foo is name.
+ yes,
+ /// Try to get information on how to link the library from pkg-config.
+ /// If that fails, error out.
+ force,
+ },
+};
+
+const FrameworkLinkInfo = struct {
+ needed: bool = false,
+ weak: bool = false,
+};
+
+pub const IncludeDir = union(enum) {
+ raw_path: []const u8,
+ raw_path_system: []const u8,
+ other_step: *CompileStep,
+ config_header_step: *ConfigHeaderStep,
+};
+
+pub const Options = struct {
+ name: []const u8,
+ root_source_file: ?FileSource = null,
+ target: CrossTarget,
+ optimize: std.builtin.Mode,
+ kind: Kind,
+ linkage: ?Linkage = null,
+ version: ?std.builtin.Version = null,
+};
+
+pub const Kind = enum {
+ exe,
+ lib,
+ obj,
+ @"test",
+ test_exe,
+};
+
+pub const Linkage = enum { dynamic, static };
+
+pub const EmitOption = union(enum) {
+ default: void,
+ no_emit: void,
+ emit: void,
+ emit_to: []const u8,
+
+ fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 {
+ return switch (self) {
+ .no_emit => b.fmt("-fno-{s}", .{arg_name}),
+ .default => null,
+ .emit => b.fmt("-f{s}", .{arg_name}),
+ .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
+ };
+ }
+};
+
+pub fn create(builder: *std.Build, options: Options) *CompileStep {
+ const name = builder.dupe(options.name);
+ const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
+ if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
+ panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
+ }
+
+ const self = builder.allocator.create(CompileStep) catch unreachable;
+ self.* = CompileStep{
+ .strip = null,
+ .unwind_tables = null,
+ .builder = builder,
+ .verbose_link = false,
+ .verbose_cc = false,
+ .optimize = options.optimize,
+ .target = options.target,
+ .linkage = options.linkage,
+ .kind = options.kind,
+ .root_src = root_src,
+ .name = name,
+ .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
+ .step = Step.init(base_id, name, builder.allocator, make),
+ .version = options.version,
+ .out_filename = undefined,
+ .out_h_filename = builder.fmt("{s}.h", .{name}),
+ .out_lib_filename = undefined,
+ .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
+ .major_only_filename = null,
+ .name_only_filename = null,
+ .packages = ArrayList(Pkg).init(builder.allocator),
+ .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
+ .link_objects = ArrayList(LinkObject).init(builder.allocator),
+ .c_macros = ArrayList([]const u8).init(builder.allocator),
+ .lib_paths = ArrayList([]const u8).init(builder.allocator),
+ .rpaths = ArrayList([]const u8).init(builder.allocator),
+ .framework_dirs = ArrayList([]const u8).init(builder.allocator),
+ .installed_headers = ArrayList(*Step).init(builder.allocator),
+ .object_src = undefined,
+ .c_std = std.Build.CStd.C99,
+ .override_lib_dir = null,
+ .main_pkg_path = null,
+ .exec_cmd_args = null,
+ .name_prefix = "",
+ .filter = null,
+ .test_runner = null,
+ .disable_stack_probing = false,
+ .disable_sanitize_c = false,
+ .sanitize_thread = false,
+ .rdynamic = false,
+ .output_dir = null,
+ .override_dest_dir = null,
+ .installed_path = null,
+ .install_step = null,
+
+ .output_path_source = GeneratedFile{ .step = &self.step },
+ .output_lib_path_source = GeneratedFile{ .step = &self.step },
+ .output_h_path_source = GeneratedFile{ .step = &self.step },
+ .output_pdb_path_source = GeneratedFile{ .step = &self.step },
+
+ .target_info = NativeTargetInfo.detect(self.target) catch unreachable,
+ };
+ self.computeOutFileNames();
+ if (root_src) |rs| rs.addStepDependencies(&self.step);
+ return self;
+}
+
+fn computeOutFileNames(self: *CompileStep) void {
+ const target = self.target_info.target;
+
+ self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
+ .root_name = self.name,
+ .target = target,
+ .output_mode = switch (self.kind) {
+ .lib => .Lib,
+ .obj => .Obj,
+ .exe, .@"test", .test_exe => .Exe,
+ },
+ .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
+ .dynamic => .Dynamic,
+ .static => .Static,
+ }) else null,
+ .version = self.version,
+ }) catch unreachable;
+
+ if (self.kind == .lib) {
+ if (self.linkage != null and self.linkage.? == .static) {
+ self.out_lib_filename = self.out_filename;
+ } else if (self.version) |version| {
+ if (target.isDarwin()) {
+ self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
+ self.name,
+ version.major,
+ });
+ self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
+ self.out_lib_filename = self.out_filename;
+ } else if (target.os.tag == .windows) {
+ self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
+ } else {
+ self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
+ self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
+ self.out_lib_filename = self.out_filename;
+ }
+ } else {
+ if (target.isDarwin()) {
+ self.out_lib_filename = self.out_filename;
+ } else if (target.os.tag == .windows) {
+ self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
+ } else {
+ self.out_lib_filename = self.out_filename;
+ }
+ }
+ if (self.output_dir != null) {
+ self.output_lib_path_source.path = self.builder.pathJoin(
+ &.{ self.output_dir.?, self.out_lib_filename },
+ );
+ }
+ }
+}
+
+pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
+ self.output_dir = self.builder.dupePath(dir);
+}
+
+pub fn install(self: *CompileStep) void {
+ self.builder.installArtifact(self);
+}
+
+pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
+ return self.builder.installRaw(self, dest_filename, options);
+}
+
+pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
+ const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
+ a.builder.getInstallStep().dependOn(&install_file.step);
+ a.installed_headers.append(&install_file.step) catch unreachable;
+}
+
+pub fn installHeadersDirectory(
+ a: *CompileStep,
+ src_dir_path: []const u8,
+ dest_rel_path: []const u8,
+) void {
+ return installHeadersDirectoryOptions(a, .{
+ .source_dir = src_dir_path,
+ .install_dir = .header,
+ .install_subdir = dest_rel_path,
+ });
+}
+
+pub fn installHeadersDirectoryOptions(
+ a: *CompileStep,
+ options: std.Build.InstallDirStep.Options,
+) void {
+ const install_dir = a.builder.addInstallDirectory(options);
+ a.builder.getInstallStep().dependOn(&install_dir.step);
+ a.installed_headers.append(&install_dir.step) catch unreachable;
+}
+
+pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
+ assert(l.kind == .lib);
+ const install_step = a.builder.getInstallStep();
+ // Copy each element from installed_headers, modifying the builder
+ // to be the new parent's builder.
+ for (l.installed_headers.items) |step| {
+ const step_copy = switch (step.id) {
+ inline .install_file, .install_dir => |id| blk: {
+ const T = id.Type();
+ const ptr = a.builder.allocator.create(T) catch unreachable;
+ ptr.* = step.cast(T).?.*;
+ ptr.override_source_builder = ptr.builder;
+ ptr.builder = a.builder;
+ break :blk &ptr.step;
+ },
+ else => unreachable,
+ };
+ a.installed_headers.append(step_copy) catch unreachable;
+ install_step.dependOn(step_copy);
+ }
+ a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
+}
+
+/// Creates a `RunStep` with an executable built with `addExecutable`.
+/// Add command line arguments with `addArg`.
+pub fn run(exe: *CompileStep) *RunStep {
+ assert(exe.kind == .exe or exe.kind == .test_exe);
+
+ // It doesn't have to be native. We catch that if you actually try to run it.
+ // Consider that this is declarative; the run step may not be run unless a user
+ // option is supplied.
+ const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
+ run_step.addArtifactArg(exe);
+
+ if (exe.kind == .test_exe) {
+ run_step.addArg(exe.builder.zig_exe);
+ }
+
+ if (exe.vcpkg_bin_path) |path| {
+ run_step.addPathDir(path);
+ }
+
+ return run_step;
+}
+
+/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
+/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
+/// When a binary cannot be ran through emulation or the option is disabled, a warning
+/// will be printed and the binary will *NOT* be ran.
+pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
+ assert(exe.kind == .exe or exe.kind == .test_exe);
+
+ const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
+ if (exe.vcpkg_bin_path) |path| {
+ RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
+ }
+ return run_step;
+}
+
+pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
+ return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
+}
+
+pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
+ self.linker_script = source.dupe(self.builder);
+ source.addStepDependencies(&self.step);
+}
+
+pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
+ self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
+}
+
+pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
+ self.frameworks.put(self.builder.dupe(framework_name), .{
+ .needed = true,
+ }) catch unreachable;
+}
+
+pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
+ self.frameworks.put(self.builder.dupe(framework_name), .{
+ .weak = true,
+ }) catch unreachable;
+}
+
+/// Returns whether the library, executable, or object depends on a particular system library.
+pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool {
+ if (isLibCLibrary(name)) {
+ return self.is_linking_libc;
+ }
+ if (isLibCppLibrary(name)) {
+ return self.is_linking_libcpp;
+ }
+ for (self.link_objects.items) |link_object| {
+ switch (link_object) {
+ .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
+ else => continue,
+ }
+ }
+ return false;
+}
+
+pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void {
+ assert(lib.kind == .lib);
+ self.linkLibraryOrObject(lib);
+}
+
+pub fn isDynamicLibrary(self: *CompileStep) bool {
+ return self.kind == .lib and self.linkage == Linkage.dynamic;
+}
+
+pub fn isStaticLibrary(self: *CompileStep) bool {
+ return self.kind == .lib and self.linkage != Linkage.dynamic;
+}
+
+pub fn producesPdbFile(self: *CompileStep) bool {
+ if (!self.target.isWindows() and !self.target.isUefi()) return false;
+ if (self.target.getObjectFormat() == .c) return false;
+ if (self.strip == true) return false;
+ return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
+}
+
+pub fn linkLibC(self: *CompileStep) void {
+ self.is_linking_libc = true;
+}
+
+pub fn linkLibCpp(self: *CompileStep) void {
+ self.is_linking_libcpp = true;
+}
+
+/// If the value is omitted, it is set to 1.
+/// `name` and `value` need not live longer than the function call.
+pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
+ const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
+ self.c_macros.append(macro) catch unreachable;
+}
+
+/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
+pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
+ self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
+}
+
+/// This one has no integration with anything, it just puts -lname on the command line.
+/// Prefer to use `linkSystemLibrary` instead.
+pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = false,
+ .weak = false,
+ .use_pkg_config = .no,
+ },
+ }) catch unreachable;
+}
+
+/// This one has no integration with anything, it just puts -needed-lname on the command line.
+/// Prefer to use `linkSystemLibraryNeeded` instead.
+pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = true,
+ .weak = false,
+ .use_pkg_config = .no,
+ },
+ }) catch unreachable;
+}
+
+/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
+/// command line. Prefer to use `linkSystemLibraryWeak` instead.
+pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = false,
+ .weak = true,
+ .use_pkg_config = .no,
+ },
+ }) catch unreachable;
+}
+
+/// This links against a system library, exclusively using pkg-config to find the library.
+/// Prefer to use `linkSystemLibrary` instead.
+pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(lib_name),
+ .needed = false,
+ .weak = false,
+ .use_pkg_config = .force,
+ },
+ }) catch unreachable;
+}
+
+/// This links against a system library, exclusively using pkg-config to find the library.
+/// Prefer to use `linkSystemLibraryNeeded` instead.
+pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(lib_name),
+ .needed = true,
+ .weak = false,
+ .use_pkg_config = .force,
+ },
+ }) catch unreachable;
+}
+
+/// Run pkg-config for the given library name and parse the output, returning the arguments
+/// that should be passed to zig to link the given library.
+pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
+ const pkg_name = match: {
+ // First we have to map the library name to pkg config name. Unfortunately,
+ // there are several examples where this is not straightforward:
+ // -lSDL2 -> pkg-config sdl2
+ // -lgdk-3 -> pkg-config gdk-3.0
+ // -latk-1.0 -> pkg-config atk
+ const pkgs = try getPkgConfigList(self.builder);
+
+ // Exact match means instant winner.
+ for (pkgs) |pkg| {
+ if (mem.eql(u8, pkg.name, lib_name)) {
+ break :match pkg.name;
+ }
+ }
+
+ // Next we'll try ignoring case.
+ for (pkgs) |pkg| {
+ if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
+ break :match pkg.name;
+ }
+ }
+
+ // Now try appending ".0".
+ for (pkgs) |pkg| {
+ if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
+ if (pos != 0) continue;
+ if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
+ break :match pkg.name;
+ }
+ }
+ }
+
+ // Trimming "-1.0".
+ if (mem.endsWith(u8, lib_name, "-1.0")) {
+ const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
+ for (pkgs) |pkg| {
+ if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
+ break :match pkg.name;
+ }
+ }
+ }
+
+ return error.PackageNotFound;
+ };
+
+ var code: u8 = undefined;
+ const stdout = if (self.builder.execAllowFail(&[_][]const u8{
+ "pkg-config",
+ pkg_name,
+ "--cflags",
+ "--libs",
+ }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
+ error.ProcessTerminated => return error.PkgConfigCrashed,
+ error.ExecNotSupported => return error.PkgConfigFailed,
+ error.ExitCodeFailure => return error.PkgConfigFailed,
+ error.FileNotFound => return error.PkgConfigNotInstalled,
+ error.ChildExecFailed => return error.PkgConfigFailed,
+ else => return err,
+ };
+
+ var zig_args = ArrayList([]const u8).init(self.builder.allocator);
+ defer zig_args.deinit();
+
+ var it = mem.tokenize(u8, stdout, " \r\n\t");
+ while (it.next()) |tok| {
+ if (mem.eql(u8, tok, "-I")) {
+ const dir = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
+ } else if (mem.startsWith(u8, tok, "-I")) {
+ try zig_args.append(tok);
+ } else if (mem.eql(u8, tok, "-L")) {
+ const dir = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
+ } else if (mem.startsWith(u8, tok, "-L")) {
+ try zig_args.append(tok);
+ } else if (mem.eql(u8, tok, "-l")) {
+ const lib = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
+ } else if (mem.startsWith(u8, tok, "-l")) {
+ try zig_args.append(tok);
+ } else if (mem.eql(u8, tok, "-D")) {
+ const macro = it.next() orelse return error.PkgConfigInvalidOutput;
+ try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
+ } else if (mem.startsWith(u8, tok, "-D")) {
+ try zig_args.append(tok);
+ } else if (self.builder.verbose) {
+ log.warn("Ignoring pkg-config flag '{s}'", .{tok});
+ }
+ }
+
+ return zig_args.toOwnedSlice();
+}
+
+pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void {
+ self.linkSystemLibraryInner(name, .{});
+}
+
+pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void {
+ self.linkSystemLibraryInner(name, .{ .needed = true });
+}
+
+pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void {
+ self.linkSystemLibraryInner(name, .{ .weak = true });
+}
+
+fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
+ needed: bool = false,
+ weak: bool = false,
+}) void {
+ if (isLibCLibrary(name)) {
+ self.linkLibC();
+ return;
+ }
+ if (isLibCppLibrary(name)) {
+ self.linkLibCpp();
+ return;
+ }
+
+ self.link_objects.append(.{
+ .system_lib = .{
+ .name = self.builder.dupe(name),
+ .needed = opts.needed,
+ .weak = opts.weak,
+ .use_pkg_config = .yes,
+ },
+ }) catch unreachable;
+}
+
+pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
+ assert(self.kind == .@"test" or self.kind == .test_exe);
+ self.name_prefix = self.builder.dupe(text);
+}
+
+pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
+ assert(self.kind == .@"test" or self.kind == .test_exe);
+ self.filter = if (text) |t| self.builder.dupe(t) else null;
+}
+
+pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
+ assert(self.kind == .@"test" or self.kind == .test_exe);
+ self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
+}
+
+/// Handy when you have many C/C++ source files and want them all to have the same flags.
+pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
+ const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
+
+ const files_copy = self.builder.dupeStrings(files);
+ const flags_copy = self.builder.dupeStrings(flags);
+
+ c_source_files.* = .{
+ .files = files_copy,
+ .flags = flags_copy,
+ };
+ self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
+}
+
+pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void {
+ self.addCSourceFileSource(.{
+ .args = flags,
+ .source = .{ .path = file },
+ });
+}
+
+pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
+ const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
+ c_source_file.* = source.dupe(self.builder);
+ self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
+ source.source.addStepDependencies(&self.step);
+}
+
+pub fn setVerboseLink(self: *CompileStep, value: bool) void {
+ self.verbose_link = value;
+}
+
+pub fn setVerboseCC(self: *CompileStep, value: bool) void {
+ self.verbose_cc = value;
+}
+
+pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
+ self.override_lib_dir = self.builder.dupePath(dir_path);
+}
+
+pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
+ self.main_pkg_path = self.builder.dupePath(dir_path);
+}
+
+pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
+ self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
+}
+
+/// Returns the generated executable, library or object file.
+/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
+pub fn getOutputSource(self: *CompileStep) FileSource {
+ return FileSource{ .generated = &self.output_path_source };
+}
+
+/// Returns the generated import library. This function can only be called for libraries.
+pub fn getOutputLibSource(self: *CompileStep) FileSource {
+ assert(self.kind == .lib);
+ return FileSource{ .generated = &self.output_lib_path_source };
+}
+
+/// Returns the generated header file.
+/// This function can only be called for libraries or object files which have `emit_h` set.
+pub fn getOutputHSource(self: *CompileStep) FileSource {
+ assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
+ assert(self.emit_h);
+ return FileSource{ .generated = &self.output_h_path_source };
+}
+
+/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
+pub fn getOutputPdbSource(self: *CompileStep) FileSource {
+ // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
+ assert(self.target.isWindows() or self.target.isUefi());
+ return FileSource{ .generated = &self.output_pdb_path_source };
+}
+
+pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
+ self.link_objects.append(.{
+ .assembly_file = .{ .path = self.builder.dupe(path) },
+ }) catch unreachable;
+}
+
+pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
+ const source_duped = source.dupe(self.builder);
+ self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
+ source_duped.addStepDependencies(&self.step);
+}
+
+pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
+ self.addObjectFileSource(.{ .path = source_file });
+}
+
+pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
+ self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
+ source.addStepDependencies(&self.step);
+}
+
+pub fn addObject(self: *CompileStep, obj: *CompileStep) void {
+ assert(obj.kind == .obj);
+ self.linkLibraryOrObject(obj);
+}
+
+pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
+pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
+pub const addLibPath = @compileError("deprecated, use addLibraryPath");
+pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
+
+pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
+ self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
+}
+
+pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
+ self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
+}
+
+pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
+ self.step.dependOn(&config_header.step);
+ self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
+}
+
+pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
+ self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
+}
+
+pub fn addRPath(self: *CompileStep, path: []const u8) void {
+ self.rpaths.append(self.builder.dupe(path)) catch unreachable;
+}
+
+pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
+ self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
+}
+
+pub fn addPackage(self: *CompileStep, package: Pkg) void {
+ self.packages.append(self.builder.dupePkg(package)) catch unreachable;
+ self.addRecursiveBuildDeps(package);
+}
+
+pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void {
+ self.addPackage(options.getPackage(package_name));
+}
+
+fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void {
+ package.source.addStepDependencies(&self.step);
+ if (package.dependencies) |deps| {
+ for (deps) |dep| {
+ self.addRecursiveBuildDeps(dep);
+ }
+ }
+}
+
+pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void {
+ self.addPackage(Pkg{
+ .name = self.builder.dupe(name),
+ .source = .{ .path = self.builder.dupe(pkg_index_path) },
+ });
+}
+
+/// If Vcpkg was found on the system, it will be added to include and lib
+/// paths for the specified target.
+pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
+ // Ideally in the Unattempted case we would call the function recursively
+ // after findVcpkgRoot and have only one switch statement, but the compiler
+ // cannot resolve the error set.
+ switch (self.builder.vcpkg_root) {
+ .unattempted => {
+ self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
+ VcpkgRoot{ .found = root }
+ else
+ .not_found;
+ },
+ .not_found => return error.VcpkgNotFound,
+ .found => {},
+ }
+
+ switch (self.builder.vcpkg_root) {
+ .unattempted => unreachable,
+ .not_found => return error.VcpkgNotFound,
+ .found => |root| {
+ const allocator = self.builder.allocator;
+ const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
+ defer self.builder.allocator.free(triplet);
+
+ const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
+ errdefer allocator.free(include_path);
+ try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
+
+ const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
+ try self.lib_paths.append(lib_path);
+
+ self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
+ },
+ }
+}
+
+pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
+ assert(self.kind == .@"test");
+ const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
+ for (args) |arg, i| {
+ duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
+ }
+ self.exec_cmd_args = duped_args;
+}
+
+fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
+ self.step.dependOn(&other.step);
+ self.link_objects.append(.{ .other_step = other }) catch unreachable;
+ self.include_dirs.append(.{ .other_step = other }) catch unreachable;
+}
+
+fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
+ const builder = self.builder;
+
+ try zig_args.append("--pkg-begin");
+ try zig_args.append(pkg.name);
+ try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
+
+ if (pkg.dependencies) |dependencies| {
+ for (dependencies) |sub_pkg| {
+ try self.makePackageCmd(sub_pkg, zig_args);
+ }
+ }
+
+ try zig_args.append("--pkg-end");
+}
+
+fn make(step: *Step) !void {
+ const self = @fieldParentPtr(CompileStep, "step", step);
+ const builder = self.builder;
+
+ if (self.root_src == null and self.link_objects.items.len == 0) {
+ log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
+ return error.NeedAnObject;
+ }
+
+ var zig_args = ArrayList([]const u8).init(builder.allocator);
+ defer zig_args.deinit();
+
+ zig_args.append(builder.zig_exe) catch unreachable;
+
+ const cmd = switch (self.kind) {
+ .lib => "build-lib",
+ .exe => "build-exe",
+ .obj => "build-obj",
+ .@"test" => "test",
+ .test_exe => "test",
+ };
+ zig_args.append(cmd) catch unreachable;
+
+ if (builder.color != .auto) {
+ try zig_args.append("--color");
+ try zig_args.append(@tagName(builder.color));
+ }
+
+ if (builder.reference_trace) |some| {
+ try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
+ }
+
+ try addFlag(&zig_args, "LLVM", self.use_llvm);
+ try addFlag(&zig_args, "LLD", self.use_lld);
+
+ if (self.target.ofmt) |ofmt| {
+ try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
+ }
+
+ if (self.entry_symbol_name) |entry| {
+ try zig_args.append("--entry");
+ try zig_args.append(entry);
+ }
+
+ if (self.stack_size) |stack_size| {
+ try zig_args.append("--stack");
+ try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
+ }
+
+ if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
+
+ // We will add link objects from transitive dependencies, but we want to keep
+ // all link objects in the same order provided.
+ // This array is used to keep self.link_objects immutable.
+ var transitive_deps: TransitiveDeps = .{
+ .link_objects = ArrayList(LinkObject).init(builder.allocator),
+ .seen_system_libs = StringHashMap(void).init(builder.allocator),
+ .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
+ .is_linking_libcpp = self.is_linking_libcpp,
+ .is_linking_libc = self.is_linking_libc,
+ .frameworks = &self.frameworks,
+ };
+
+ try transitive_deps.seen_steps.put(&self.step, {});
+ try transitive_deps.add(self.link_objects.items);
+
+ var prev_has_extra_flags = false;
+
+ for (transitive_deps.link_objects.items) |link_object| {
+ switch (link_object) {
+ .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
+
+ .other_step => |other| switch (other.kind) {
+ .exe => @panic("Cannot link with an executable build artifact"),
+ .test_exe => @panic("Cannot link with an executable build artifact"),
+ .@"test" => @panic("Cannot link with a test"),
+ .obj => {
+ try zig_args.append(other.getOutputSource().getPath(builder));
+ },
+ .lib => l: {
+ if (self.isStaticLibrary() and other.isStaticLibrary()) {
+ // Avoid putting a static library inside a static library.
+ break :l;
+ }
+
+ const full_path_lib = other.getOutputLibSource().getPath(builder);
+ try zig_args.append(full_path_lib);
+
+ if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
+ if (fs.path.dirname(full_path_lib)) |dirname| {
+ try zig_args.append("-rpath");
+ try zig_args.append(dirname);
+ }
+ }
+ },
+ },
+
+ .system_lib => |system_lib| {
+ const prefix: []const u8 = prefix: {
+ if (system_lib.needed) break :prefix "-needed-l";
+ if (system_lib.weak) {
+ if (self.target.isDarwin()) break :prefix "-weak-l";
+ log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
+ }
+ break :prefix "-l";
+ };
+ switch (system_lib.use_pkg_config) {
+ .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
+ .yes, .force => {
+ if (self.runPkgConfig(system_lib.name)) |args| {
+ try zig_args.appendSlice(args);
+ } else |err| switch (err) {
+ error.PkgConfigInvalidOutput,
+ error.PkgConfigCrashed,
+ error.PkgConfigFailed,
+ error.PkgConfigNotInstalled,
+ error.PackageNotFound,
+ => switch (system_lib.use_pkg_config) {
+ .yes => {
+ // pkg-config failed, so fall back to linking the library
+ // by name directly.
+ try zig_args.append(builder.fmt("{s}{s}", .{
+ prefix,
+ system_lib.name,
+ }));
+ },
+ .force => {
+ panic("pkg-config failed for library {s}", .{system_lib.name});
+ },
+ .no => unreachable,
+ },
+
+ else => |e| return e,
+ }
+ },
+ }
+ },
+
+ .assembly_file => |asm_file| {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-extra-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ try zig_args.append(asm_file.getPath(builder));
+ },
+
+ .c_source_file => |c_source_file| {
+ if (c_source_file.args.len == 0) {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ } else {
+ try zig_args.append("-cflags");
+ for (c_source_file.args) |arg| {
+ try zig_args.append(arg);
+ }
+ try zig_args.append("--");
+ }
+ try zig_args.append(c_source_file.source.getPath(builder));
+ },
+
+ .c_source_files => |c_source_files| {
+ if (c_source_files.flags.len == 0) {
+ if (prev_has_extra_flags) {
+ try zig_args.append("-cflags");
+ try zig_args.append("--");
+ prev_has_extra_flags = false;
+ }
+ } else {
+ try zig_args.append("-cflags");
+ for (c_source_files.flags) |flag| {
+ try zig_args.append(flag);
+ }
+ try zig_args.append("--");
+ }
+ for (c_source_files.files) |file| {
+ try zig_args.append(builder.pathFromRoot(file));
+ }
+ },
+ }
+ }
+
+ if (transitive_deps.is_linking_libcpp) {
+ try zig_args.append("-lc++");
+ }
+
+ if (transitive_deps.is_linking_libc) {
+ try zig_args.append("-lc");
+ }
+
+ if (self.image_base) |image_base| {
+ try zig_args.append("--image-base");
+ try zig_args.append(builder.fmt("0x{x}", .{image_base}));
+ }
+
+ if (self.filter) |filter| {
+ try zig_args.append("--test-filter");
+ try zig_args.append(filter);
+ }
+
+ if (self.test_evented_io) {
+ try zig_args.append("--test-evented-io");
+ }
+
+ if (self.name_prefix.len != 0) {
+ try zig_args.append("--test-name-prefix");
+ try zig_args.append(self.name_prefix);
+ }
+
+ if (self.test_runner) |test_runner| {
+ try zig_args.append("--test-runner");
+ try zig_args.append(builder.pathFromRoot(test_runner));
+ }
+
+ for (builder.debug_log_scopes) |log_scope| {
+ try zig_args.append("--debug-log");
+ try zig_args.append(log_scope);
+ }
+
+ if (builder.debug_compile_errors) {
+ try zig_args.append("--debug-compile-errors");
+ }
+
+ if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
+ if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
+ if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
+ if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
+ if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
+ if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
+
+ if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
+ if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
+ if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
+ if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
+ if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
+ if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
+ if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
+
+ if (self.emit_h) try zig_args.append("-femit-h");
+
+ try addFlag(&zig_args, "strip", self.strip);
+ try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
+
+ switch (self.compress_debug_sections) {
+ .none => {},
+ .zlib => try zig_args.append("--compress-debug-sections=zlib"),
+ }
+
+ if (self.link_eh_frame_hdr) {
+ try zig_args.append("--eh-frame-hdr");
+ }
+ if (self.link_emit_relocs) {
+ try zig_args.append("--emit-relocs");
+ }
+ if (self.link_function_sections) {
+ try zig_args.append("-ffunction-sections");
+ }
+ if (self.link_gc_sections) |x| {
+ try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
+ }
+ if (self.linker_allow_shlib_undefined) |x| {
+ try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
+ }
+ if (self.link_z_notext) {
+ try zig_args.append("-z");
+ try zig_args.append("notext");
+ }
+ if (!self.link_z_relro) {
+ try zig_args.append("-z");
+ try zig_args.append("norelro");
+ }
+ if (self.link_z_lazy) {
+ try zig_args.append("-z");
+ try zig_args.append("lazy");
+ }
+ if (self.link_z_common_page_size) |size| {
+ try zig_args.append("-z");
+ try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
+ }
+ if (self.link_z_max_page_size) |size| {
+ try zig_args.append("-z");
+ try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
+ }
+
+ if (self.libc_file) |libc_file| {
+ try zig_args.append("--libc");
+ try zig_args.append(libc_file.getPath(builder));
+ } else if (builder.libc_file) |libc_file| {
+ try zig_args.append("--libc");
+ try zig_args.append(libc_file);
+ }
+
+ switch (self.optimize) {
+ .Debug => {}, // Skip since it's the default.
+ else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable,
+ }
+
+ try zig_args.append("--cache-dir");
+ try zig_args.append(builder.pathFromRoot(builder.cache_root));
+
+ try zig_args.append("--global-cache-dir");
+ try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
+
+ zig_args.append("--name") catch unreachable;
+ zig_args.append(self.name) catch unreachable;
+
+ if (self.linkage) |some| switch (some) {
+ .dynamic => try zig_args.append("-dynamic"),
+ .static => try zig_args.append("-static"),
+ };
+ if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
+ if (self.version) |version| {
+ zig_args.append("--version") catch unreachable;
+ zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
+ }
+
+ if (self.target.isDarwin()) {
+ const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
+ self.target.libPrefix(),
+ self.name,
+ self.target.dynamicLibSuffix(),
+ });
+ try zig_args.append("-install_name");
+ try zig_args.append(install_name);
+ }
+ }
+
+ if (self.entitlements) |entitlements| {
+ try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
+ }
+ if (self.pagezero_size) |pagezero_size| {
+ const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
+ try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
+ }
+ if (self.search_strategy) |strat| switch (strat) {
+ .paths_first => try zig_args.append("-search_paths_first"),
+ .dylibs_first => try zig_args.append("-search_dylibs_first"),
+ };
+ if (self.headerpad_size) |headerpad_size| {
+ const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
+ try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
+ }
+ if (self.headerpad_max_install_names) {
+ try zig_args.append("-headerpad_max_install_names");
+ }
+ if (self.dead_strip_dylibs) {
+ try zig_args.append("-dead_strip_dylibs");
+ }
+
+ try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
+ try addFlag(&zig_args, "single-threaded", self.single_threaded);
+ if (self.disable_stack_probing) {
+ try zig_args.append("-fno-stack-check");
+ }
+ try addFlag(&zig_args, "stack-protector", self.stack_protector);
+ if (self.red_zone) |red_zone| {
+ if (red_zone) {
+ try zig_args.append("-mred-zone");
+ } else {
+ try zig_args.append("-mno-red-zone");
+ }
+ }
+ try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
+ try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
+
+ if (self.disable_sanitize_c) {
+ try zig_args.append("-fno-sanitize-c");
+ }
+ if (self.sanitize_thread) {
+ try zig_args.append("-fsanitize-thread");
+ }
+ if (self.rdynamic) {
+ try zig_args.append("-rdynamic");
+ }
+ if (self.import_memory) {
+ try zig_args.append("--import-memory");
+ }
+ if (self.import_symbols) {
+ try zig_args.append("--import-symbols");
+ }
+ if (self.import_table) {
+ try zig_args.append("--import-table");
+ }
+ if (self.export_table) {
+ try zig_args.append("--export-table");
+ }
+ if (self.initial_memory) |initial_memory| {
+ try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
+ }
+ if (self.max_memory) |max_memory| {
+ try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
+ }
+ if (self.shared_memory) {
+ try zig_args.append("--shared-memory");
+ }
+ if (self.global_base) |global_base| {
+ try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
+ }
+
+ if (self.code_model != .default) {
+ try zig_args.append("-mcmodel");
+ try zig_args.append(@tagName(self.code_model));
+ }
+ if (self.wasi_exec_model) |model| {
+ try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
+ }
+ for (self.export_symbol_names) |symbol_name| {
+ try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
+ }
+
+ if (!self.target.isNative()) {
+ try zig_args.appendSlice(&.{
+ "-target", try self.target.zigTriple(builder.allocator),
+ "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
+ });
+
+ if (self.target.dynamic_linker.get()) |dynamic_linker| {
+ try zig_args.append("--dynamic-linker");
+ try zig_args.append(dynamic_linker);
+ }
+ }
+
+ if (self.linker_script) |linker_script| {
+ try zig_args.append("--script");
+ try zig_args.append(linker_script.getPath(builder));
+ }
+
+ if (self.version_script) |version_script| {
+ try zig_args.append("--version-script");
+ try zig_args.append(builder.pathFromRoot(version_script));
+ }
+
+ if (self.kind == .@"test") {
+ if (self.exec_cmd_args) |exec_cmd_args| {
+ for (exec_cmd_args) |cmd_arg| {
+ if (cmd_arg) |arg| {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(arg);
+ } else {
+ try zig_args.append("--test-cmd-bin");
+ }
+ }
+ } else {
+ const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
+
+ switch (builder.host.getExternalExecutor(self.target_info, .{
+ .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
+ .link_libc = transitive_deps.is_linking_libc,
+ })) {
+ .native => {},
+ .bad_dl, .bad_os_or_cpu => {
+ try zig_args.append("--test-no-exec");
+ },
+ .rosetta => if (builder.enable_rosetta) {
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ .qemu => |bin_name| ok: {
+ if (builder.enable_qemu) qemu: {
+ const glibc_dir_arg = if (need_cross_glibc)
+ builder.glibc_runtimes_dir orelse break :qemu
+ else
+ null;
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ if (glibc_dir_arg) |dir| {
+ // TODO look into making this a call to `linuxTriple`. This
+ // needs the directory to be called "i686" rather than
+ // "x86" which is why we do it manually here.
+ const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
+ const cpu_arch = self.target.getCpuArch();
+ const os_tag = self.target.getOsTag();
+ const abi = self.target.getAbi();
+ const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
+ "i686"
+ else
+ @tagName(cpu_arch);
+ const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
+ dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
+ });
+
+ try zig_args.append("--test-cmd");
+ try zig_args.append("-L");
+ try zig_args.append("--test-cmd");
+ try zig_args.append(full_dir);
+ }
+ try zig_args.append("--test-cmd-bin");
+ break :ok;
+ }
+ try zig_args.append("--test-no-exec");
+ },
+ .wine => |bin_name| if (builder.enable_wine) {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ .wasmtime => |bin_name| if (builder.enable_wasmtime) {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ try zig_args.append("--test-cmd");
+ try zig_args.append("--dir=.");
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ .darling => |bin_name| if (builder.enable_darling) {
+ try zig_args.append("--test-cmd");
+ try zig_args.append(bin_name);
+ try zig_args.append("--test-cmd-bin");
+ } else {
+ try zig_args.append("--test-no-exec");
+ },
+ }
+ }
+ } else if (self.kind == .test_exe) {
+ try zig_args.append("--test-no-exec");
+ }
+
+ for (self.packages.items) |pkg| {
+ try self.makePackageCmd(pkg, &zig_args);
+ }
+
+ for (self.include_dirs.items) |include_dir| {
+ switch (include_dir) {
+ .raw_path => |include_path| {
+ try zig_args.append("-I");
+ try zig_args.append(builder.pathFromRoot(include_path));
+ },
+ .raw_path_system => |include_path| {
+ if (builder.sysroot != null) {
+ try zig_args.append("-iwithsysroot");
+ } else {
+ try zig_args.append("-isystem");
+ }
+
+ const resolved_include_path = builder.pathFromRoot(include_path);
+
+ const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
+ // We need to check for disk designator and strip it out from dir path so
+ // that zig/clang can concat resolved_include_path with sysroot.
+ const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
+
+ if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
+ break :blk resolved_include_path[where + disk_designator.len ..];
+ }
+
+ break :blk resolved_include_path;
+ } else resolved_include_path;
+
+ try zig_args.append(common_include_path);
+ },
+ .other_step => |other| {
+ if (other.emit_h) {
+ const h_path = other.getOutputHSource().getPath(builder);
+ try zig_args.append("-isystem");
+ try zig_args.append(fs.path.dirname(h_path).?);
+ }
+ if (other.installed_headers.items.len > 0) {
+ for (other.installed_headers.items) |install_step| {
+ try install_step.make();
+ }
+ try zig_args.append("-I");
+ try zig_args.append(builder.pathJoin(&.{
+ other.builder.install_prefix, "include",
+ }));
+ }
+ },
+ .config_header_step => |config_header| {
+ try zig_args.append("-I");
+ try zig_args.append(config_header.output_dir);
+ },
+ }
+ }
+
+ for (self.lib_paths.items) |lib_path| {
+ try zig_args.append("-L");
+ try zig_args.append(lib_path);
+ }
+
+ for (self.rpaths.items) |rpath| {
+ try zig_args.append("-rpath");
+ try zig_args.append(rpath);
+ }
+
+ for (self.c_macros.items) |c_macro| {
+ try zig_args.append("-D");
+ try zig_args.append(c_macro);
+ }
+
+ if (self.target.isDarwin()) {
+ for (self.framework_dirs.items) |dir| {
+ if (builder.sysroot != null) {
+ try zig_args.append("-iframeworkwithsysroot");
+ } else {
+ try zig_args.append("-iframework");
+ }
+ try zig_args.append(dir);
+ try zig_args.append("-F");
+ try zig_args.append(dir);
+ }
+
+ var it = self.frameworks.iterator();
+ while (it.next()) |entry| {
+ const name = entry.key_ptr.*;
+ const info = entry.value_ptr.*;
+ if (info.needed) {
+ zig_args.append("-needed_framework") catch unreachable;
+ } else if (info.weak) {
+ zig_args.append("-weak_framework") catch unreachable;
+ } else {
+ zig_args.append("-framework") catch unreachable;
+ }
+ zig_args.append(name) catch unreachable;
+ }
+ } else {
+ if (self.framework_dirs.items.len > 0) {
+ log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
+ }
+
+ if (self.frameworks.count() > 0) {
+ log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
+ }
+ }
+
+ if (builder.sysroot) |sysroot| {
+ try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
+ }
+
+ for (builder.search_prefixes.items) |search_prefix| {
+ try zig_args.append("-L");
+ try zig_args.append(builder.pathJoin(&.{
+ search_prefix, "lib",
+ }));
+ try zig_args.append("-I");
+ try zig_args.append(builder.pathJoin(&.{
+ search_prefix, "include",
+ }));
+ }
+
+ try addFlag(&zig_args, "valgrind", self.valgrind_support);
+ try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
+ try addFlag(&zig_args, "build-id", self.build_id);
+
+ if (self.override_lib_dir) |dir| {
+ try zig_args.append("--zig-lib-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ } else if (builder.override_lib_dir) |dir| {
+ try zig_args.append("--zig-lib-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
+
+ if (self.main_pkg_path) |dir| {
+ try zig_args.append("--main-pkg-path");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
+
+ try addFlag(&zig_args, "PIC", self.force_pic);
+ try addFlag(&zig_args, "PIE", self.pie);
+ try addFlag(&zig_args, "lto", self.want_lto);
+
+ if (self.subsystem) |subsystem| {
+ try zig_args.append("--subsystem");
+ try zig_args.append(switch (subsystem) {
+ .Console => "console",
+ .Windows => "windows",
+ .Posix => "posix",
+ .Native => "native",
+ .EfiApplication => "efi_application",
+ .EfiBootServiceDriver => "efi_boot_service_driver",
+ .EfiRom => "efi_rom",
+ .EfiRuntimeDriver => "efi_runtime_driver",
+ });
+ }
+
+ try zig_args.append("--enable-cache");
+
+ // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
+ // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
+ // pass that to zig, e.g. via 'zig build-lib @args.rsp'
+ // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
+ var args_length: usize = 0;
+ for (zig_args.items) |arg| {
+ args_length += arg.len + 1; // +1 to account for null terminator
+ }
+ if (args_length >= 30 * 1024) {
+ const args_dir = try fs.path.join(
+ builder.allocator,
+ &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
+ );
+ try std.fs.cwd().makePath(args_dir);
+
+ var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
+ defer args_arena.deinit();
+
+ const args_to_escape = zig_args.items[2..];
+ var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
+
+ arg_blk: for (args_to_escape) |arg| {
+ for (arg) |c, arg_idx| {
+ if (c == '\\' or c == '"') {
+ // Slow path for arguments that need to be escaped. We'll need to allocate and copy
+ var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
+ const writer = escaped.writer();
+ writer.writeAll(arg[0..arg_idx]) catch unreachable;
+ for (arg[arg_idx..]) |to_escape| {
+ if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
+ try writer.writeByte(to_escape);
+ }
+ escaped_args.appendAssumeCapacity(escaped.items);
+ continue :arg_blk;
+ }
+ }
+ escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
+ }
+
+ // Write the args to zig-cache/args/ to avoid conflicts with
+ // other zig build commands running in parallel.
+ const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
+ const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
+
+ var args_hash: [Sha256.digest_length]u8 = undefined;
+ Sha256.hash(args, &args_hash, .{});
+ var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
+ _ = try std.fmt.bufPrint(
+ &args_hex_hash,
+ "{s}",
+ .{std.fmt.fmtSliceHexLower(&args_hash)},
+ );
+
+ const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
+ try std.fs.cwd().writeFile(args_file, args);
+
+ zig_args.shrinkRetainingCapacity(2);
+ try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
+ }
+
+ const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
+ const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
+
+ if (self.output_dir) |output_dir| {
+ var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
+ defer src_dir.close();
+
+ // Create the output directory if it doesn't exist.
+ try std.fs.cwd().makePath(output_dir);
+
+ var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
+ defer dest_dir.close();
+
+ var it = src_dir.iterate();
+ while (try it.next()) |entry| {
+ // The compiler can put these files into the same directory, but we don't
+ // want to copy them over.
+ if (mem.eql(u8, entry.name, "llvm-ar.id") or
+ mem.eql(u8, entry.name, "libs.txt") or
+ mem.eql(u8, entry.name, "builtin.zig") or
+ mem.eql(u8, entry.name, "zld.id") or
+ mem.eql(u8, entry.name, "lld.id")) continue;
+
+ _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
+ }
+ } else {
+ self.output_dir = build_output_dir;
+ }
+
+ // This will ensure all output filenames will now have the output_dir available!
+ self.computeOutFileNames();
+
+ // Update generated files
+ if (self.output_dir != null) {
+ self.output_path_source.path = builder.pathJoin(
+ &.{ self.output_dir.?, self.out_filename },
+ );
+
+ if (self.emit_h) {
+ self.output_h_path_source.path = builder.pathJoin(
+ &.{ self.output_dir.?, self.out_h_filename },
+ );
+ }
+
+ if (self.target.isWindows() or self.target.isUefi()) {
+ self.output_pdb_path_source.path = builder.pathJoin(
+ &.{ self.output_dir.?, self.out_pdb_filename },
+ );
+ }
+ }
+
+ if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
+ try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
+ }
+}
+
+fn isLibCLibrary(name: []const u8) bool {
+ const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
+ for (libc_libraries) |libc_lib_name| {
+ if (mem.eql(u8, name, libc_lib_name))
+ return true;
+ }
+ return false;
+}
+
+fn isLibCppLibrary(name: []const u8) bool {
+ const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
+ for (libcpp_libraries) |libcpp_lib_name| {
+ if (mem.eql(u8, name, libcpp_lib_name))
+ return true;
+ }
+ return false;
+}
+
+/// Returned slice must be freed by the caller.
+fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
+ const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
+ defer allocator.free(appdata_path);
+
+ const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
+ defer allocator.free(path_file);
+
+ const file = fs.cwd().openFile(path_file, .{}) catch return null;
+ defer file.close();
+
+ const size = @intCast(usize, try file.getEndPos());
+ const vcpkg_path = try allocator.alloc(u8, size);
+ const size_read = try file.read(vcpkg_path);
+ std.debug.assert(size == size_read);
+
+ return vcpkg_path;
+}
+
+pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
+ const out_dir = fs.path.dirname(output_path) orelse ".";
+ const out_basename = fs.path.basename(output_path);
+ // sym link for libfoo.so.1 to libfoo.so.1.2.3
+ const major_only_path = fs.path.join(
+ allocator,
+ &[_][]const u8{ out_dir, filename_major_only },
+ ) catch unreachable;
+ fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
+ log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
+ return err;
+ };
+ // sym link for libfoo.so to libfoo.so.1
+ const name_only_path = fs.path.join(
+ allocator,
+ &[_][]const u8{ out_dir, filename_name_only },
+ ) catch unreachable;
+ fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
+ log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
+ return err;
+ };
+}
+
+fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
+ const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
+ var list = ArrayList(PkgConfigPkg).init(self.allocator);
+ errdefer list.deinit();
+ var line_it = mem.tokenize(u8, stdout, "\r\n");
+ while (line_it.next()) |line| {
+ if (mem.trim(u8, line, " \t").len == 0) continue;
+ var tok_it = mem.tokenize(u8, line, " \t");
+ try list.append(PkgConfigPkg{
+ .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
+ .desc = tok_it.rest(),
+ });
+ }
+ return list.toOwnedSlice();
+}
+
+fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
+ if (self.pkg_config_pkg_list) |res| {
+ return res;
+ }
+ var code: u8 = undefined;
+ if (execPkgConfigList(self, &code)) |list| {
+ self.pkg_config_pkg_list = list;
+ return list;
+ } else |err| {
+ const result = switch (err) {
+ error.ProcessTerminated => error.PkgConfigCrashed,
+ error.ExecNotSupported => error.PkgConfigFailed,
+ error.ExitCodeFailure => error.PkgConfigFailed,
+ error.FileNotFound => error.PkgConfigNotInstalled,
+ error.InvalidName => error.PkgConfigNotInstalled,
+ error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
+ error.ChildExecFailed => error.PkgConfigFailed,
+ else => return err,
+ };
+ self.pkg_config_pkg_list = result;
+ return result;
+ }
+}
+
+test "addPackage" {
+ if (builtin.os.tag == .wasi) return error.SkipZigTest;
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+
+ const host = try NativeTargetInfo.detect(.{});
+
+ var builder = try std.Build.create(
+ arena.allocator(),
+ "test",
+ "test",
+ "test",
+ "test",
+ host,
+ );
+ defer builder.destroy();
+
+ const pkg_dep = Pkg{
+ .name = "pkg_dep",
+ .source = .{ .path = "/not/a/pkg_dep.zig" },
+ };
+ const pkg_top = Pkg{
+ .name = "pkg_dep",
+ .source = .{ .path = "/not/a/pkg_top.zig" },
+ .dependencies = &[_]Pkg{pkg_dep},
+ };
+
+ var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
+ exe.addPackage(pkg_top);
+
+ try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
+
+ const dupe = exe.packages.items[0];
+ try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
+}
+
+fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
+ const cond = opt orelse return;
+ try args.ensureUnusedCapacity(1);
+ if (cond) {
+ args.appendAssumeCapacity("-f" ++ name);
+ } else {
+ args.appendAssumeCapacity("-fno-" ++ name);
+ }
+}
+
+const TransitiveDeps = struct {
+ link_objects: ArrayList(LinkObject),
+ seen_system_libs: StringHashMap(void),
+ seen_steps: std.AutoHashMap(*const Step, void),
+ is_linking_libcpp: bool,
+ is_linking_libc: bool,
+ frameworks: *StringHashMap(FrameworkLinkInfo),
+
+ fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
+ try td.link_objects.ensureUnusedCapacity(link_objects.len);
+
+ for (link_objects) |link_object| {
+ try td.link_objects.append(link_object);
+ switch (link_object) {
+ .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
+ else => {},
+ }
+ }
+ }
+
+ fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void {
+ // Inherit dependency on libc and libc++
+ td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
+ td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
+
+ // Inherit dependencies on darwin frameworks
+ if (!dyn) {
+ var it = other.frameworks.iterator();
+ while (it.next()) |framework| {
+ try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
+ }
+ }
+
+ // Inherit dependencies on system libraries and static libraries.
+ for (other.link_objects.items) |other_link_object| {
+ switch (other_link_object) {
+ .system_lib => |system_lib| {
+ if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
+ continue;
+
+ if (dyn)
+ continue;
+
+ try td.link_objects.append(other_link_object);
+ },
+ .other_step => |inner_other| {
+ if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
+ continue;
+
+ if (!dyn)
+ try td.link_objects.append(other_link_object);
+
+ try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
+ },
+ else => continue,
+ }
+ }
+ }
+};
diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig
index b7b12d791f..26804b2fc8 100644
--- a/lib/std/Build/EmulatableRunStep.zig
+++ b/lib/std/Build/EmulatableRunStep.zig
@@ -6,7 +6,7 @@
const std = @import("../std.zig");
const Step = std.Build.Step;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const RunStep = std.Build.RunStep;
const fs = std.fs;
@@ -23,7 +23,7 @@ step: Step,
builder: *std.Build,
/// The artifact (executable) to be run by this step
-exe: *LibExeObjStep,
+exe: *CompileStep,
/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
expected_exit_code: ?u8 = 0,
@@ -45,7 +45,7 @@ hide_foreign_binaries_warning: bool,
/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
/// When set to false, and the binary is foreign, running the executable is skipped.
/// Asserts given artifact is an executable.
-pub fn create(builder: *std.Build, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep {
+pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig
index 929b30e935..898b9e85e7 100644
--- a/lib/std/Build/InstallArtifactStep.zig
+++ b/lib/std/Build/InstallArtifactStep.zig
@@ -1,6 +1,6 @@
const std = @import("../std.zig");
const Step = std.Build.Step;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const InstallDir = std.Build.InstallDir;
const InstallArtifactStep = @This();
@@ -8,12 +8,12 @@ pub const base_id = .install_artifact;
step: Step,
builder: *std.Build,
-artifact: *LibExeObjStep,
+artifact: *CompileStep,
dest_dir: InstallDir,
pdb_dir: ?InstallDir,
h_dir: ?InstallDir,
-pub fn create(builder: *std.Build, artifact: *LibExeObjStep) *InstallArtifactStep {
+pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
if (artifact.install_step) |s| return s;
const self = builder.allocator.create(InstallArtifactStep) catch unreachable;
@@ -67,7 +67,7 @@ fn make(step: *Step) !void {
const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
- try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
+ try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
}
if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
diff --git a/lib/std/Build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig
index 08d646ff88..e7ffe78878 100644
--- a/lib/std/Build/InstallRawStep.zig
+++ b/lib/std/Build/InstallRawStep.zig
@@ -9,7 +9,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
const File = std.fs.File;
const InstallDir = std.Build.InstallDir;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const Step = std.Build.Step;
const elf = std.elf;
const fs = std.fs;
@@ -25,7 +25,7 @@ pub const RawFormat = enum {
step: Step,
builder: *std.Build,
-artifact: *LibExeObjStep,
+artifact: *CompileStep,
dest_dir: InstallDir,
dest_filename: []const u8,
options: CreateOptions,
@@ -40,7 +40,7 @@ pub const CreateOptions = struct {
pub fn create(
builder: *std.Build,
- artifact: *LibExeObjStep,
+ artifact: *CompileStep,
dest_filename: []const u8,
options: CreateOptions,
) *InstallRawStep {
diff --git a/lib/std/Build/LibExeObjStep.zig b/lib/std/Build/LibExeObjStep.zig
deleted file mode 100644
index 67f42c1783..0000000000
--- a/lib/std/Build/LibExeObjStep.zig
+++ /dev/null
@@ -1,2045 +0,0 @@
-const builtin = @import("builtin");
-const std = @import("../std.zig");
-const mem = std.mem;
-const log = std.log;
-const fs = std.fs;
-const assert = std.debug.assert;
-const panic = std.debug.panic;
-const ArrayList = std.ArrayList;
-const StringHashMap = std.StringHashMap;
-const Sha256 = std.crypto.hash.sha2.Sha256;
-const Allocator = mem.Allocator;
-const Step = std.Build.Step;
-const CrossTarget = std.zig.CrossTarget;
-const NativeTargetInfo = std.zig.system.NativeTargetInfo;
-const FileSource = std.Build.FileSource;
-const PkgConfigPkg = std.Build.PkgConfigPkg;
-const PkgConfigError = std.Build.PkgConfigError;
-const ExecError = std.Build.ExecError;
-const Pkg = std.Build.Pkg;
-const VcpkgRoot = std.Build.VcpkgRoot;
-const InstallDir = std.Build.InstallDir;
-const InstallArtifactStep = std.Build.InstallArtifactStep;
-const GeneratedFile = std.Build.GeneratedFile;
-const InstallRawStep = std.Build.InstallRawStep;
-const EmulatableRunStep = std.Build.EmulatableRunStep;
-const CheckObjectStep = std.Build.CheckObjectStep;
-const RunStep = std.Build.RunStep;
-const OptionsStep = std.Build.OptionsStep;
-const ConfigHeaderStep = std.Build.ConfigHeaderStep;
-const LibExeObjStep = @This();
-
-pub const base_id = .lib_exe_obj;
-
-step: Step,
-builder: *std.Build,
-name: []const u8,
-target: CrossTarget,
-target_info: NativeTargetInfo,
-optimize: std.builtin.Mode,
-linker_script: ?FileSource = null,
-version_script: ?[]const u8 = null,
-out_filename: []const u8,
-linkage: ?Linkage = null,
-version: ?std.builtin.Version,
-kind: Kind,
-major_only_filename: ?[]const u8,
-name_only_filename: ?[]const u8,
-strip: ?bool,
-unwind_tables: ?bool,
-// keep in sync with src/link.zig:CompressDebugSections
-compress_debug_sections: enum { none, zlib } = .none,
-lib_paths: ArrayList([]const u8),
-rpaths: ArrayList([]const u8),
-framework_dirs: ArrayList([]const u8),
-frameworks: StringHashMap(FrameworkLinkInfo),
-verbose_link: bool,
-verbose_cc: bool,
-emit_analysis: EmitOption = .default,
-emit_asm: EmitOption = .default,
-emit_bin: EmitOption = .default,
-emit_docs: EmitOption = .default,
-emit_implib: EmitOption = .default,
-emit_llvm_bc: EmitOption = .default,
-emit_llvm_ir: EmitOption = .default,
-// Lots of things depend on emit_h having a consistent path,
-// so it is not an EmitOption for now.
-emit_h: bool = false,
-bundle_compiler_rt: ?bool = null,
-single_threaded: ?bool = null,
-stack_protector: ?bool = null,
-disable_stack_probing: bool,
-disable_sanitize_c: bool,
-sanitize_thread: bool,
-rdynamic: bool,
-import_memory: bool = false,
-/// For WebAssembly targets, this will allow for undefined symbols to
-/// be imported from the host environment.
-import_symbols: bool = false,
-import_table: bool = false,
-export_table: bool = false,
-initial_memory: ?u64 = null,
-max_memory: ?u64 = null,
-shared_memory: bool = false,
-global_base: ?u64 = null,
-c_std: std.Build.CStd,
-override_lib_dir: ?[]const u8,
-main_pkg_path: ?[]const u8,
-exec_cmd_args: ?[]const ?[]const u8,
-name_prefix: []const u8,
-filter: ?[]const u8,
-test_evented_io: bool = false,
-test_runner: ?[]const u8,
-code_model: std.builtin.CodeModel = .default,
-wasi_exec_model: ?std.builtin.WasiExecModel = null,
-/// Symbols to be exported when compiling to wasm
-export_symbol_names: []const []const u8 = &.{},
-
-root_src: ?FileSource,
-out_h_filename: []const u8,
-out_lib_filename: []const u8,
-out_pdb_filename: []const u8,
-packages: ArrayList(Pkg),
-
-object_src: []const u8,
-
-link_objects: ArrayList(LinkObject),
-include_dirs: ArrayList(IncludeDir),
-c_macros: ArrayList([]const u8),
-installed_headers: ArrayList(*Step),
-output_dir: ?[]const u8,
-is_linking_libc: bool = false,
-is_linking_libcpp: bool = false,
-vcpkg_bin_path: ?[]const u8 = null,
-
-/// This may be set in order to override the default install directory
-override_dest_dir: ?InstallDir,
-installed_path: ?[]const u8,
-install_step: ?*InstallArtifactStep,
-
-/// Base address for an executable image.
-image_base: ?u64 = null,
-
-libc_file: ?FileSource = null,
-
-valgrind_support: ?bool = null,
-each_lib_rpath: ?bool = null,
-/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
-/// which can be used to coordinate a stripped binary with its debug symbols.
-/// As an example, the bloaty project refuses to work unless its inputs have
-/// build ids, in order to prevent accidental mismatches.
-/// The default is to not include this section because it slows down linking.
-build_id: ?bool = null,
-
-/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
-/// file.
-link_eh_frame_hdr: bool = false,
-link_emit_relocs: bool = false,
-
-/// Place every function in its own section so that unused ones may be
-/// safely garbage-collected during the linking phase.
-link_function_sections: bool = false,
-
-/// Remove functions and data that are unreachable by the entry point or
-/// exported symbols.
-link_gc_sections: ?bool = null,
-
-linker_allow_shlib_undefined: ?bool = null,
-
-/// Permit read-only relocations in read-only segments. Disallowed by default.
-link_z_notext: bool = false,
-
-/// Force all relocations to be read-only after processing.
-link_z_relro: bool = true,
-
-/// Allow relocations to be lazily processed after load.
-link_z_lazy: bool = false,
-
-/// Common page size
-link_z_common_page_size: ?u64 = null,
-
-/// Maximum page size
-link_z_max_page_size: ?u64 = null,
-
-/// (Darwin) Install name for the dylib
-install_name: ?[]const u8 = null,
-
-/// (Darwin) Path to entitlements file
-entitlements: ?[]const u8 = null,
-
-/// (Darwin) Size of the pagezero segment.
-pagezero_size: ?u64 = null,
-
-/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
-/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
-/// option.
-/// By default, if no option is specified, the linker assumes `paths_first` as the default
-/// search strategy.
-search_strategy: ?enum { paths_first, dylibs_first } = null,
-
-/// (Darwin) Set size of the padding between the end of load commands
-/// and start of `__TEXT,__text` section.
-headerpad_size: ?u32 = null,
-
-/// (Darwin) Automatically Set size of the padding between the end of load commands
-/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
-headerpad_max_install_names: bool = false,
-
-/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
-dead_strip_dylibs: bool = false,
-
-/// Position Independent Code
-force_pic: ?bool = null,
-
-/// Position Independent Executable
-pie: ?bool = null,
-
-red_zone: ?bool = null,
-
-omit_frame_pointer: ?bool = null,
-dll_export_fns: ?bool = null,
-
-subsystem: ?std.Target.SubSystem = null,
-
-entry_symbol_name: ?[]const u8 = null,
-
-/// Overrides the default stack size
-stack_size: ?u64 = null,
-
-want_lto: ?bool = null,
-use_llvm: ?bool = null,
-use_lld: ?bool = null,
-
-output_path_source: GeneratedFile,
-output_lib_path_source: GeneratedFile,
-output_h_path_source: GeneratedFile,
-output_pdb_path_source: GeneratedFile,
-
-pub const CSourceFiles = struct {
- files: []const []const u8,
- flags: []const []const u8,
-};
-
-pub const CSourceFile = struct {
- source: FileSource,
- args: []const []const u8,
-
- pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile {
- return .{
- .source = self.source.dupe(b),
- .args = b.dupeStrings(self.args),
- };
- }
-};
-
-pub const LinkObject = union(enum) {
- static_path: FileSource,
- other_step: *LibExeObjStep,
- system_lib: SystemLib,
- assembly_file: FileSource,
- c_source_file: *CSourceFile,
- c_source_files: *CSourceFiles,
-};
-
-pub const SystemLib = struct {
- name: []const u8,
- needed: bool,
- weak: bool,
- use_pkg_config: enum {
- /// Don't use pkg-config, just pass -lfoo where foo is name.
- no,
- /// Try to get information on how to link the library from pkg-config.
- /// If that fails, fall back to passing -lfoo where foo is name.
- yes,
- /// Try to get information on how to link the library from pkg-config.
- /// If that fails, error out.
- force,
- },
-};
-
-const FrameworkLinkInfo = struct {
- needed: bool = false,
- weak: bool = false,
-};
-
-pub const IncludeDir = union(enum) {
- raw_path: []const u8,
- raw_path_system: []const u8,
- other_step: *LibExeObjStep,
- config_header_step: *ConfigHeaderStep,
-};
-
-pub const Options = struct {
- name: []const u8,
- root_source_file: ?FileSource = null,
- target: CrossTarget,
- optimize: std.builtin.Mode,
- kind: Kind,
- linkage: ?Linkage = null,
- version: ?std.builtin.Version = null,
-};
-
-pub const Kind = enum {
- exe,
- lib,
- obj,
- @"test",
- test_exe,
-};
-
-pub const Linkage = enum { dynamic, static };
-
-pub const EmitOption = union(enum) {
- default: void,
- no_emit: void,
- emit: void,
- emit_to: []const u8,
-
- fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 {
- return switch (self) {
- .no_emit => b.fmt("-fno-{s}", .{arg_name}),
- .default => null,
- .emit => b.fmt("-f{s}", .{arg_name}),
- .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
- };
- }
-};
-
-pub fn create(builder: *std.Build, options: Options) *LibExeObjStep {
- const name = builder.dupe(options.name);
- const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
- if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
- panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
- }
-
- const self = builder.allocator.create(LibExeObjStep) catch unreachable;
- self.* = LibExeObjStep{
- .strip = null,
- .unwind_tables = null,
- .builder = builder,
- .verbose_link = false,
- .verbose_cc = false,
- .optimize = options.optimize,
- .target = options.target,
- .linkage = options.linkage,
- .kind = options.kind,
- .root_src = root_src,
- .name = name,
- .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
- .step = Step.init(base_id, name, builder.allocator, make),
- .version = options.version,
- .out_filename = undefined,
- .out_h_filename = builder.fmt("{s}.h", .{name}),
- .out_lib_filename = undefined,
- .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
- .major_only_filename = null,
- .name_only_filename = null,
- .packages = ArrayList(Pkg).init(builder.allocator),
- .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
- .link_objects = ArrayList(LinkObject).init(builder.allocator),
- .c_macros = ArrayList([]const u8).init(builder.allocator),
- .lib_paths = ArrayList([]const u8).init(builder.allocator),
- .rpaths = ArrayList([]const u8).init(builder.allocator),
- .framework_dirs = ArrayList([]const u8).init(builder.allocator),
- .installed_headers = ArrayList(*Step).init(builder.allocator),
- .object_src = undefined,
- .c_std = std.Build.CStd.C99,
- .override_lib_dir = null,
- .main_pkg_path = null,
- .exec_cmd_args = null,
- .name_prefix = "",
- .filter = null,
- .test_runner = null,
- .disable_stack_probing = false,
- .disable_sanitize_c = false,
- .sanitize_thread = false,
- .rdynamic = false,
- .output_dir = null,
- .override_dest_dir = null,
- .installed_path = null,
- .install_step = null,
-
- .output_path_source = GeneratedFile{ .step = &self.step },
- .output_lib_path_source = GeneratedFile{ .step = &self.step },
- .output_h_path_source = GeneratedFile{ .step = &self.step },
- .output_pdb_path_source = GeneratedFile{ .step = &self.step },
-
- .target_info = NativeTargetInfo.detect(self.target) catch unreachable,
- };
- self.computeOutFileNames();
- if (root_src) |rs| rs.addStepDependencies(&self.step);
- return self;
-}
-
-fn computeOutFileNames(self: *LibExeObjStep) void {
- const target = self.target_info.target;
-
- self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
- .root_name = self.name,
- .target = target,
- .output_mode = switch (self.kind) {
- .lib => .Lib,
- .obj => .Obj,
- .exe, .@"test", .test_exe => .Exe,
- },
- .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
- .dynamic => .Dynamic,
- .static => .Static,
- }) else null,
- .version = self.version,
- }) catch unreachable;
-
- if (self.kind == .lib) {
- if (self.linkage != null and self.linkage.? == .static) {
- self.out_lib_filename = self.out_filename;
- } else if (self.version) |version| {
- if (target.isDarwin()) {
- self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
- self.name,
- version.major,
- });
- self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
- self.out_lib_filename = self.out_filename;
- } else if (target.os.tag == .windows) {
- self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
- } else {
- self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
- self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
- self.out_lib_filename = self.out_filename;
- }
- } else {
- if (target.isDarwin()) {
- self.out_lib_filename = self.out_filename;
- } else if (target.os.tag == .windows) {
- self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
- } else {
- self.out_lib_filename = self.out_filename;
- }
- }
- if (self.output_dir != null) {
- self.output_lib_path_source.path = self.builder.pathJoin(
- &.{ self.output_dir.?, self.out_lib_filename },
- );
- }
- }
-}
-
-pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
- self.output_dir = self.builder.dupePath(dir);
-}
-
-pub fn install(self: *LibExeObjStep) void {
- self.builder.installArtifact(self);
-}
-
-pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
- return self.builder.installRaw(self, dest_filename, options);
-}
-
-pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
- const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
- a.builder.getInstallStep().dependOn(&install_file.step);
- a.installed_headers.append(&install_file.step) catch unreachable;
-}
-
-pub fn installHeadersDirectory(
- a: *LibExeObjStep,
- src_dir_path: []const u8,
- dest_rel_path: []const u8,
-) void {
- return installHeadersDirectoryOptions(a, .{
- .source_dir = src_dir_path,
- .install_dir = .header,
- .install_subdir = dest_rel_path,
- });
-}
-
-pub fn installHeadersDirectoryOptions(
- a: *LibExeObjStep,
- options: std.Build.InstallDirStep.Options,
-) void {
- const install_dir = a.builder.addInstallDirectory(options);
- a.builder.getInstallStep().dependOn(&install_dir.step);
- a.installed_headers.append(&install_dir.step) catch unreachable;
-}
-
-pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
- assert(l.kind == .lib);
- const install_step = a.builder.getInstallStep();
- // Copy each element from installed_headers, modifying the builder
- // to be the new parent's builder.
- for (l.installed_headers.items) |step| {
- const step_copy = switch (step.id) {
- inline .install_file, .install_dir => |id| blk: {
- const T = id.Type();
- const ptr = a.builder.allocator.create(T) catch unreachable;
- ptr.* = step.cast(T).?.*;
- ptr.override_source_builder = ptr.builder;
- ptr.builder = a.builder;
- break :blk &ptr.step;
- },
- else => unreachable,
- };
- a.installed_headers.append(step_copy) catch unreachable;
- install_step.dependOn(step_copy);
- }
- a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
-}
-
-/// Creates a `RunStep` with an executable built with `addExecutable`.
-/// Add command line arguments with `addArg`.
-pub fn run(exe: *LibExeObjStep) *RunStep {
- assert(exe.kind == .exe or exe.kind == .test_exe);
-
- // It doesn't have to be native. We catch that if you actually try to run it.
- // Consider that this is declarative; the run step may not be run unless a user
- // option is supplied.
- const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
- run_step.addArtifactArg(exe);
-
- if (exe.kind == .test_exe) {
- run_step.addArg(exe.builder.zig_exe);
- }
-
- if (exe.vcpkg_bin_path) |path| {
- run_step.addPathDir(path);
- }
-
- return run_step;
-}
-
-/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
-/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
-/// When a binary cannot be ran through emulation or the option is disabled, a warning
-/// will be printed and the binary will *NOT* be ran.
-pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
- assert(exe.kind == .exe or exe.kind == .test_exe);
-
- const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
- if (exe.vcpkg_bin_path) |path| {
- RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
- }
- return run_step;
-}
-
-pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
- return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
-}
-
-pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
- self.linker_script = source.dupe(self.builder);
- source.addStepDependencies(&self.step);
-}
-
-pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
-}
-
-pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{
- .needed = true,
- }) catch unreachable;
-}
-
-pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{
- .weak = true,
- }) catch unreachable;
-}
-
-/// Returns whether the library, executable, or object depends on a particular system library.
-pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
- if (isLibCLibrary(name)) {
- return self.is_linking_libc;
- }
- if (isLibCppLibrary(name)) {
- return self.is_linking_libcpp;
- }
- for (self.link_objects.items) |link_object| {
- switch (link_object) {
- .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
- else => continue,
- }
- }
- return false;
-}
-
-pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
- assert(lib.kind == .lib);
- self.linkLibraryOrObject(lib);
-}
-
-pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
- return self.kind == .lib and self.linkage == Linkage.dynamic;
-}
-
-pub fn isStaticLibrary(self: *LibExeObjStep) bool {
- return self.kind == .lib and self.linkage != Linkage.dynamic;
-}
-
-pub fn producesPdbFile(self: *LibExeObjStep) bool {
- if (!self.target.isWindows() and !self.target.isUefi()) return false;
- if (self.target.getObjectFormat() == .c) return false;
- if (self.strip == true) return false;
- return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
-}
-
-pub fn linkLibC(self: *LibExeObjStep) void {
- self.is_linking_libc = true;
-}
-
-pub fn linkLibCpp(self: *LibExeObjStep) void {
- self.is_linking_libcpp = true;
-}
-
-/// If the value is omitted, it is set to 1.
-/// `name` and `value` need not live longer than the function call.
-pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
- const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
- self.c_macros.append(macro) catch unreachable;
-}
-
-/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
-pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
- self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
-}
-
-/// This one has no integration with anything, it just puts -lname on the command line.
-/// Prefer to use `linkSystemLibrary` instead.
-pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = false,
- .weak = false,
- .use_pkg_config = .no,
- },
- }) catch unreachable;
-}
-
-/// This one has no integration with anything, it just puts -needed-lname on the command line.
-/// Prefer to use `linkSystemLibraryNeeded` instead.
-pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = true,
- .weak = false,
- .use_pkg_config = .no,
- },
- }) catch unreachable;
-}
-
-/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
-/// command line. Prefer to use `linkSystemLibraryWeak` instead.
-pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = false,
- .weak = true,
- .use_pkg_config = .no,
- },
- }) catch unreachable;
-}
-
-/// This links against a system library, exclusively using pkg-config to find the library.
-/// Prefer to use `linkSystemLibrary` instead.
-pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(lib_name),
- .needed = false,
- .weak = false,
- .use_pkg_config = .force,
- },
- }) catch unreachable;
-}
-
-/// This links against a system library, exclusively using pkg-config to find the library.
-/// Prefer to use `linkSystemLibraryNeeded` instead.
-pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(lib_name),
- .needed = true,
- .weak = false,
- .use_pkg_config = .force,
- },
- }) catch unreachable;
-}
-
-/// Run pkg-config for the given library name and parse the output, returning the arguments
-/// that should be passed to zig to link the given library.
-pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
- const pkg_name = match: {
- // First we have to map the library name to pkg config name. Unfortunately,
- // there are several examples where this is not straightforward:
- // -lSDL2 -> pkg-config sdl2
- // -lgdk-3 -> pkg-config gdk-3.0
- // -latk-1.0 -> pkg-config atk
- const pkgs = try getPkgConfigList(self.builder);
-
- // Exact match means instant winner.
- for (pkgs) |pkg| {
- if (mem.eql(u8, pkg.name, lib_name)) {
- break :match pkg.name;
- }
- }
-
- // Next we'll try ignoring case.
- for (pkgs) |pkg| {
- if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
- break :match pkg.name;
- }
- }
-
- // Now try appending ".0".
- for (pkgs) |pkg| {
- if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
- if (pos != 0) continue;
- if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
- break :match pkg.name;
- }
- }
- }
-
- // Trimming "-1.0".
- if (mem.endsWith(u8, lib_name, "-1.0")) {
- const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
- for (pkgs) |pkg| {
- if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
- break :match pkg.name;
- }
- }
- }
-
- return error.PackageNotFound;
- };
-
- var code: u8 = undefined;
- const stdout = if (self.builder.execAllowFail(&[_][]const u8{
- "pkg-config",
- pkg_name,
- "--cflags",
- "--libs",
- }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
- error.ProcessTerminated => return error.PkgConfigCrashed,
- error.ExecNotSupported => return error.PkgConfigFailed,
- error.ExitCodeFailure => return error.PkgConfigFailed,
- error.FileNotFound => return error.PkgConfigNotInstalled,
- error.ChildExecFailed => return error.PkgConfigFailed,
- else => return err,
- };
-
- var zig_args = ArrayList([]const u8).init(self.builder.allocator);
- defer zig_args.deinit();
-
- var it = mem.tokenize(u8, stdout, " \r\n\t");
- while (it.next()) |tok| {
- if (mem.eql(u8, tok, "-I")) {
- const dir = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
- } else if (mem.startsWith(u8, tok, "-I")) {
- try zig_args.append(tok);
- } else if (mem.eql(u8, tok, "-L")) {
- const dir = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
- } else if (mem.startsWith(u8, tok, "-L")) {
- try zig_args.append(tok);
- } else if (mem.eql(u8, tok, "-l")) {
- const lib = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
- } else if (mem.startsWith(u8, tok, "-l")) {
- try zig_args.append(tok);
- } else if (mem.eql(u8, tok, "-D")) {
- const macro = it.next() orelse return error.PkgConfigInvalidOutput;
- try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
- } else if (mem.startsWith(u8, tok, "-D")) {
- try zig_args.append(tok);
- } else if (self.builder.verbose) {
- log.warn("Ignoring pkg-config flag '{s}'", .{tok});
- }
- }
-
- return zig_args.toOwnedSlice();
-}
-
-pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
- self.linkSystemLibraryInner(name, .{});
-}
-
-pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
- self.linkSystemLibraryInner(name, .{ .needed = true });
-}
-
-pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
- self.linkSystemLibraryInner(name, .{ .weak = true });
-}
-
-fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
- needed: bool = false,
- weak: bool = false,
-}) void {
- if (isLibCLibrary(name)) {
- self.linkLibC();
- return;
- }
- if (isLibCppLibrary(name)) {
- self.linkLibCpp();
- return;
- }
-
- self.link_objects.append(.{
- .system_lib = .{
- .name = self.builder.dupe(name),
- .needed = opts.needed,
- .weak = opts.weak,
- .use_pkg_config = .yes,
- },
- }) catch unreachable;
-}
-
-pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
- assert(self.kind == .@"test" or self.kind == .test_exe);
- self.name_prefix = self.builder.dupe(text);
-}
-
-pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
- assert(self.kind == .@"test" or self.kind == .test_exe);
- self.filter = if (text) |t| self.builder.dupe(t) else null;
-}
-
-pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
- assert(self.kind == .@"test" or self.kind == .test_exe);
- self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
-}
-
-/// Handy when you have many C/C++ source files and want them all to have the same flags.
-pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
- const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
-
- const files_copy = self.builder.dupeStrings(files);
- const flags_copy = self.builder.dupeStrings(flags);
-
- c_source_files.* = .{
- .files = files_copy,
- .flags = flags_copy,
- };
- self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
-}
-
-pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
- self.addCSourceFileSource(.{
- .args = flags,
- .source = .{ .path = file },
- });
-}
-
-pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
- const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
- c_source_file.* = source.dupe(self.builder);
- self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
- source.source.addStepDependencies(&self.step);
-}
-
-pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
- self.verbose_link = value;
-}
-
-pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
- self.verbose_cc = value;
-}
-
-pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
- self.override_lib_dir = self.builder.dupePath(dir_path);
-}
-
-pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
- self.main_pkg_path = self.builder.dupePath(dir_path);
-}
-
-pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
- self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
-}
-
-/// Returns the generated executable, library or object file.
-/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
-pub fn getOutputSource(self: *LibExeObjStep) FileSource {
- return FileSource{ .generated = &self.output_path_source };
-}
-
-/// Returns the generated import library. This function can only be called for libraries.
-pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
- assert(self.kind == .lib);
- return FileSource{ .generated = &self.output_lib_path_source };
-}
-
-/// Returns the generated header file.
-/// This function can only be called for libraries or object files which have `emit_h` set.
-pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
- assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
- assert(self.emit_h);
- return FileSource{ .generated = &self.output_h_path_source };
-}
-
-/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
-pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
- // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
- assert(self.target.isWindows() or self.target.isUefi());
- return FileSource{ .generated = &self.output_pdb_path_source };
-}
-
-pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
- self.link_objects.append(.{
- .assembly_file = .{ .path = self.builder.dupe(path) },
- }) catch unreachable;
-}
-
-pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
- const source_duped = source.dupe(self.builder);
- self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
- source_duped.addStepDependencies(&self.step);
-}
-
-pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
- self.addObjectFileSource(.{ .path = source_file });
-}
-
-pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
- self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
- source.addStepDependencies(&self.step);
-}
-
-pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
- assert(obj.kind == .obj);
- self.linkLibraryOrObject(obj);
-}
-
-pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
-pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
-pub const addLibPath = @compileError("deprecated, use addLibraryPath");
-pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
-
-pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
- self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
-}
-
-pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
- self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
-}
-
-pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
- self.step.dependOn(&config_header.step);
- self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
-}
-
-pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
- self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
-}
-
-pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
- self.rpaths.append(self.builder.dupe(path)) catch unreachable;
-}
-
-pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
- self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
-}
-
-pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
- self.packages.append(self.builder.dupePkg(package)) catch unreachable;
- self.addRecursiveBuildDeps(package);
-}
-
-pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
- self.addPackage(options.getPackage(package_name));
-}
-
-fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
- package.source.addStepDependencies(&self.step);
- if (package.dependencies) |deps| {
- for (deps) |dep| {
- self.addRecursiveBuildDeps(dep);
- }
- }
-}
-
-pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
- self.addPackage(Pkg{
- .name = self.builder.dupe(name),
- .source = .{ .path = self.builder.dupe(pkg_index_path) },
- });
-}
-
-/// If Vcpkg was found on the system, it will be added to include and lib
-/// paths for the specified target.
-pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
- // Ideally in the Unattempted case we would call the function recursively
- // after findVcpkgRoot and have only one switch statement, but the compiler
- // cannot resolve the error set.
- switch (self.builder.vcpkg_root) {
- .unattempted => {
- self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
- VcpkgRoot{ .found = root }
- else
- .not_found;
- },
- .not_found => return error.VcpkgNotFound,
- .found => {},
- }
-
- switch (self.builder.vcpkg_root) {
- .unattempted => unreachable,
- .not_found => return error.VcpkgNotFound,
- .found => |root| {
- const allocator = self.builder.allocator;
- const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
- defer self.builder.allocator.free(triplet);
-
- const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
- errdefer allocator.free(include_path);
- try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
-
- const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
- try self.lib_paths.append(lib_path);
-
- self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
- },
- }
-}
-
-pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
- assert(self.kind == .@"test");
- const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
- for (args) |arg, i| {
- duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
- }
- self.exec_cmd_args = duped_args;
-}
-
-fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
- self.step.dependOn(&other.step);
- self.link_objects.append(.{ .other_step = other }) catch unreachable;
- self.include_dirs.append(.{ .other_step = other }) catch unreachable;
-}
-
-fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
- const builder = self.builder;
-
- try zig_args.append("--pkg-begin");
- try zig_args.append(pkg.name);
- try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
-
- if (pkg.dependencies) |dependencies| {
- for (dependencies) |sub_pkg| {
- try self.makePackageCmd(sub_pkg, zig_args);
- }
- }
-
- try zig_args.append("--pkg-end");
-}
-
-fn make(step: *Step) !void {
- const self = @fieldParentPtr(LibExeObjStep, "step", step);
- const builder = self.builder;
-
- if (self.root_src == null and self.link_objects.items.len == 0) {
- log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
- return error.NeedAnObject;
- }
-
- var zig_args = ArrayList([]const u8).init(builder.allocator);
- defer zig_args.deinit();
-
- zig_args.append(builder.zig_exe) catch unreachable;
-
- const cmd = switch (self.kind) {
- .lib => "build-lib",
- .exe => "build-exe",
- .obj => "build-obj",
- .@"test" => "test",
- .test_exe => "test",
- };
- zig_args.append(cmd) catch unreachable;
-
- if (builder.color != .auto) {
- try zig_args.append("--color");
- try zig_args.append(@tagName(builder.color));
- }
-
- if (builder.reference_trace) |some| {
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
- }
-
- try addFlag(&zig_args, "LLVM", self.use_llvm);
- try addFlag(&zig_args, "LLD", self.use_lld);
-
- if (self.target.ofmt) |ofmt| {
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
- }
-
- if (self.entry_symbol_name) |entry| {
- try zig_args.append("--entry");
- try zig_args.append(entry);
- }
-
- if (self.stack_size) |stack_size| {
- try zig_args.append("--stack");
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
- }
-
- if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
-
- // We will add link objects from transitive dependencies, but we want to keep
- // all link objects in the same order provided.
- // This array is used to keep self.link_objects immutable.
- var transitive_deps: TransitiveDeps = .{
- .link_objects = ArrayList(LinkObject).init(builder.allocator),
- .seen_system_libs = StringHashMap(void).init(builder.allocator),
- .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
- .is_linking_libcpp = self.is_linking_libcpp,
- .is_linking_libc = self.is_linking_libc,
- .frameworks = &self.frameworks,
- };
-
- try transitive_deps.seen_steps.put(&self.step, {});
- try transitive_deps.add(self.link_objects.items);
-
- var prev_has_extra_flags = false;
-
- for (transitive_deps.link_objects.items) |link_object| {
- switch (link_object) {
- .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
-
- .other_step => |other| switch (other.kind) {
- .exe => @panic("Cannot link with an executable build artifact"),
- .test_exe => @panic("Cannot link with an executable build artifact"),
- .@"test" => @panic("Cannot link with a test"),
- .obj => {
- try zig_args.append(other.getOutputSource().getPath(builder));
- },
- .lib => l: {
- if (self.isStaticLibrary() and other.isStaticLibrary()) {
- // Avoid putting a static library inside a static library.
- break :l;
- }
-
- const full_path_lib = other.getOutputLibSource().getPath(builder);
- try zig_args.append(full_path_lib);
-
- if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
- if (fs.path.dirname(full_path_lib)) |dirname| {
- try zig_args.append("-rpath");
- try zig_args.append(dirname);
- }
- }
- },
- },
-
- .system_lib => |system_lib| {
- const prefix: []const u8 = prefix: {
- if (system_lib.needed) break :prefix "-needed-l";
- if (system_lib.weak) {
- if (self.target.isDarwin()) break :prefix "-weak-l";
- log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
- }
- break :prefix "-l";
- };
- switch (system_lib.use_pkg_config) {
- .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
- .yes, .force => {
- if (self.runPkgConfig(system_lib.name)) |args| {
- try zig_args.appendSlice(args);
- } else |err| switch (err) {
- error.PkgConfigInvalidOutput,
- error.PkgConfigCrashed,
- error.PkgConfigFailed,
- error.PkgConfigNotInstalled,
- error.PackageNotFound,
- => switch (system_lib.use_pkg_config) {
- .yes => {
- // pkg-config failed, so fall back to linking the library
- // by name directly.
- try zig_args.append(builder.fmt("{s}{s}", .{
- prefix,
- system_lib.name,
- }));
- },
- .force => {
- panic("pkg-config failed for library {s}", .{system_lib.name});
- },
- .no => unreachable,
- },
-
- else => |e| return e,
- }
- },
- }
- },
-
- .assembly_file => |asm_file| {
- if (prev_has_extra_flags) {
- try zig_args.append("-extra-cflags");
- try zig_args.append("--");
- prev_has_extra_flags = false;
- }
- try zig_args.append(asm_file.getPath(builder));
- },
-
- .c_source_file => |c_source_file| {
- if (c_source_file.args.len == 0) {
- if (prev_has_extra_flags) {
- try zig_args.append("-cflags");
- try zig_args.append("--");
- prev_has_extra_flags = false;
- }
- } else {
- try zig_args.append("-cflags");
- for (c_source_file.args) |arg| {
- try zig_args.append(arg);
- }
- try zig_args.append("--");
- }
- try zig_args.append(c_source_file.source.getPath(builder));
- },
-
- .c_source_files => |c_source_files| {
- if (c_source_files.flags.len == 0) {
- if (prev_has_extra_flags) {
- try zig_args.append("-cflags");
- try zig_args.append("--");
- prev_has_extra_flags = false;
- }
- } else {
- try zig_args.append("-cflags");
- for (c_source_files.flags) |flag| {
- try zig_args.append(flag);
- }
- try zig_args.append("--");
- }
- for (c_source_files.files) |file| {
- try zig_args.append(builder.pathFromRoot(file));
- }
- },
- }
- }
-
- if (transitive_deps.is_linking_libcpp) {
- try zig_args.append("-lc++");
- }
-
- if (transitive_deps.is_linking_libc) {
- try zig_args.append("-lc");
- }
-
- if (self.image_base) |image_base| {
- try zig_args.append("--image-base");
- try zig_args.append(builder.fmt("0x{x}", .{image_base}));
- }
-
- if (self.filter) |filter| {
- try zig_args.append("--test-filter");
- try zig_args.append(filter);
- }
-
- if (self.test_evented_io) {
- try zig_args.append("--test-evented-io");
- }
-
- if (self.name_prefix.len != 0) {
- try zig_args.append("--test-name-prefix");
- try zig_args.append(self.name_prefix);
- }
-
- if (self.test_runner) |test_runner| {
- try zig_args.append("--test-runner");
- try zig_args.append(builder.pathFromRoot(test_runner));
- }
-
- for (builder.debug_log_scopes) |log_scope| {
- try zig_args.append("--debug-log");
- try zig_args.append(log_scope);
- }
-
- if (builder.debug_compile_errors) {
- try zig_args.append("--debug-compile-errors");
- }
-
- if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
- if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
- if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
- if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
- if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
- if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
-
- if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
- if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
- if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
- if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
- if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
- if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
- if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
-
- if (self.emit_h) try zig_args.append("-femit-h");
-
- try addFlag(&zig_args, "strip", self.strip);
- try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
-
- switch (self.compress_debug_sections) {
- .none => {},
- .zlib => try zig_args.append("--compress-debug-sections=zlib"),
- }
-
- if (self.link_eh_frame_hdr) {
- try zig_args.append("--eh-frame-hdr");
- }
- if (self.link_emit_relocs) {
- try zig_args.append("--emit-relocs");
- }
- if (self.link_function_sections) {
- try zig_args.append("-ffunction-sections");
- }
- if (self.link_gc_sections) |x| {
- try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
- }
- if (self.linker_allow_shlib_undefined) |x| {
- try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
- }
- if (self.link_z_notext) {
- try zig_args.append("-z");
- try zig_args.append("notext");
- }
- if (!self.link_z_relro) {
- try zig_args.append("-z");
- try zig_args.append("norelro");
- }
- if (self.link_z_lazy) {
- try zig_args.append("-z");
- try zig_args.append("lazy");
- }
- if (self.link_z_common_page_size) |size| {
- try zig_args.append("-z");
- try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
- }
- if (self.link_z_max_page_size) |size| {
- try zig_args.append("-z");
- try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
- }
-
- if (self.libc_file) |libc_file| {
- try zig_args.append("--libc");
- try zig_args.append(libc_file.getPath(builder));
- } else if (builder.libc_file) |libc_file| {
- try zig_args.append("--libc");
- try zig_args.append(libc_file);
- }
-
- switch (self.optimize) {
- .Debug => {}, // Skip since it's the default.
- else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable,
- }
-
- try zig_args.append("--cache-dir");
- try zig_args.append(builder.pathFromRoot(builder.cache_root));
-
- try zig_args.append("--global-cache-dir");
- try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
-
- zig_args.append("--name") catch unreachable;
- zig_args.append(self.name) catch unreachable;
-
- if (self.linkage) |some| switch (some) {
- .dynamic => try zig_args.append("-dynamic"),
- .static => try zig_args.append("-static"),
- };
- if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
- if (self.version) |version| {
- zig_args.append("--version") catch unreachable;
- zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
- }
-
- if (self.target.isDarwin()) {
- const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
- self.target.libPrefix(),
- self.name,
- self.target.dynamicLibSuffix(),
- });
- try zig_args.append("-install_name");
- try zig_args.append(install_name);
- }
- }
-
- if (self.entitlements) |entitlements| {
- try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
- }
- if (self.pagezero_size) |pagezero_size| {
- const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
- try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
- }
- if (self.search_strategy) |strat| switch (strat) {
- .paths_first => try zig_args.append("-search_paths_first"),
- .dylibs_first => try zig_args.append("-search_dylibs_first"),
- };
- if (self.headerpad_size) |headerpad_size| {
- const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
- try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
- }
- if (self.headerpad_max_install_names) {
- try zig_args.append("-headerpad_max_install_names");
- }
- if (self.dead_strip_dylibs) {
- try zig_args.append("-dead_strip_dylibs");
- }
-
- try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
- try addFlag(&zig_args, "single-threaded", self.single_threaded);
- if (self.disable_stack_probing) {
- try zig_args.append("-fno-stack-check");
- }
- try addFlag(&zig_args, "stack-protector", self.stack_protector);
- if (self.red_zone) |red_zone| {
- if (red_zone) {
- try zig_args.append("-mred-zone");
- } else {
- try zig_args.append("-mno-red-zone");
- }
- }
- try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
- try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
-
- if (self.disable_sanitize_c) {
- try zig_args.append("-fno-sanitize-c");
- }
- if (self.sanitize_thread) {
- try zig_args.append("-fsanitize-thread");
- }
- if (self.rdynamic) {
- try zig_args.append("-rdynamic");
- }
- if (self.import_memory) {
- try zig_args.append("--import-memory");
- }
- if (self.import_symbols) {
- try zig_args.append("--import-symbols");
- }
- if (self.import_table) {
- try zig_args.append("--import-table");
- }
- if (self.export_table) {
- try zig_args.append("--export-table");
- }
- if (self.initial_memory) |initial_memory| {
- try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
- }
- if (self.max_memory) |max_memory| {
- try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
- }
- if (self.shared_memory) {
- try zig_args.append("--shared-memory");
- }
- if (self.global_base) |global_base| {
- try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
- }
-
- if (self.code_model != .default) {
- try zig_args.append("-mcmodel");
- try zig_args.append(@tagName(self.code_model));
- }
- if (self.wasi_exec_model) |model| {
- try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
- }
- for (self.export_symbol_names) |symbol_name| {
- try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
- }
-
- if (!self.target.isNative()) {
- try zig_args.appendSlice(&.{
- "-target", try self.target.zigTriple(builder.allocator),
- "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
- });
-
- if (self.target.dynamic_linker.get()) |dynamic_linker| {
- try zig_args.append("--dynamic-linker");
- try zig_args.append(dynamic_linker);
- }
- }
-
- if (self.linker_script) |linker_script| {
- try zig_args.append("--script");
- try zig_args.append(linker_script.getPath(builder));
- }
-
- if (self.version_script) |version_script| {
- try zig_args.append("--version-script");
- try zig_args.append(builder.pathFromRoot(version_script));
- }
-
- if (self.kind == .@"test") {
- if (self.exec_cmd_args) |exec_cmd_args| {
- for (exec_cmd_args) |cmd_arg| {
- if (cmd_arg) |arg| {
- try zig_args.append("--test-cmd");
- try zig_args.append(arg);
- } else {
- try zig_args.append("--test-cmd-bin");
- }
- }
- } else {
- const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
-
- switch (builder.host.getExternalExecutor(self.target_info, .{
- .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
- .link_libc = transitive_deps.is_linking_libc,
- })) {
- .native => {},
- .bad_dl, .bad_os_or_cpu => {
- try zig_args.append("--test-no-exec");
- },
- .rosetta => if (builder.enable_rosetta) {
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- .qemu => |bin_name| ok: {
- if (builder.enable_qemu) qemu: {
- const glibc_dir_arg = if (need_cross_glibc)
- builder.glibc_runtimes_dir orelse break :qemu
- else
- null;
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- if (glibc_dir_arg) |dir| {
- // TODO look into making this a call to `linuxTriple`. This
- // needs the directory to be called "i686" rather than
- // "x86" which is why we do it manually here.
- const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
- const cpu_arch = self.target.getCpuArch();
- const os_tag = self.target.getOsTag();
- const abi = self.target.getAbi();
- const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
- "i686"
- else
- @tagName(cpu_arch);
- const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
- dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
- });
-
- try zig_args.append("--test-cmd");
- try zig_args.append("-L");
- try zig_args.append("--test-cmd");
- try zig_args.append(full_dir);
- }
- try zig_args.append("--test-cmd-bin");
- break :ok;
- }
- try zig_args.append("--test-no-exec");
- },
- .wine => |bin_name| if (builder.enable_wine) {
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- .wasmtime => |bin_name| if (builder.enable_wasmtime) {
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- try zig_args.append("--test-cmd");
- try zig_args.append("--dir=.");
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- .darling => |bin_name| if (builder.enable_darling) {
- try zig_args.append("--test-cmd");
- try zig_args.append(bin_name);
- try zig_args.append("--test-cmd-bin");
- } else {
- try zig_args.append("--test-no-exec");
- },
- }
- }
- } else if (self.kind == .test_exe) {
- try zig_args.append("--test-no-exec");
- }
-
- for (self.packages.items) |pkg| {
- try self.makePackageCmd(pkg, &zig_args);
- }
-
- for (self.include_dirs.items) |include_dir| {
- switch (include_dir) {
- .raw_path => |include_path| {
- try zig_args.append("-I");
- try zig_args.append(builder.pathFromRoot(include_path));
- },
- .raw_path_system => |include_path| {
- if (builder.sysroot != null) {
- try zig_args.append("-iwithsysroot");
- } else {
- try zig_args.append("-isystem");
- }
-
- const resolved_include_path = builder.pathFromRoot(include_path);
-
- const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
- // We need to check for disk designator and strip it out from dir path so
- // that zig/clang can concat resolved_include_path with sysroot.
- const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
-
- if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
- break :blk resolved_include_path[where + disk_designator.len ..];
- }
-
- break :blk resolved_include_path;
- } else resolved_include_path;
-
- try zig_args.append(common_include_path);
- },
- .other_step => |other| {
- if (other.emit_h) {
- const h_path = other.getOutputHSource().getPath(builder);
- try zig_args.append("-isystem");
- try zig_args.append(fs.path.dirname(h_path).?);
- }
- if (other.installed_headers.items.len > 0) {
- for (other.installed_headers.items) |install_step| {
- try install_step.make();
- }
- try zig_args.append("-I");
- try zig_args.append(builder.pathJoin(&.{
- other.builder.install_prefix, "include",
- }));
- }
- },
- .config_header_step => |config_header| {
- try zig_args.append("-I");
- try zig_args.append(config_header.output_dir);
- },
- }
- }
-
- for (self.lib_paths.items) |lib_path| {
- try zig_args.append("-L");
- try zig_args.append(lib_path);
- }
-
- for (self.rpaths.items) |rpath| {
- try zig_args.append("-rpath");
- try zig_args.append(rpath);
- }
-
- for (self.c_macros.items) |c_macro| {
- try zig_args.append("-D");
- try zig_args.append(c_macro);
- }
-
- if (self.target.isDarwin()) {
- for (self.framework_dirs.items) |dir| {
- if (builder.sysroot != null) {
- try zig_args.append("-iframeworkwithsysroot");
- } else {
- try zig_args.append("-iframework");
- }
- try zig_args.append(dir);
- try zig_args.append("-F");
- try zig_args.append(dir);
- }
-
- var it = self.frameworks.iterator();
- while (it.next()) |entry| {
- const name = entry.key_ptr.*;
- const info = entry.value_ptr.*;
- if (info.needed) {
- zig_args.append("-needed_framework") catch unreachable;
- } else if (info.weak) {
- zig_args.append("-weak_framework") catch unreachable;
- } else {
- zig_args.append("-framework") catch unreachable;
- }
- zig_args.append(name) catch unreachable;
- }
- } else {
- if (self.framework_dirs.items.len > 0) {
- log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
- }
-
- if (self.frameworks.count() > 0) {
- log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
- }
- }
-
- if (builder.sysroot) |sysroot| {
- try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
- }
-
- for (builder.search_prefixes.items) |search_prefix| {
- try zig_args.append("-L");
- try zig_args.append(builder.pathJoin(&.{
- search_prefix, "lib",
- }));
- try zig_args.append("-I");
- try zig_args.append(builder.pathJoin(&.{
- search_prefix, "include",
- }));
- }
-
- try addFlag(&zig_args, "valgrind", self.valgrind_support);
- try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
- try addFlag(&zig_args, "build-id", self.build_id);
-
- if (self.override_lib_dir) |dir| {
- try zig_args.append("--zig-lib-dir");
- try zig_args.append(builder.pathFromRoot(dir));
- } else if (builder.override_lib_dir) |dir| {
- try zig_args.append("--zig-lib-dir");
- try zig_args.append(builder.pathFromRoot(dir));
- }
-
- if (self.main_pkg_path) |dir| {
- try zig_args.append("--main-pkg-path");
- try zig_args.append(builder.pathFromRoot(dir));
- }
-
- try addFlag(&zig_args, "PIC", self.force_pic);
- try addFlag(&zig_args, "PIE", self.pie);
- try addFlag(&zig_args, "lto", self.want_lto);
-
- if (self.subsystem) |subsystem| {
- try zig_args.append("--subsystem");
- try zig_args.append(switch (subsystem) {
- .Console => "console",
- .Windows => "windows",
- .Posix => "posix",
- .Native => "native",
- .EfiApplication => "efi_application",
- .EfiBootServiceDriver => "efi_boot_service_driver",
- .EfiRom => "efi_rom",
- .EfiRuntimeDriver => "efi_runtime_driver",
- });
- }
-
- try zig_args.append("--enable-cache");
-
- // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
- // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
- // pass that to zig, e.g. via 'zig build-lib @args.rsp'
- // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
- var args_length: usize = 0;
- for (zig_args.items) |arg| {
- args_length += arg.len + 1; // +1 to account for null terminator
- }
- if (args_length >= 30 * 1024) {
- const args_dir = try fs.path.join(
- builder.allocator,
- &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
- );
- try std.fs.cwd().makePath(args_dir);
-
- var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
- defer args_arena.deinit();
-
- const args_to_escape = zig_args.items[2..];
- var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
-
- arg_blk: for (args_to_escape) |arg| {
- for (arg) |c, arg_idx| {
- if (c == '\\' or c == '"') {
- // Slow path for arguments that need to be escaped. We'll need to allocate and copy
- var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
- const writer = escaped.writer();
- writer.writeAll(arg[0..arg_idx]) catch unreachable;
- for (arg[arg_idx..]) |to_escape| {
- if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
- try writer.writeByte(to_escape);
- }
- escaped_args.appendAssumeCapacity(escaped.items);
- continue :arg_blk;
- }
- }
- escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
- }
-
- // Write the args to zig-cache/args/ to avoid conflicts with
- // other zig build commands running in parallel.
- const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
- const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
-
- var args_hash: [Sha256.digest_length]u8 = undefined;
- Sha256.hash(args, &args_hash, .{});
- var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
- _ = try std.fmt.bufPrint(
- &args_hex_hash,
- "{s}",
- .{std.fmt.fmtSliceHexLower(&args_hash)},
- );
-
- const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
- try std.fs.cwd().writeFile(args_file, args);
-
- zig_args.shrinkRetainingCapacity(2);
- try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
- }
-
- const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
- const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
-
- if (self.output_dir) |output_dir| {
- var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
- defer src_dir.close();
-
- // Create the output directory if it doesn't exist.
- try std.fs.cwd().makePath(output_dir);
-
- var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
- defer dest_dir.close();
-
- var it = src_dir.iterate();
- while (try it.next()) |entry| {
- // The compiler can put these files into the same directory, but we don't
- // want to copy them over.
- if (mem.eql(u8, entry.name, "llvm-ar.id") or
- mem.eql(u8, entry.name, "libs.txt") or
- mem.eql(u8, entry.name, "builtin.zig") or
- mem.eql(u8, entry.name, "zld.id") or
- mem.eql(u8, entry.name, "lld.id")) continue;
-
- _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
- }
- } else {
- self.output_dir = build_output_dir;
- }
-
- // This will ensure all output filenames will now have the output_dir available!
- self.computeOutFileNames();
-
- // Update generated files
- if (self.output_dir != null) {
- self.output_path_source.path = builder.pathJoin(
- &.{ self.output_dir.?, self.out_filename },
- );
-
- if (self.emit_h) {
- self.output_h_path_source.path = builder.pathJoin(
- &.{ self.output_dir.?, self.out_h_filename },
- );
- }
-
- if (self.target.isWindows() or self.target.isUefi()) {
- self.output_pdb_path_source.path = builder.pathJoin(
- &.{ self.output_dir.?, self.out_pdb_filename },
- );
- }
- }
-
- if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
- try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
- }
-}
-
-fn isLibCLibrary(name: []const u8) bool {
- const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
- for (libc_libraries) |libc_lib_name| {
- if (mem.eql(u8, name, libc_lib_name))
- return true;
- }
- return false;
-}
-
-fn isLibCppLibrary(name: []const u8) bool {
- const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
- for (libcpp_libraries) |libcpp_lib_name| {
- if (mem.eql(u8, name, libcpp_lib_name))
- return true;
- }
- return false;
-}
-
-/// Returned slice must be freed by the caller.
-fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
- const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
- defer allocator.free(appdata_path);
-
- const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
- defer allocator.free(path_file);
-
- const file = fs.cwd().openFile(path_file, .{}) catch return null;
- defer file.close();
-
- const size = @intCast(usize, try file.getEndPos());
- const vcpkg_path = try allocator.alloc(u8, size);
- const size_read = try file.read(vcpkg_path);
- std.debug.assert(size == size_read);
-
- return vcpkg_path;
-}
-
-pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
- const out_dir = fs.path.dirname(output_path) orelse ".";
- const out_basename = fs.path.basename(output_path);
- // sym link for libfoo.so.1 to libfoo.so.1.2.3
- const major_only_path = fs.path.join(
- allocator,
- &[_][]const u8{ out_dir, filename_major_only },
- ) catch unreachable;
- fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
- log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
- return err;
- };
- // sym link for libfoo.so to libfoo.so.1
- const name_only_path = fs.path.join(
- allocator,
- &[_][]const u8{ out_dir, filename_name_only },
- ) catch unreachable;
- fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
- log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
- return err;
- };
-}
-
-fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
- const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
- var list = ArrayList(PkgConfigPkg).init(self.allocator);
- errdefer list.deinit();
- var line_it = mem.tokenize(u8, stdout, "\r\n");
- while (line_it.next()) |line| {
- if (mem.trim(u8, line, " \t").len == 0) continue;
- var tok_it = mem.tokenize(u8, line, " \t");
- try list.append(PkgConfigPkg{
- .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
- .desc = tok_it.rest(),
- });
- }
- return list.toOwnedSlice();
-}
-
-fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
- if (self.pkg_config_pkg_list) |res| {
- return res;
- }
- var code: u8 = undefined;
- if (execPkgConfigList(self, &code)) |list| {
- self.pkg_config_pkg_list = list;
- return list;
- } else |err| {
- const result = switch (err) {
- error.ProcessTerminated => error.PkgConfigCrashed,
- error.ExecNotSupported => error.PkgConfigFailed,
- error.ExitCodeFailure => error.PkgConfigFailed,
- error.FileNotFound => error.PkgConfigNotInstalled,
- error.InvalidName => error.PkgConfigNotInstalled,
- error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
- error.ChildExecFailed => error.PkgConfigFailed,
- else => return err,
- };
- self.pkg_config_pkg_list = result;
- return result;
- }
-}
-
-test "addPackage" {
- if (builtin.os.tag == .wasi) return error.SkipZigTest;
-
- var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
- defer arena.deinit();
-
- const host = try NativeTargetInfo.detect(.{});
-
- var builder = try std.Build.create(
- arena.allocator(),
- "test",
- "test",
- "test",
- "test",
- host,
- );
- defer builder.destroy();
-
- const pkg_dep = Pkg{
- .name = "pkg_dep",
- .source = .{ .path = "/not/a/pkg_dep.zig" },
- };
- const pkg_top = Pkg{
- .name = "pkg_dep",
- .source = .{ .path = "/not/a/pkg_top.zig" },
- .dependencies = &[_]Pkg{pkg_dep},
- };
-
- var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
- exe.addPackage(pkg_top);
-
- try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
-
- const dupe = exe.packages.items[0];
- try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
-}
-
-fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
- const cond = opt orelse return;
- try args.ensureUnusedCapacity(1);
- if (cond) {
- args.appendAssumeCapacity("-f" ++ name);
- } else {
- args.appendAssumeCapacity("-fno-" ++ name);
- }
-}
-
-const TransitiveDeps = struct {
- link_objects: ArrayList(LinkObject),
- seen_system_libs: StringHashMap(void),
- seen_steps: std.AutoHashMap(*const Step, void),
- is_linking_libcpp: bool,
- is_linking_libc: bool,
- frameworks: *StringHashMap(FrameworkLinkInfo),
-
- fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
- try td.link_objects.ensureUnusedCapacity(link_objects.len);
-
- for (link_objects) |link_object| {
- try td.link_objects.append(link_object);
- switch (link_object) {
- .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
- else => {},
- }
- }
- }
-
- fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
- // Inherit dependency on libc and libc++
- td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
- td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
-
- // Inherit dependencies on darwin frameworks
- if (!dyn) {
- var it = other.frameworks.iterator();
- while (it.next()) |framework| {
- try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
- }
- }
-
- // Inherit dependencies on system libraries and static libraries.
- for (other.link_objects.items) |other_link_object| {
- switch (other_link_object) {
- .system_lib => |system_lib| {
- if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
- continue;
-
- if (dyn)
- continue;
-
- try td.link_objects.append(other_link_object);
- },
- .other_step => |inner_other| {
- if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
- continue;
-
- if (!dyn)
- try td.link_objects.append(other_link_object);
-
- try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
- },
- else => continue,
- }
- }
- }
-};
diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig
index 8e1a7ef2fc..94c41ce9e6 100644
--- a/lib/std/Build/OptionsStep.zig
+++ b/lib/std/Build/OptionsStep.zig
@@ -3,7 +3,7 @@ const builtin = @import("builtin");
const fs = std.fs;
const Step = std.Build.Step;
const GeneratedFile = std.Build.GeneratedFile;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const FileSource = std.Build.FileSource;
const OptionsStep = @This();
@@ -195,7 +195,7 @@ pub fn addOptionFileSource(
/// The value is the path in the cache dir.
/// Adds a dependency automatically.
-pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
+pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
self.step.dependOn(&artifact.step);
}
@@ -266,7 +266,7 @@ fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
const OptionArtifactArg = struct {
name: []const u8,
- artifact: *LibExeObjStep,
+ artifact: *CompileStep,
};
const OptionFileSourceArg = struct {
diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig
index 83e4f7f66c..9ab9ea6335 100644
--- a/lib/std/Build/RunStep.zig
+++ b/lib/std/Build/RunStep.zig
@@ -1,7 +1,7 @@
const std = @import("../std.zig");
const builtin = @import("builtin");
const Step = std.Build.Step;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const WriteFileStep = std.Build.WriteFileStep;
const fs = std.fs;
const mem = std.mem;
@@ -48,7 +48,7 @@ pub const StdIoAction = union(enum) {
};
pub const Arg = union(enum) {
- artifact: *LibExeObjStep,
+ artifact: *CompileStep,
file_source: std.Build.FileSource,
bytes: []u8,
};
@@ -66,7 +66,7 @@ pub fn create(builder: *std.Build, name: []const u8) *RunStep {
return self;
}
-pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
+pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
self.step.dependOn(&artifact.step);
}
@@ -355,13 +355,13 @@ fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
std.debug.print("\n", .{});
}
-fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
+fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
addPathForDynLibsInternal(&self.step, self.builder, artifact);
}
/// This should only be used for internal usage, this is called automatically
/// for the user.
-pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *LibExeObjStep) void {
+pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void {
for (artifact.link_objects.items) |link_object| {
switch (link_object) {
.other_step => |other| {
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
index 86d6645c29..aff8a49161 100644
--- a/lib/std/Build/Step.zig
+++ b/lib/std/Build/Step.zig
@@ -7,7 +7,7 @@ done_flag: bool,
pub const Id = enum {
top_level,
- lib_exe_obj,
+ compile,
install_artifact,
install_file,
install_dir,
@@ -28,7 +28,7 @@ pub const Id = enum {
pub fn Type(comptime id: Id) type {
return switch (id) {
.top_level => Build.TopLevelStep,
- .lib_exe_obj => Build.LibExeObjStep,
+ .compile => Build.CompileStep,
.install_artifact => Build.InstallArtifactStep,
.install_file => Build.InstallFileStep,
.install_dir => Build.InstallDirStep,
diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig
index 5404747846..c59362aeee 100644
--- a/lib/std/Build/TranslateCStep.zig
+++ b/lib/std/Build/TranslateCStep.zig
@@ -1,6 +1,6 @@
const std = @import("../std.zig");
const Step = std.Build.Step;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const CheckFileStep = std.Build.CheckFileStep;
const fs = std.fs;
const mem = std.mem;
@@ -51,11 +51,11 @@ pub const AddExecutableOptions = struct {
version: ?std.builtin.Version = null,
target: ?CrossTarget = null,
optimize: ?std.builtin.Mode = null,
- linkage: ?LibExeObjStep.Linkage = null,
+ linkage: ?CompileStep.Linkage = null,
};
/// Creates a step to build an executable from the translated source.
-pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *LibExeObjStep {
+pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
return self.builder.addExecutable(.{
.root_source_file = .{ .generated = &self.output_file },
.name = options.name orelse "translated_c",
diff --git a/lib/std/std.zig b/lib/std/std.zig
index e0318ceb43..7440b49662 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -97,8 +97,10 @@ pub const zig = @import("zig.zig");
pub const start = @import("start.zig");
///// Deprecated. Use `std.Build` instead.
+///// TODO: remove this after releasing 0.11.0
//pub const build = struct {
// /// Deprecated. Use `std.Build` instead.
+// /// TODO: remove this after releasing 0.11.0
// pub const Builder = Build;
//};
diff --git a/test/link/macho/bugs/13457/build.zig b/test/link/macho/bugs/13457/build.zig
index 6ca1f31b86..3560b4a168 100644
--- a/test/link/macho/bugs/13457/build.zig
+++ b/test/link/macho/bugs/13457/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/link/macho/dead_strip/build.zig b/test/link/macho/dead_strip/build.zig
index b6c3002492..d82c81edca 100644
--- a/test/link/macho/dead_strip/build.zig
+++ b/test/link/macho/dead_strip/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
@@ -36,7 +35,11 @@ pub fn build(b: *std.Build) void {
}
}
-fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn createScenario(
+ b: *std.Build,
+ optimize: std.builtin.OptimizeMode,
+ target: std.zig.CrossTarget,
+) *std.Build.CompileStep {
const exe = b.addExecutable(.{
.name = "test",
.optimize = optimize,
diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig
index f61b30ca4a..8b62cec6e6 100644
--- a/test/link/macho/dead_strip_dylibs/build.zig
+++ b/test/link/macho/dead_strip_dylibs/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
@@ -35,7 +34,7 @@ pub fn build(b: *std.Build) void {
}
}
-fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
+fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
const exe = b.addExecutable(.{
.name = "test",
.optimize = optimize,
diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig
index 2b3c6abb8a..3ef17573f8 100644
--- a/test/link/macho/headerpad/build.zig
+++ b/test/link/macho/headerpad/build.zig
@@ -1,6 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
@@ -93,7 +92,7 @@ pub fn build(b: *std.Build) void {
}
}
-fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep {
+fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
const exe = b.addExecutable(.{
.name = "main",
.optimize = optimize,
diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig
index 62b70b21f1..8b6e3dd87f 100644
--- a/test/link/macho/needed_framework/build.zig
+++ b/test/link/macho/needed_framework/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig
index cdad94357b..92a73d22b7 100644
--- a/test/link/macho/needed_library/build.zig
+++ b/test/link/macho/needed_library/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig
index eeda89446b..62757f885b 100644
--- a/test/link/macho/search_strategy/build.zig
+++ b/test/link/macho/search_strategy/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
@@ -35,7 +34,11 @@ pub fn build(b: *std.Build) void {
}
}
-fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn createScenario(
+ b: *std.Build,
+ optimize: std.builtin.OptimizeMode,
+ target: std.zig.CrossTarget,
+) *std.Build.CompileStep {
const static = b.addStaticLibrary(.{
.name = "a",
.optimize = optimize,
diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig
index 6eabc02b5f..408076657b 100644
--- a/test/link/macho/strict_validation/build.zig
+++ b/test/link/macho/strict_validation/build.zig
@@ -1,6 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig
index e43c002e2e..408f762f5d 100644
--- a/test/link/macho/unwind_info/build.zig
+++ b/test/link/macho/unwind_info/build.zig
@@ -1,6 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
@@ -51,7 +50,11 @@ fn testUnwindInfo(
test_step.dependOn(&run_cmd.step);
}
-fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn createScenario(
+ b: *std.Build,
+ optimize: std.builtin.OptimizeMode,
+ target: std.zig.CrossTarget,
+) *std.Build.CompileStep {
const exe = b.addExecutable(.{
.name = "test",
.optimize = optimize,
diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig
index 62c288f1a0..6a68263fbf 100644
--- a/test/link/macho/uuid/build.zig
+++ b/test/link/macho/uuid/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test");
@@ -51,7 +50,11 @@ fn testUuid(
}
}
-fn simpleDylib(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep {
+fn simpleDylib(
+ b: *std.Build,
+ optimize: std.builtin.OptimizeMode,
+ target: std.zig.CrossTarget,
+) *std.Build.CompileStep {
const dylib = b.addSharedLibrary(.{
.name = "test",
.version = .{ .major = 1, .minor = 0 },
diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig
index 5be66991dd..ca28458d77 100644
--- a/test/link/macho/weak_framework/build.zig
+++ b/test/link/macho/weak_framework/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig
index 505ab5ae96..de5aa45e30 100644
--- a/test/link/macho/weak_library/build.zig
+++ b/test/link/macho/weak_library/build.zig
@@ -1,5 +1,4 @@
const std = @import("std");
-const LibExeObjectStep = std.Build.LibExeObjStep;
pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
diff --git a/test/tests.zig b/test/tests.zig
index de25528dde..0f36c8eb94 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -8,7 +8,7 @@ const mem = std.mem;
const fmt = std.fmt;
const ArrayList = std.ArrayList;
const OptimizeMode = std.builtin.OptimizeMode;
-const LibExeObjStep = std.Build.LibExeObjStep;
+const CompileStep = std.Build.CompileStep;
const Allocator = mem.Allocator;
const ExecError = std.Build.ExecError;
const Step = std.Build.Step;
@@ -842,7 +842,7 @@ pub const StackTracesContext = struct {
step: Step,
context: *StackTracesContext,
- exe: *LibExeObjStep,
+ exe: *CompileStep,
name: []const u8,
optimize_mode: OptimizeMode,
expect_output: []const u8,
@@ -850,7 +850,7 @@ pub const StackTracesContext = struct {
pub fn create(
context: *StackTracesContext,
- exe: *LibExeObjStep,
+ exe: *CompileStep,
name: []const u8,
optimize_mode: OptimizeMode,
expect_output: []const u8,
@@ -1180,14 +1180,14 @@ pub const GenHContext = struct {
const GenHCmpOutputStep = struct {
step: Step,
context: *GenHContext,
- obj: *LibExeObjStep,
+ obj: *CompileStep,
name: []const u8,
test_index: usize,
case: *const TestCase,
pub fn create(
context: *GenHContext,
- obj: *LibExeObjStep,
+ obj: *CompileStep,
name: []const u8,
case: *const TestCase,
) *GenHCmpOutputStep {
--
cgit v1.2.3