feature. See also
. The project being documented here (as the example) is the Zig library itself.
File
Code
const Compile = @This();
const builtin = @import("builtin");
const std = @import("std");
const Io = std.Io;
const mem = std.mem;
const fs = std.fs;
const assert = std.debug.assert;
const panic = std.debug.panic;
const StringHashMap = std.StringHashMap;
const Allocator = std.mem.Allocator;
const Step = std.Build.Step;
const LazyPath = std.Build.LazyPath;
const Module = std.Build.Module;
const InstallDir = std.Build.InstallDir;
const Path = std.Build.Cache.Path;
const Configuration = std.Build.Configuration;
pub const base_tag: Step.Tag = .compile;
step: Step,
root_module: *Module,
name: []const u8,
linker_script: ?LazyPath = null,
version_script: ?LazyPath = null,
out_filename: []const u8,
linkage: ?std.builtin.LinkMode = null,
version: ?std.SemanticVersion,
kind: Kind,
formatted_panics: ?bool = null,
compress_debug_sections: std.zig.CompressDebugSections = .none,
verbose_link: bool,
verbose_cc: bool,
bundle_compiler_rt: ?bool = null,
bundle_ubsan_rt: ?bool = null,
rdynamic: bool,
import_memory: bool = false,
export_memory: bool = false,
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,
zig_lib_dir: ?LazyPath,
filters: []const []const u8,
test_runner: ?TestRunner,
wasi_exec_model: ?std.builtin.WasiExecModel = null,
installed_headers: std.ArrayList(HeaderInstallation),
installed_headers_include_tree: ?*Step.WriteFile = null,
rc_includes: std.zig.RcIncludes = .any,
win32_manifest: ?LazyPath = null,
win32_module_definition: ?LazyPath = null,
image_base: ?u64 = null,
libc_file: ?LazyPath = null,
each_lib_rpath: ?bool = null,
build_id: ?std.zig.BuildId = null,
link_eh_frame_hdr: bool = false,
link_emit_relocs: bool = false,
link_function_sections: bool = false,
link_data_sections: bool = false,
link_gc_sections: ?bool = null,
linker_dynamicbase: bool = true,
linker_allow_shlib_undefined: ?bool = null,
linker_allow_undefined_version: ?bool = null,
linker_enable_new_dtags: ?bool = null,
link_z_notext: bool = false,
link_z_relro: bool = true,
link_z_lazy: bool = false,
link_z_common_page_size: ?u64 = null,
link_z_max_page_size: ?u64 = null,
link_z_defs: bool = false,
install_name: ?[]const u8 = null,
entitlements: ?LazyPath = null,
pagezero_size: ?u64 = null,
headerpad_size: ?u32 = null,
headerpad_max_install_names: bool = false,
dead_strip_dylibs: bool = false,
force_load_objc: bool = false,
discard_local_symbols: bool = false,
pie: ?bool = null,
lto: ?std.zig.LtoMode = null,
dll_export_fns: ?bool = null,
subsystem: ?std.zig.Subsystem = null,
mingw_unicode_entry_point: bool = false,
entry: Entry = .default,
force_undefined_symbols: std.array_hash_map.String(void),
stack_size: ?u64 = null,
use_llvm: ?bool,
use_lld: ?bool,
use_new_linker: ?bool,
allow_so_scripts: ?bool = null,
expect_errors: ?ExpectedCompileErrors = null,
error_limit: ?u32 = null,
is_linking_libc: bool = false,
is_linking_libcpp: bool = false,
sanitize_coverage_trace_pc_guard: ?bool = null,
incremental: ?bool = null,
emit_directory: Configuration.OptionalGeneratedFileIndex = .none,
generated_docs: Configuration.OptionalGeneratedFileIndex = .none,
generated_asm: Configuration.OptionalGeneratedFileIndex = .none,
generated_bin: Configuration.OptionalGeneratedFileIndex = .none,
generated_pdb: Configuration.OptionalGeneratedFileIndex = .none,
generated_implib: Configuration.OptionalGeneratedFileIndex = .none,
generated_llvm_bc: Configuration.OptionalGeneratedFileIndex = .none,
generated_llvm_ir: Configuration.OptionalGeneratedFileIndex = .none,
generated_h: Configuration.OptionalGeneratedFileIndex = .none,
pub const ExpectedCompileErrors = union(enum) {
contains: []const u8,
exact: []const []const u8,
starts_with: []const u8,
stderr_contains: []const u8,
};
pub const Entry = union(enum) {
default,
disabled,
enabled,
symbol_name: []const u8,
};
pub const Options = struct {
name: []const u8,
root_module: *Module,
kind: Kind,
linkage: ?std.builtin.LinkMode = null,
version: ?std.SemanticVersion = null,
max_rss: u64 = 0,
filters: []const []const u8 = &.{},
test_runner: ?TestRunner = null,
use_llvm: ?bool = null,
use_lld: ?bool = null,
zig_lib_dir: ?LazyPath = null,
win32_manifest: ?LazyPath = null,
win32_module_definition: ?LazyPath = null,
entitlements: ?LazyPath = null,
};
pub const Kind = Configuration.Step.Compile.Kind;
pub const HeaderInstallation = union(enum) {
file: File,
directory: Directory,
pub const File = struct {
source: LazyPath,
dest_rel_path: []const u8,
pub fn dupe(file: File, graph: *const std.Build.Graph) File {
return .{
.source = file.source.dupe(graph),
.dest_rel_path = graph.dupePath(file.dest_rel_path),
};
}
};
pub const Directory = struct {
source: LazyPath,
dest_rel_path: []const u8,
options: Directory.Options,
pub const Options = struct {
exclude_extensions: []const []const u8 = &.{},
include_extensions: ?[]const []const u8 = &.{".h"},
pub fn dupe(opts: Directory.Options, graph: *const std.Build.Graph) Directory.Options {
return .{
.exclude_extensions = graph.dupeStrings(opts.exclude_extensions),
.include_extensions = if (opts.include_extensions) |incs| graph.dupeStrings(incs) else null,
};
}
};
pub fn dupe(dir: Directory, graph: *const std.Build.Graph) Directory {
return .{
.source = dir.source.dupe(graph),
.dest_rel_path = graph.dupePath(dir.dest_rel_path),
.options = dir.options.dupe(graph),
};
}
};
pub fn getSource(installation: HeaderInstallation) LazyPath {
return switch (installation) {
inline .file, .directory => |x| x.source,
};
}
pub fn dupe(installation: HeaderInstallation, graph: *const std.Build.Graph) HeaderInstallation {
return switch (installation) {
.file => |f| .{ .file = f.dupe(graph) },
.directory => |d| .{ .directory = d.dupe(graph) },
};
}
};
pub const TestRunner = struct {
path: LazyPath,
mode: enum { simple, server },
};
pub fn create(owner: *std.Build, options: Options) *Compile {
const graph = owner.graph;
const arena = graph.arena;
const name = owner.dupe(options.name);
if (mem.find(u8, name, "/") != null or mem.find(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 resolved_target = options.root_module.resolved_target orelse
@panic("the root Module of a Compile step must be created with a known 'target' field");
const target = &resolved_target.result;
const step_name = owner.fmt("compile {s} {s} {s}", .{
if (options.kind.isTest() and mem.eql(u8, name, "test"))
@tagName(options.kind)
else
owner.fmt("{t} {s}", .{ options.kind, name }),
@tagName(options.root_module.optimize orelse .debug),
resolved_target.query.zigTriple(arena) catch @panic("OOM"),
});
const out_filename = std.zig.binNameAlloc(arena, .{
.root_name = name,
.cpu_arch = target.cpu.arch,
.os_tag = target.os.tag,
.ofmt = target.ofmt,
.abi = target.abi,
.output_mode = switch (options.kind) {
.lib => .Lib,
.obj, .test_obj => .Obj,
.exe, .@"test" => .Exe,
},
.link_mode = options.linkage,
.version = options.version,
}) catch @panic("OOM");
const compile = arena.create(Compile) catch @panic("OOM");
compile.* = .{
.root_module = options.root_module,
.verbose_link = false,
.verbose_cc = false,
.linkage = options.linkage,
.kind = options.kind,
.name = name,
.step = .init(.{
.tag = base_tag,
.name = step_name,
.owner = owner,
.max_rss = options.max_rss,
}),
.version = options.version,
.out_filename = out_filename,
.installed_headers = .empty,
.zig_lib_dir = null,
.filters = options.filters,
.test_runner = null,
.rdynamic = false,
.force_undefined_symbols = .empty,
.use_llvm = options.use_llvm,
.use_lld = options.use_lld,
.use_new_linker = null,
};
if (options.zig_lib_dir) |lp| {
compile.zig_lib_dir = lp.dupe(graph);
lp.addStepDependencies(&compile.step);
}
if (options.test_runner) |runner| {
compile.test_runner = .{
.path = runner.path.dupe(graph),
.mode = runner.mode,
};
runner.path.addStepDependencies(&compile.step);
}
// gets embedded, so for any other target the manifest file is just ignored.
if (target.ofmt == .coff) {
if (options.win32_manifest) |lp| {
compile.win32_manifest = lp.dupe(graph);
lp.addStepDependencies(&compile.step);
}
if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) {
if (options.win32_module_definition) |lp| {
compile.win32_module_definition = lp.dupe(graph);
lp.addStepDependencies(&compile.step);
}
}
}
if (options.entitlements) |lp| {
compile.entitlements = lp.dupe(graph);
lp.addStepDependencies(&compile.step);
}
return compile;
}
pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void {
const graph = cs.step.owner.graph;
const arena = graph.arena;
const installation: HeaderInstallation = .{ .file = .{
.source = source.dupe(graph),
.dest_rel_path = graph.dupePath(dest_rel_path),
} };
cs.installed_headers.append(arena, installation) catch @panic("OOM");
cs.addHeaderInstallationToIncludeTree(installation);
installation.getSource().addStepDependencies(&cs.step);
}
pub fn installHeadersDirectory(
cs: *Compile,
source: LazyPath,
dest_rel_path: []const u8,
options: HeaderInstallation.Directory.Options,
) void {
const graph = cs.step.owner.graph;
const arena = graph.arena;
const installation: HeaderInstallation = .{ .directory = .{
.source = source.dupe(graph),
.dest_rel_path = graph.dupePath(dest_rel_path),
.options = options.dupe(graph),
} };
cs.installed_headers.append(arena, installation) catch @panic("OOM");
cs.addHeaderInstallationToIncludeTree(installation);
installation.getSource().addStepDependencies(&cs.step);
}
pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void {
cs.installHeader(config_header.getOutputFile(), config_header.include_path);
}
pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void {
assert(lib.kind == .lib);
const graph = cs.step.owner.graph;
const arena = graph.arena;
for (lib.installed_headers.items) |installation| {
const installation_copy = installation.dupe(graph);
cs.installed_headers.append(arena, installation_copy) catch @panic("OOM");
cs.addHeaderInstallationToIncludeTree(installation_copy);
installation_copy.getSource().addStepDependencies(&cs.step);
}
}
fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstallation) void {
if (cs.installed_headers_include_tree) |wf| switch (installation) {
.file => |file| {
_ = wf.addCopyFile(file.source, file.dest_rel_path);
},
.directory => |dir| {
_ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{
.exclude_extensions = dir.options.exclude_extensions,
.include_extensions = dir.options.include_extensions,
});
},
};
}
pub fn getEmittedIncludeTree(cs: *Compile) LazyPath {
if (cs.installed_headers_include_tree) |wf| return wf.getDirectory();
const b = cs.step.owner;
const wf = b.addWriteFiles();
cs.installed_headers_include_tree = wf;
for (cs.installed_headers.items) |installation| {
cs.addHeaderInstallationToIncludeTree(installation);
}
// only dependent modules do.
return wf.getDirectory();
}
pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {
const b = cs.step.owner;
var copy = options;
if (copy.basename == null) {
if (options.format) |f| {
copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) });
} else {
copy.basename = cs.name;
}
}
return b.addObjCopy(cs.getEmittedBin(), copy);
}
pub fn setLinkerScript(compile: *Compile, source: LazyPath) void {
const graph = compile.step.owner.graph;
compile.linker_script = source.dupe(graph);
source.addStepDependencies(&compile.step);
}
pub fn setVersionScript(compile: *Compile, source: LazyPath) void {
const graph = compile.step.owner.graph;
compile.version_script = source.dupe(graph);
source.addStepDependencies(&compile.step);
}
pub fn forceUndefinedSymbol(compile: *Compile, symbol_name: []const u8) void {
const graph = compile.step.owner.graph;
const arena = graph.arena;
compile.force_undefined_symbols.put(arena, graph.dupeString(symbol_name), {}) catch @panic("OOM");
}
pub fn dependsOnSystemLibrary(compile: *Compile, name: []const u8) bool {
var is_linking_libc = false;
var is_linking_libcpp = false;
for (compile.getCompileDependencies(true)) |some_compile| {
for (some_compile.root_module.getGraph().modules) |mod| {
for (mod.link_objects.items) |lo| {
switch (lo) {
.system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
else => {},
}
}
if (mod.link_libc orelse false) is_linking_libc = true;
if (mod.link_libcpp orelse false) is_linking_libcpp = true;
}
}
const target = compile.rootModuleTarget();
if (std.zig.target.isLibCLibName(&target, name)) {
return is_linking_libc;
}
if (std.zig.target.isLibCxxLibName(&target, name)) {
return is_linking_libcpp;
}
return false;
}
pub fn isDynamicLibrary(compile: *const Compile) bool {
return compile.kind == .lib and compile.linkage == .dynamic;
}
pub fn isStaticLibrary(compile: *const Compile) bool {
return compile.kind == .lib and compile.linkage != .dynamic;
}
pub fn isDll(compile: *Compile) bool {
return compile.isDynamicLibrary() and compile.rootModuleTarget().os.tag == .windows;
}
pub fn producesPdbFile(compile: *Compile) bool {
const target = compile.rootModuleTarget();
// TODO: just share this logic with the compiler, silly!
switch (target.os.tag) {
.windows, .uefi => {},
else => return false,
}
if (target.ofmt == .c) return false;
if (compile.use_llvm == false) return false;
if (compile.root_module.strip == true or
(compile.root_module.strip == null and compile.root_module.optimize == .small))
{
return false;
}
return compile.isDynamicLibrary() or compile.kind == .exe or compile.kind == .@"test";
}
pub fn producesCompilerRtDynLib(compile: *Compile) bool {
if (compile.rootModuleTarget().ofmt != .coff) return false;
if (compile.bundle_compiler_rt orelse (compile.kind == .exe or compile.isDynamicLibrary()))
return compile.use_llvm == false;
return false;
}
pub fn producesImplib(compile: *Compile) bool {
return compile.isDll();
}
pub fn setVerboseLink(compile: *Compile, value: bool) void {
compile.verbose_link = value;
}
pub fn setVerboseCC(compile: *Compile, value: bool) void {
compile.verbose_cc = value;
}
pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void {
const graph = compile.step.owner.graph;
if (libc_file) |f| {
compile.libc_file = f.dupe(graph);
f.addStepDependencies(&compile.step);
} else {
compile.libc_file = null;
}
}
fn getEmittedFileGeneric(compile: *Compile, output_file: *Configuration.OptionalGeneratedFileIndex) LazyPath {
if (output_file.unwrap()) |index| return .{ .generated = .{ .index = index } };
const graph = compile.step.owner.graph;
const index = graph.addGeneratedFile(&compile.step);
output_file.* = .init(index);
return .{ .generated = .{ .index = index } };
}
pub fn getEmittedBinDirectory(compile: *Compile) LazyPath {
_ = compile.getEmittedBin();
return compile.getEmittedFileGeneric(&compile.emit_directory);
}
pub fn getEmittedBin(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_bin);
}
pub fn getEmittedImplib(compile: *Compile) LazyPath {
assert(compile.kind == .lib);
return compile.getEmittedFileGeneric(&compile.generated_implib);
}
pub fn getEmittedH(compile: *Compile) LazyPath {
assert(compile.kind != .exe and compile.kind != .@"test");
return compile.getEmittedFileGeneric(&compile.generated_h);
}
pub fn getEmittedPdb(compile: *Compile) LazyPath {
_ = compile.getEmittedBin();
return compile.getEmittedFileGeneric(&compile.generated_pdb);
}
pub fn getEmittedDocs(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_docs);
}
pub fn getEmittedAsm(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_asm);
}
pub fn getEmittedLlvmIr(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_llvm_ir);
}
pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
}
pub fn rootModuleTarget(c: *Compile) std.Target {
return c.root_module.resolved_target.?.result;
}
pub fn getCompileDependencies(start: *Compile, chase_dynamic: bool) []const *Compile {
const arena = start.step.owner.graph.arena;
var compiles: std.array_hash_map.Auto(*Compile, void) = .empty;
var next_idx: usize = 0;
compiles.putNoClobber(arena, start, {}) catch @panic("OOM");
while (next_idx < compiles.count()) {
const compile = compiles.keys()[next_idx];
next_idx += 1;
for (compile.root_module.getGraph().modules) |mod| {
for (mod.link_objects.items) |lo| {
switch (lo) {
.other_step => |other_compile| {
if (!chase_dynamic and other_compile.isDynamicLibrary()) continue;
compiles.put(arena, other_compile, {}) catch @panic("OOM");
},
else => {},
}
}
}
}
return compiles.keys();
}