mirror of
https://github.com/ziglang/zig.git
synced 2024-11-21 19:42:56 +00:00
Merge 097a1777f0
into f845fa04a0
This commit is contained in:
commit
a5d85cdf90
@ -339,6 +339,10 @@ pub fn main() !void {
|
||||
try builder.runBuild(root);
|
||||
}
|
||||
|
||||
for (builder.top_level_steps.values()) |s| {
|
||||
try finalizeSteps(&s.step);
|
||||
}
|
||||
|
||||
if (graph.needed_lazy_dependencies.entries.len != 0) {
|
||||
var buffer: std.ArrayListUnmanaged(u8) = .empty;
|
||||
for (graph.needed_lazy_dependencies.keys()) |k| {
|
||||
@ -636,6 +640,7 @@ fn runStepNames(
|
||||
test_count += s.test_results.test_count;
|
||||
|
||||
switch (s.state) {
|
||||
.unfinalized => unreachable,
|
||||
.precheck_unstarted => unreachable,
|
||||
.precheck_started => unreachable,
|
||||
.running => unreachable,
|
||||
@ -781,6 +786,7 @@ fn printStepStatus(
|
||||
run: *const Run,
|
||||
) !void {
|
||||
switch (s.state) {
|
||||
.unfinalized => unreachable,
|
||||
.precheck_unstarted => unreachable,
|
||||
.precheck_started => unreachable,
|
||||
.precheck_done => unreachable,
|
||||
@ -978,6 +984,34 @@ fn printTreeStep(
|
||||
}
|
||||
}
|
||||
|
||||
/// Traverse the dependency graph after the user build() call,
|
||||
/// this allows for checks and postprocessing after the steps are fully configured by the user.
|
||||
fn finalizeSteps(
|
||||
s: *Step,
|
||||
) !void {
|
||||
switch (s.state) {
|
||||
.unfinalized => {
|
||||
try s.finalize();
|
||||
s.state = .precheck_unstarted;
|
||||
|
||||
for (s.dependencies.items) |dep| {
|
||||
try finalizeSteps(dep);
|
||||
}
|
||||
},
|
||||
|
||||
.precheck_unstarted => {},
|
||||
|
||||
.precheck_started => unreachable,
|
||||
.precheck_done => unreachable,
|
||||
.dependency_failure => unreachable,
|
||||
.running => unreachable,
|
||||
.success => unreachable,
|
||||
.failure => unreachable,
|
||||
.skipped => unreachable,
|
||||
.skipped_oom => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Traverse the dependency graph depth-first and make it undirected by having
|
||||
/// steps know their dependants (they only know dependencies at start).
|
||||
/// Along the way, check that there is no dependency loop, and record the steps
|
||||
@ -996,6 +1030,7 @@ fn constructGraphAndCheckForDependencyLoop(
|
||||
rand: std.Random,
|
||||
) !void {
|
||||
switch (s.state) {
|
||||
.unfinalized => unreachable,
|
||||
.precheck_started => {
|
||||
std.debug.print("dependency loop detected:\n {s}\n", .{s.name});
|
||||
return error.DependencyLoopDetected;
|
||||
@ -1058,6 +1093,7 @@ fn workerMakeOneStep(
|
||||
// dependency is not finished yet.
|
||||
return;
|
||||
},
|
||||
.unfinalized => unreachable,
|
||||
.precheck_unstarted => unreachable,
|
||||
.precheck_started => unreachable,
|
||||
}
|
||||
|
@ -796,6 +796,31 @@ pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile {
|
||||
});
|
||||
}
|
||||
|
||||
pub const PchOptions = struct {
|
||||
name: []const u8,
|
||||
target: ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
max_rss: usize = 0,
|
||||
link_libc: ?bool = null,
|
||||
link_libcpp: ?bool = null,
|
||||
};
|
||||
|
||||
pub fn addPrecompiledCHeader(b: *Build, options: PchOptions, source: Module.CSourceFile) *Step.Compile {
|
||||
const pch = Step.Compile.create(b, .{
|
||||
.name = options.name,
|
||||
.root_module = .{
|
||||
.target = options.target,
|
||||
.optimize = options.optimize,
|
||||
.link_libc = options.link_libc,
|
||||
.link_libcpp = options.link_libcpp,
|
||||
},
|
||||
.kind = .pch,
|
||||
.max_rss = options.max_rss,
|
||||
});
|
||||
pch.addCSourceFile(source);
|
||||
return pch;
|
||||
}
|
||||
|
||||
pub const SharedLibraryOptions = struct {
|
||||
name: []const u8,
|
||||
/// To choose the same computer as the one building the package, pass the
|
||||
|
@ -46,11 +46,36 @@ pub const RPath = union(enum) {
|
||||
special: []const u8,
|
||||
};
|
||||
|
||||
// subset of Compilation.FileExt
|
||||
pub const AsmSourceLang = enum {
|
||||
assembly,
|
||||
assembly_with_cpp,
|
||||
|
||||
pub fn getLangName(lang: @This()) []const u8 {
|
||||
return switch (lang) {
|
||||
.assembly => "assembler",
|
||||
.assembly_with_cpp => "assembler-with-cpp",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const AsmSourceFile = struct {
|
||||
file: LazyPath,
|
||||
lang: ?AsmSourceLang = null,
|
||||
|
||||
pub fn dupe(file: AsmSourceFile, b: *std.Build) AsmSourceFile {
|
||||
return .{
|
||||
.file = file.file.dupe(b),
|
||||
.lang = file.lang,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const LinkObject = union(enum) {
|
||||
static_path: LazyPath,
|
||||
other_step: *Step.Compile,
|
||||
system_lib: SystemLib,
|
||||
assembly_file: LazyPath,
|
||||
assembly_file: *AsmSourceFile,
|
||||
c_source_file: *CSourceFile,
|
||||
c_source_files: *CSourceFiles,
|
||||
win32_resource_file: *RcSourceFile,
|
||||
@ -78,22 +103,87 @@ pub const SystemLib = struct {
|
||||
pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
|
||||
};
|
||||
|
||||
/// Supported languages for "zig clang -x <lang>".
|
||||
// subset of Compilation.FileExt
|
||||
pub const CSourceLang = enum {
|
||||
/// "c"
|
||||
c,
|
||||
/// "c-header"
|
||||
h,
|
||||
/// "c++"
|
||||
cpp,
|
||||
/// "c++-header"
|
||||
hpp,
|
||||
/// "objective-c"
|
||||
m,
|
||||
/// "objective-c-header"
|
||||
hm,
|
||||
/// "objective-c++"
|
||||
mm,
|
||||
/// "objective-c++-header"
|
||||
hmm,
|
||||
/// "assembler"
|
||||
assembly,
|
||||
/// "assembler-with-cpp"
|
||||
assembly_with_cpp,
|
||||
/// "cuda"
|
||||
cu,
|
||||
|
||||
pub fn getLangName(lang: @This()) []const u8 {
|
||||
return switch (lang) {
|
||||
.assembly => "assembler",
|
||||
.assembly_with_cpp => "assembler-with-cpp",
|
||||
.c => "c",
|
||||
.cpp => "c++",
|
||||
.h => "c-header",
|
||||
.hpp => "c++-header",
|
||||
.hm => "objective-c-header",
|
||||
.hmm => "objective-c++-header",
|
||||
.cu => "cuda",
|
||||
.m => "objective-c",
|
||||
.mm => "objective-c++",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const PrecompiledHeader = union(enum) {
|
||||
/// automatically create the PCH compile step for the source header file,
|
||||
/// inheriting the options from the parent compile step.
|
||||
source_header: struct { path: LazyPath, lang: ?CSourceLang = null },
|
||||
|
||||
/// final PCH compile step,
|
||||
/// can be provided by the user or else will be created from the `source_header` field during step finalization.
|
||||
pch_step: *Step.Compile,
|
||||
|
||||
pub fn getPath(pch: PrecompiledHeader, b: *std.Build) []const u8 {
|
||||
switch (pch) {
|
||||
.source_header => unreachable,
|
||||
.pch_step => |pch_step| return pch_step.getEmittedBin().getPath(b),
|
||||
}
|
||||
}
|
||||
};
|
||||
pub const CSourceFiles = struct {
|
||||
root: LazyPath,
|
||||
/// `files` is relative to `root`, which is
|
||||
/// the build root by default
|
||||
files: []const []const u8,
|
||||
lang: ?CSourceLang = null,
|
||||
flags: []const []const u8,
|
||||
precompiled_header: ?PrecompiledHeader = null,
|
||||
};
|
||||
|
||||
pub const CSourceFile = struct {
|
||||
file: LazyPath,
|
||||
lang: ?CSourceLang = null,
|
||||
flags: []const []const u8 = &.{},
|
||||
precompiled_header: ?PrecompiledHeader = null,
|
||||
|
||||
pub fn dupe(file: CSourceFile, b: *std.Build) CSourceFile {
|
||||
return .{
|
||||
.file = file.file.dupe(b),
|
||||
.lang = file.lang,
|
||||
.flags = b.dupeStrings(file.flags),
|
||||
.precompiled_header = file.precompiled_header,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -324,10 +414,9 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void {
|
||||
addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree());
|
||||
},
|
||||
|
||||
.static_path,
|
||||
.assembly_file,
|
||||
=> |lp| addLazyPathDependencies(m, dependee, lp),
|
||||
.static_path => |lp| addLazyPathDependencies(m, dependee, lp),
|
||||
|
||||
.assembly_file => |x| addLazyPathDependencies(m, dependee, x.file),
|
||||
.c_source_file => |x| addLazyPathDependencies(m, dependee, x.file),
|
||||
.win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file),
|
||||
|
||||
@ -361,7 +450,7 @@ fn addStepDependencies(m: *Module, module: *Module, dependee: *Step) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn addStepDependenciesOnly(m: *Module, dependee: *Step) void {
|
||||
pub fn addStepDependenciesOnly(m: *Module, dependee: *Step) void {
|
||||
for (m.depending_steps.keys()) |compile| {
|
||||
compile.step.dependOn(dependee);
|
||||
}
|
||||
@ -523,7 +612,9 @@ pub const AddCSourceFilesOptions = struct {
|
||||
/// package that owns the `Compile` step.
|
||||
root: ?LazyPath = null,
|
||||
files: []const []const u8,
|
||||
lang: ?CSourceLang = null,
|
||||
flags: []const []const u8 = &.{},
|
||||
precompiled_header: ?PrecompiledHeader = null,
|
||||
};
|
||||
|
||||
/// Handy when you have many C/C++ source files and want them all to have the same flags.
|
||||
@ -544,10 +635,22 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
|
||||
c_source_files.* = .{
|
||||
.root = options.root orelse b.path(""),
|
||||
.files = b.dupeStrings(options.files),
|
||||
.lang = options.lang,
|
||||
.flags = b.dupeStrings(options.flags),
|
||||
.precompiled_header = options.precompiled_header,
|
||||
};
|
||||
m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
|
||||
addLazyPathDependenciesOnly(m, c_source_files.root);
|
||||
|
||||
if (options.precompiled_header) |pch| {
|
||||
switch (pch) {
|
||||
.source_header => |src| addLazyPathDependenciesOnly(m, src.path),
|
||||
.pch_step => |step| {
|
||||
_ = step.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
|
||||
addStepDependenciesOnly(m, &step.step);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
|
||||
@ -557,6 +660,16 @@ pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
|
||||
c_source_file.* = source.dupe(b);
|
||||
m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM");
|
||||
addLazyPathDependenciesOnly(m, source.file);
|
||||
|
||||
if (source.precompiled_header) |pch| {
|
||||
switch (pch) {
|
||||
.source_header => |src| addLazyPathDependenciesOnly(m, src.path),
|
||||
.pch_step => |step| {
|
||||
_ = step.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
|
||||
addStepDependenciesOnly(m, &step.step);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource files must have the extension `.rc`.
|
||||
@ -579,10 +692,12 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
|
||||
pub fn addAssemblyFile(m: *Module, source: AsmSourceFile) void {
|
||||
const b = m.owner;
|
||||
m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
|
||||
addLazyPathDependenciesOnly(m, source);
|
||||
const source_file = b.allocator.create(AsmSourceFile) catch @panic("OOM");
|
||||
source_file.* = source.dupe(b);
|
||||
m.link_objects.append(b.allocator, .{ .assembly_file = source_file }) catch @panic("OOM");
|
||||
addLazyPathDependenciesOnly(m, source.file);
|
||||
}
|
||||
|
||||
pub fn addObjectFile(m: *Module, object: LazyPath) void {
|
||||
|
@ -1,6 +1,7 @@
|
||||
id: Id,
|
||||
name: []const u8,
|
||||
owner: *Build,
|
||||
finalizeFn: FinalizeFn,
|
||||
makeFn: MakeFn,
|
||||
|
||||
dependencies: std.ArrayList(*Step),
|
||||
@ -68,6 +69,8 @@ pub const TestResults = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const FinalizeFn = *const fn (step: *Step) anyerror!void;
|
||||
|
||||
pub const MakeOptions = struct {
|
||||
progress_node: std.Progress.Node,
|
||||
thread_pool: *std.Thread.Pool,
|
||||
@ -77,6 +80,7 @@ pub const MakeOptions = struct {
|
||||
pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void;
|
||||
|
||||
pub const State = enum {
|
||||
unfinalized,
|
||||
precheck_unstarted,
|
||||
precheck_started,
|
||||
/// This is also used to indicate "dirty" steps that have been modified
|
||||
@ -183,6 +187,7 @@ pub const StepOptions = struct {
|
||||
id: Id,
|
||||
name: []const u8,
|
||||
owner: *Build,
|
||||
finalizeFn: FinalizeFn = finalizeNoOp,
|
||||
makeFn: MakeFn = makeNoOp,
|
||||
first_ret_addr: ?usize = null,
|
||||
max_rss: usize = 0,
|
||||
@ -195,11 +200,12 @@ pub fn init(options: StepOptions) Step {
|
||||
.id = options.id,
|
||||
.name = arena.dupe(u8, options.name) catch @panic("OOM"),
|
||||
.owner = options.owner,
|
||||
.finalizeFn = options.finalizeFn,
|
||||
.makeFn = options.makeFn,
|
||||
.dependencies = std.ArrayList(*Step).init(arena),
|
||||
.dependants = .{},
|
||||
.inputs = Inputs.init,
|
||||
.state = .precheck_unstarted,
|
||||
.state = .unfinalized,
|
||||
.max_rss = options.max_rss,
|
||||
.debug_stack_trace = blk: {
|
||||
const addresses = arena.alloc(usize, options.owner.debug_stack_frames_count) catch @panic("OOM");
|
||||
@ -222,6 +228,11 @@ pub fn init(options: StepOptions) Step {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn finalize(s: *Step) !void {
|
||||
assert(s.state == .unfinalized);
|
||||
try s.finalizeFn(s);
|
||||
}
|
||||
|
||||
/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
|
||||
/// have already reported the error. Otherwise, we add a simple error report
|
||||
/// here.
|
||||
@ -266,6 +277,10 @@ pub fn getStackTrace(s: *Step) ?std.builtin.StackTrace {
|
||||
};
|
||||
}
|
||||
|
||||
fn finalizeNoOp(step: *Step) anyerror!void {
|
||||
_ = step;
|
||||
}
|
||||
|
||||
fn makeNoOp(step: *Step, options: MakeOptions) anyerror!void {
|
||||
_ = options;
|
||||
|
||||
|
@ -216,9 +216,9 @@ generated_h: ?*GeneratedFile,
|
||||
/// Defaults to `std.math.maxInt(u16)`
|
||||
error_limit: ?u32 = null,
|
||||
|
||||
/// Computed during make().
|
||||
/// Computed during finalize().
|
||||
is_linking_libc: bool = false,
|
||||
/// Computed during make().
|
||||
/// Computed during finalize().
|
||||
is_linking_libcpp: bool = false,
|
||||
|
||||
no_builtin: bool = false,
|
||||
@ -284,6 +284,7 @@ pub const Kind = enum {
|
||||
exe,
|
||||
lib,
|
||||
obj,
|
||||
pch,
|
||||
@"test",
|
||||
};
|
||||
|
||||
@ -367,6 +368,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
|
||||
.exe => "zig build-exe",
|
||||
.lib => "zig build-lib",
|
||||
.obj => "zig build-obj",
|
||||
.pch => "zig build-pch",
|
||||
.@"test" => "zig test",
|
||||
},
|
||||
name_adjusted,
|
||||
@ -374,17 +376,21 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
|
||||
resolved_target.query.zigTriple(owner.allocator) catch @panic("OOM"),
|
||||
});
|
||||
|
||||
const out_filename = std.zig.binNameAlloc(owner.allocator, .{
|
||||
.root_name = name,
|
||||
.target = target,
|
||||
.output_mode = switch (options.kind) {
|
||||
.lib => .Lib,
|
||||
.obj => .Obj,
|
||||
.exe, .@"test" => .Exe,
|
||||
},
|
||||
.link_mode = options.linkage,
|
||||
.version = options.version,
|
||||
}) catch @panic("OOM");
|
||||
const out_filename = if (options.kind == .pch)
|
||||
std.fmt.allocPrint(owner.allocator, "{s}.pch", .{name}) catch @panic("OOM")
|
||||
else
|
||||
std.zig.binNameAlloc(owner.allocator, .{
|
||||
.root_name = name,
|
||||
.target = target,
|
||||
.output_mode = switch (options.kind) {
|
||||
.lib => .Lib,
|
||||
.obj => .Obj,
|
||||
.exe, .@"test" => .Exe,
|
||||
.pch => unreachable,
|
||||
},
|
||||
.link_mode = options.linkage,
|
||||
.version = options.version,
|
||||
}) catch @panic("OOM");
|
||||
|
||||
const compile = owner.allocator.create(Compile) catch @panic("OOM");
|
||||
compile.* = .{
|
||||
@ -398,6 +404,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
|
||||
.id = base_id,
|
||||
.name = step_name,
|
||||
.owner = owner,
|
||||
.finalizeFn = finalize,
|
||||
.makeFn = make,
|
||||
.max_rss = options.max_rss,
|
||||
}),
|
||||
@ -815,10 +822,12 @@ pub fn linkFrameworkWeak(c: *Compile, name: []const u8) void {
|
||||
|
||||
/// Handy when you have many C/C++ source files and want them all to have the same flags.
|
||||
pub fn addCSourceFiles(compile: *Compile, options: Module.AddCSourceFilesOptions) void {
|
||||
assert(compile.kind != .pch); // pch can only be generated from a single C header file
|
||||
compile.root_module.addCSourceFiles(options);
|
||||
}
|
||||
|
||||
pub fn addCSourceFile(compile: *Compile, source: Module.CSourceFile) void {
|
||||
assert(compile.kind != .pch or compile.root_module.link_objects.items.len == 0); // pch can only be generated from a single C header file
|
||||
compile.root_module.addCSourceFile(source);
|
||||
}
|
||||
|
||||
@ -826,6 +835,7 @@ pub fn addCSourceFile(compile: *Compile, source: Module.CSourceFile) void {
|
||||
/// Can be called regardless of target. The .rc file will be ignored
|
||||
/// if the target object format does not support embedded resources.
|
||||
pub fn addWin32ResourceFile(compile: *Compile, source: Module.RcSourceFile) void {
|
||||
assert(compile.kind != .pch); // pch can only be generated from a single C header file
|
||||
compile.root_module.addWin32ResourceFile(source);
|
||||
}
|
||||
|
||||
@ -905,15 +915,18 @@ pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
|
||||
return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
|
||||
}
|
||||
|
||||
pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
|
||||
pub fn addAssemblyFile(compile: *Compile, source: Module.AsmSourceFile) void {
|
||||
assert(compile.kind != .pch); // pch can only be generated from a single C header file
|
||||
compile.root_module.addAssemblyFile(source);
|
||||
}
|
||||
|
||||
pub fn addObjectFile(compile: *Compile, source: LazyPath) void {
|
||||
assert(compile.kind != .pch); // pch can only be generated from a single C header file
|
||||
compile.root_module.addObjectFile(source);
|
||||
}
|
||||
|
||||
pub fn addObject(compile: *Compile, object: *Compile) void {
|
||||
assert(compile.kind != .pch); // pch can only be generated from a single C header file
|
||||
compile.root_module.addObject(object);
|
||||
}
|
||||
|
||||
@ -1038,6 +1051,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
.lib => "build-lib",
|
||||
.exe => "build-exe",
|
||||
.obj => "build-obj",
|
||||
.pch => "build-pch",
|
||||
.@"test" => "test",
|
||||
};
|
||||
try zig_args.append(cmd);
|
||||
@ -1089,22 +1103,13 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
|
||||
var prev_has_cflags = false;
|
||||
var prev_has_rcflags = false;
|
||||
var prev_has_xflag = false;
|
||||
var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first;
|
||||
var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
|
||||
// Track the number of positional arguments so that a nice error can be
|
||||
// emitted if there is nothing to link.
|
||||
var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);
|
||||
|
||||
{
|
||||
// Fully recursive iteration including dynamic libraries to detect
|
||||
// libc and libc++ linkage.
|
||||
var dep_it = compile.root_module.iterateDependencies(compile, true);
|
||||
while (dep_it.next()) |key| {
|
||||
if (key.module.link_libc == true) compile.is_linking_libc = true;
|
||||
if (key.module.link_libcpp == true) compile.is_linking_libcpp = true;
|
||||
}
|
||||
}
|
||||
|
||||
var cli_named_modules = try CliNamedModules.init(arena, &compile.root_module);
|
||||
|
||||
// For this loop, don't chase dynamic libraries because their link
|
||||
@ -1127,6 +1132,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
|
||||
// Inherit dependencies on system libraries and static libraries.
|
||||
for (dep.module.link_objects.items) |link_object| {
|
||||
if (prev_has_xflag and link_object != .c_source_file and link_object != .c_source_files and link_object != .assembly_file) {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append("none");
|
||||
prev_has_xflag = false;
|
||||
}
|
||||
|
||||
switch (link_object) {
|
||||
.static_path => |static_path| {
|
||||
if (my_responsibility) {
|
||||
@ -1210,6 +1221,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
switch (other.kind) {
|
||||
.exe => return step.fail("cannot link with an executable build artifact", .{}),
|
||||
.@"test" => return step.fail("cannot link with a test", .{}),
|
||||
.pch => @panic("Cannot link with a precompiled header file"),
|
||||
.obj => {
|
||||
const included_in_lib_or_obj = !my_responsibility and
|
||||
(dep.compile.?.kind == .lib or dep.compile.?.kind == .obj);
|
||||
@ -1257,14 +1269,25 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
try zig_args.append("--");
|
||||
prev_has_cflags = false;
|
||||
}
|
||||
try zig_args.append(asm_file.getPath2(dep.module.owner, step));
|
||||
|
||||
if (asm_file.lang) |lang| {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append(lang.getLangName());
|
||||
prev_has_xflag = true;
|
||||
} else if (prev_has_xflag) {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append("none");
|
||||
prev_has_xflag = false;
|
||||
}
|
||||
|
||||
try zig_args.append(asm_file.file.getPath2(dep.module.owner, step));
|
||||
total_linker_objects += 1;
|
||||
},
|
||||
|
||||
.c_source_file => |c_source_file| l: {
|
||||
if (!my_responsibility) break :l;
|
||||
|
||||
if (c_source_file.flags.len == 0) {
|
||||
if (c_source_file.flags.len == 0 and c_source_file.precompiled_header == null) {
|
||||
if (prev_has_cflags) {
|
||||
try zig_args.append("-cflags");
|
||||
try zig_args.append("--");
|
||||
@ -1275,9 +1298,25 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
for (c_source_file.flags) |arg| {
|
||||
try zig_args.append(arg);
|
||||
}
|
||||
if (c_source_file.precompiled_header) |pch| {
|
||||
try zig_args.append("-include-pch");
|
||||
try zig_args.append(pch.getPath(b));
|
||||
try zig_args.append("-fpch-validate-input-files-content");
|
||||
}
|
||||
try zig_args.append("--");
|
||||
prev_has_cflags = true;
|
||||
}
|
||||
|
||||
if (c_source_file.lang) |lang| {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append(lang.getLangName());
|
||||
prev_has_xflag = true;
|
||||
} else if (prev_has_xflag) {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append("none");
|
||||
prev_has_xflag = false;
|
||||
}
|
||||
|
||||
try zig_args.append(c_source_file.file.getPath2(dep.module.owner, step));
|
||||
total_linker_objects += 1;
|
||||
},
|
||||
@ -1285,7 +1324,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
.c_source_files => |c_source_files| l: {
|
||||
if (!my_responsibility) break :l;
|
||||
|
||||
if (c_source_files.flags.len == 0) {
|
||||
if (c_source_files.flags.len == 0 and c_source_files.precompiled_header == null) {
|
||||
if (prev_has_cflags) {
|
||||
try zig_args.append("-cflags");
|
||||
try zig_args.append("--");
|
||||
@ -1296,10 +1335,27 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
for (c_source_files.flags) |flag| {
|
||||
try zig_args.append(flag);
|
||||
}
|
||||
|
||||
if (c_source_files.precompiled_header) |pch| {
|
||||
try zig_args.append("-include-pch");
|
||||
try zig_args.append(pch.getPath(b));
|
||||
try zig_args.append("-fpch-validate-input-files-content");
|
||||
}
|
||||
|
||||
try zig_args.append("--");
|
||||
prev_has_cflags = true;
|
||||
}
|
||||
|
||||
if (c_source_files.lang) |lang| {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append(lang.getLangName());
|
||||
prev_has_xflag = true;
|
||||
} else if (prev_has_xflag) {
|
||||
try zig_args.append("-x");
|
||||
try zig_args.append("none");
|
||||
prev_has_xflag = false;
|
||||
}
|
||||
|
||||
const root_path = c_source_files.root.getPath2(dep.module.owner, step);
|
||||
for (c_source_files.files) |file| {
|
||||
try zig_args.append(b.pathJoin(&.{ root_path, file }));
|
||||
@ -1780,6 +1836,90 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
|
||||
return try zig_args.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn finalize(step: *Step) !void {
|
||||
const compile: *Compile = @fieldParentPtr("step", step);
|
||||
const b = step.owner;
|
||||
|
||||
{
|
||||
// Fully recursive iteration including dynamic libraries to detect
|
||||
// libc and libc++ linkage.
|
||||
var dep_it = compile.root_module.iterateDependencies(compile, true);
|
||||
while (dep_it.next()) |key| {
|
||||
if (key.module.link_libc == true) compile.is_linking_libc = true;
|
||||
if (key.module.link_libcpp == true) compile.is_linking_libcpp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (compile.kind == .pch) {
|
||||
// precompiled headers must have a single input header file.
|
||||
var it = compile.root_module.iterateDependencies(compile, false);
|
||||
const link_objects = it.next().?.module.link_objects;
|
||||
assert(link_objects.items.len == 1 and link_objects.items[0] == .c_source_file);
|
||||
assert(it.next() == null);
|
||||
}
|
||||
|
||||
// add additional compile steps for precompiled headers
|
||||
for (compile.root_module.link_objects.items) |*link_object| {
|
||||
const pch_ptr: *Module.PrecompiledHeader, const flags: []const []const u8 = blk: {
|
||||
switch (link_object.*) {
|
||||
.c_source_file => |src| {
|
||||
if (src.precompiled_header) |*pch| break :blk .{ pch, src.flags };
|
||||
},
|
||||
.c_source_files => |src| {
|
||||
if (src.precompiled_header) |*pch| break :blk .{ pch, src.flags };
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
continue;
|
||||
};
|
||||
|
||||
switch (pch_ptr.*) {
|
||||
.pch_step => {
|
||||
// step customized by the user, nothing to do.
|
||||
},
|
||||
.source_header => |src| {
|
||||
const name = switch (src.path) {
|
||||
.src_path => |sp| fs.path.basename(sp.sub_path),
|
||||
.cwd_relative => |p| fs.path.basename(p),
|
||||
.generated => "generated",
|
||||
.dependency => "dependency",
|
||||
};
|
||||
|
||||
const step_name = b.fmt("zig build-pch {s}{s} {s}", .{
|
||||
name,
|
||||
@tagName(compile.root_module.optimize orelse .Debug),
|
||||
compile.root_module.resolved_target.?.query.zigTriple(b.allocator) catch @panic("OOM"),
|
||||
});
|
||||
|
||||
// We generate a new compile step for each use,
|
||||
// leveraging the cache system to reuse the generated pch file when possible.
|
||||
const compile_pch = b.allocator.create(Compile) catch @panic("OOM");
|
||||
|
||||
// For robustness, suppose all options have an impact on the header compilation.
|
||||
// (instead of auditing each llvm version for flags observable from header compilation)
|
||||
// So, copy everything and minimally adjust as needed:
|
||||
compile_pch.* = compile.*;
|
||||
|
||||
compile_pch.kind = .pch;
|
||||
compile_pch.step.name = step_name;
|
||||
compile_pch.name = name;
|
||||
compile_pch.out_filename = std.fmt.allocPrint(b.allocator, "{s}.pch", .{name}) catch @panic("OOM");
|
||||
compile_pch.installed_headers = .init(b.allocator);
|
||||
compile_pch.force_undefined_symbols = .init(b.allocator);
|
||||
|
||||
compile_pch.root_module.link_objects = .{};
|
||||
compile_pch.addCSourceFile(.{ .file = src.path, .lang = src.lang, .flags = flags });
|
||||
|
||||
// finalize the parent compile step by modifying it to use the generated pch compile step
|
||||
pch_ptr.* = .{ .pch_step = compile_pch };
|
||||
_ = compile_pch.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
|
||||
compile.root_module.addStepDependenciesOnly(&compile_pch.step);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make(step: *Step, options: Step.MakeOptions) !void {
|
||||
const b = step.owner;
|
||||
const compile: *Compile = @fieldParentPtr("step", step);
|
||||
|
@ -56,7 +56,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins
|
||||
const dest_dir: ?InstallDir = switch (options.dest_dir) {
|
||||
.disabled => null,
|
||||
.default => switch (artifact.kind) {
|
||||
.obj => @panic("object files have no standard installation procedure"),
|
||||
.obj, .pch => @panic("object files have no standard installation procedure"),
|
||||
.exe, .@"test" => .bin,
|
||||
.lib => if (artifact.isDll()) .bin else .lib,
|
||||
},
|
||||
|
@ -4691,11 +4691,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
|
||||
else
|
||||
"/dev/null";
|
||||
|
||||
try argv.ensureUnusedCapacity(6);
|
||||
try argv.ensureUnusedCapacity(7);
|
||||
switch (comp.clang_preprocessor_mode) {
|
||||
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
|
||||
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
|
||||
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
|
||||
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-fpch-validate-input-files-content", "-o", out_obj_path }),
|
||||
.stdout => argv.appendAssumeCapacity("-E"),
|
||||
}
|
||||
|
||||
@ -4734,11 +4734,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
|
||||
try argv.appendSlice(c_object.src.extra_flags);
|
||||
try argv.appendSlice(c_object.src.cache_exempt_flags);
|
||||
|
||||
try argv.ensureUnusedCapacity(6);
|
||||
try argv.ensureUnusedCapacity(7);
|
||||
switch (comp.clang_preprocessor_mode) {
|
||||
.no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
|
||||
.yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
|
||||
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
|
||||
.pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-fpch-validate-input-files-content", "-o", out_obj_path }),
|
||||
.stdout => argv.appendAssumeCapacity("-E"),
|
||||
}
|
||||
if (out_diag_path) |diag_file_path| {
|
||||
|
11
src/main.zig
11
src/main.zig
@ -82,6 +82,7 @@ const normal_usage =
|
||||
\\ build-exe Create executable from source or object files
|
||||
\\ build-lib Create library from source or object files
|
||||
\\ build-obj Create object from source or object files
|
||||
\\ build-pch Create a precompiled header from a c or c++ header
|
||||
\\ test Perform unit testing
|
||||
\\ run Create executable and run immediately
|
||||
\\
|
||||
@ -263,6 +264,8 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
} else if (mem.eql(u8, cmd, "build-obj")) {
|
||||
dev.check(.build_obj_command);
|
||||
return buildOutputType(gpa, arena, args, .{ .build = .Obj });
|
||||
} else if (mem.eql(u8, cmd, "build-pch")) {
|
||||
return buildOutputType(gpa, arena, args, .pch);
|
||||
} else if (mem.eql(u8, cmd, "test")) {
|
||||
dev.check(.test_command);
|
||||
return buildOutputType(gpa, arena, args, .zig_test);
|
||||
@ -380,6 +383,7 @@ const usage_build_generic =
|
||||
\\Usage: zig build-exe [options] [files]
|
||||
\\ zig build-lib [options] [files]
|
||||
\\ zig build-obj [options] [files]
|
||||
\\ zig build-pch [options] [files]
|
||||
\\ zig test [options] [files]
|
||||
\\ zig run [options] [files] [-- [args]]
|
||||
\\ zig translate-c [options] [file]
|
||||
@ -733,6 +737,7 @@ const ArgMode = union(enum) {
|
||||
build: std.builtin.OutputMode,
|
||||
cc,
|
||||
cpp,
|
||||
pch,
|
||||
translate_c,
|
||||
zig_test,
|
||||
run,
|
||||
@ -999,11 +1004,15 @@ fn buildOutputType(
|
||||
var n_jobs: ?u32 = null;
|
||||
|
||||
switch (arg_mode) {
|
||||
.build, .translate_c, .zig_test, .run => {
|
||||
.build, .translate_c, .zig_test, .run, .pch => {
|
||||
switch (arg_mode) {
|
||||
.build => |m| {
|
||||
create_module.opts.output_mode = m;
|
||||
},
|
||||
.pch => {
|
||||
create_module.opts.output_mode = .Obj;
|
||||
clang_preprocessor_mode = .pch;
|
||||
},
|
||||
.translate_c => {
|
||||
emit_bin = .no;
|
||||
create_module.opts.output_mode = .Obj;
|
||||
|
@ -119,7 +119,7 @@ fn addCompileStep(
|
||||
});
|
||||
}
|
||||
if (overlay.asm_source_bytes) |bytes| {
|
||||
compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes));
|
||||
compile_step.addAssemblyFile(.{ .file = b.addWriteFiles().add("a.s", bytes) });
|
||||
}
|
||||
return compile_step;
|
||||
}
|
||||
@ -147,7 +147,7 @@ pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
|
||||
const b = comp.step.owner;
|
||||
const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
|
||||
const file = WriteFile.create(b).add("a.s", actual_bytes);
|
||||
comp.addAssemblyFile(file);
|
||||
comp.addAssemblyFile(.{ .file = file });
|
||||
}
|
||||
|
||||
pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
|
||||
|
@ -101,7 +101,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void {
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
});
|
||||
exe.addAssemblyFile(first_file);
|
||||
exe.addAssemblyFile(.{ .file = first_file });
|
||||
|
||||
const run = b.addRunArtifact(exe);
|
||||
run.setName(annotated_case_name);
|
||||
|
@ -126,6 +126,9 @@
|
||||
.c_compiler = .{
|
||||
.path = "c_compiler",
|
||||
},
|
||||
.c_header = .{
|
||||
.path = "c_header",
|
||||
},
|
||||
.pie = .{
|
||||
.path = "pie",
|
||||
},
|
||||
|
91
test/standalone/c_header/build.zig
Normal file
91
test/standalone/c_header/build.zig
Normal file
@ -0,0 +1,91 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const test_step = b.step("test", "Test it");
|
||||
b.default_step = test_step;
|
||||
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// testcase 1: single-file c-header library
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "single-file-library",
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.linkLibC();
|
||||
exe.addIncludePath(b.path("."));
|
||||
exe.addCSourceFile(.{
|
||||
.file = b.path("single_file_library.h"),
|
||||
.lang = .c,
|
||||
.flags = &.{"-DTSTLIB_IMPLEMENTATION"},
|
||||
});
|
||||
|
||||
test_step.dependOn(&b.addRunArtifact(exe).step);
|
||||
}
|
||||
|
||||
// testcase 2: precompiled header in C, from a generated file, with a compile step generated automaticcaly, twice with a cache hit
|
||||
// and it also test the explicit source lang not inferred from file extenson.
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "pchtest",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
});
|
||||
|
||||
const generated_header = b.addWriteFiles().add("generated.h",
|
||||
\\ /* generated file */
|
||||
\\ #include "include_a.h"
|
||||
);
|
||||
|
||||
exe.addCSourceFile(.{
|
||||
.file = b.path("test.c2"),
|
||||
.flags = &[_][]const u8{},
|
||||
.lang = .c,
|
||||
.precompiled_header = .{ .source_header = .{ .path = generated_header, .lang = .h } },
|
||||
});
|
||||
exe.addCSourceFiles(.{
|
||||
.files = &.{"test.c"},
|
||||
.flags = &[_][]const u8{},
|
||||
.lang = .c,
|
||||
.precompiled_header = .{ .source_header = .{ .path = generated_header, .lang = .h } },
|
||||
});
|
||||
|
||||
exe.addIncludePath(b.path("."));
|
||||
|
||||
test_step.dependOn(&b.addRunArtifact(exe).step);
|
||||
}
|
||||
|
||||
// testcase 3: precompiled header in C++, from a .h file that must be precompiled as c++, with an explicit pch compile step.
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "pchtest++",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.linkLibCpp();
|
||||
|
||||
const pch = b.addPrecompiledCHeader(.{
|
||||
.name = "pch_c++",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libcpp = true,
|
||||
}, .{
|
||||
.file = b.path("include_a.h"),
|
||||
.flags = &[_][]const u8{},
|
||||
.lang = .hpp,
|
||||
});
|
||||
|
||||
exe.addCSourceFile(.{
|
||||
.file = b.path("test.cpp"),
|
||||
.flags = &[_][]const u8{},
|
||||
.precompiled_header = .{ .pch_step = pch },
|
||||
});
|
||||
|
||||
test_step.dependOn(&b.addRunArtifact(exe).step);
|
||||
}
|
||||
}
|
16
test/standalone/c_header/include_a.h
Normal file
16
test/standalone/c_header/include_a.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "include_b.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#include <iostream>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#define A_INCLUDED 1
|
||||
|
7
test/standalone/c_header/include_b.h
Normal file
7
test/standalone/c_header/include_b.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
|
||||
typedef double real;
|
||||
|
||||
#define B_INCLUDED 1
|
11
test/standalone/c_header/main.zig
Normal file
11
test/standalone/c_header/main.zig
Normal file
@ -0,0 +1,11 @@
|
||||
const std = @import("std");
|
||||
const C = @cImport({
|
||||
@cInclude("single_file_library.h");
|
||||
});
|
||||
|
||||
pub fn main() !void {
|
||||
const msg = "hello";
|
||||
const val = C.tstlib_len(msg);
|
||||
if (val != msg.len)
|
||||
std.process.exit(1);
|
||||
}
|
14
test/standalone/c_header/single_file_library.h
Normal file
14
test/standalone/c_header/single_file_library.h
Normal file
@ -0,0 +1,14 @@
|
||||
// library header:
|
||||
extern unsigned tstlib_len(const char* msg);
|
||||
|
||||
// library implementation:
|
||||
#ifdef TSTLIB_IMPLEMENTATION
|
||||
|
||||
#include <string.h>
|
||||
|
||||
unsigned tstlib_len(const char* msg)
|
||||
{
|
||||
return strlen(msg);
|
||||
}
|
||||
|
||||
#endif
|
19
test/standalone/c_header/test.c
Normal file
19
test/standalone/c_header/test.c
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
// includes commented out to make sure the symbols come from the precompiled header.
|
||||
//#include "include_a.h"
|
||||
//#include "include_b.h"
|
||||
|
||||
#ifndef A_INCLUDED
|
||||
#error "pch not included"
|
||||
#endif
|
||||
#ifndef B_INCLUDED
|
||||
#error "pch not included"
|
||||
#endif
|
||||
|
||||
extern int func(real a, bool cond);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
real a = 0.123;
|
||||
return func(a, (argc > 1));
|
||||
}
|
20
test/standalone/c_header/test.c2
Normal file
20
test/standalone/c_header/test.c2
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
// includes commented out to make sure the symbols come from the precompiled header.
|
||||
//#include "include_a.h"
|
||||
//#include "include_b.h"
|
||||
|
||||
#ifndef A_INCLUDED
|
||||
#error "pch not included"
|
||||
#endif
|
||||
#ifndef B_INCLUDED
|
||||
#error "pch not included"
|
||||
#endif
|
||||
|
||||
int func(real a, bool cond)
|
||||
{
|
||||
if (cond) {
|
||||
fprintf(stdout, "abs(%g)=%g\n", a, fabs(a));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
23
test/standalone/c_header/test.cpp
Normal file
23
test/standalone/c_header/test.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
// includes commented out to make sure the symbols come from the precompiled header.
|
||||
//#include "includeA.h"
|
||||
//#include "includeB.h"
|
||||
|
||||
#ifndef A_INCLUDED
|
||||
#error "pch not included"
|
||||
#endif
|
||||
#ifndef B_INCLUDED
|
||||
#error "pch not included"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
real a = -0.123;
|
||||
|
||||
if (argc > 1) {
|
||||
std::cout << "abs(" << a << ")=" << fabs(a) << "\n";
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user