Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simpler pch support for zig build #20879

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,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) = .{};
for (graph.needed_lazy_dependencies.keys()) |k| {
Expand Down Expand Up @@ -635,6 +639,7 @@ fn runStepNames(
test_count += s.test_results.test_count;

switch (s.state) {
.unfinalized => unreachable,
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
.running => unreachable,
Expand Down Expand Up @@ -780,6 +785,7 @@ fn printStepStatus(
run: *const Run,
) !void {
switch (s.state) {
.unfinalized => unreachable,
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
.precheck_done => unreachable,
Expand Down Expand Up @@ -977,6 +983,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
Expand All @@ -995,6 +1029,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;
Expand Down Expand Up @@ -1057,6 +1092,7 @@ fn workerMakeOneStep(
// dependency is not finished yet.
return;
},
.unfinalized => unreachable,
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
}
Expand Down
25 changes: 25 additions & 0 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,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
Expand Down
131 changes: 123 additions & 8 deletions lib/std/Build/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
};
}
};
Expand Down Expand Up @@ -286,10 +376,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),

Expand Down Expand Up @@ -323,7 +412,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);
}
Expand Down Expand Up @@ -485,7 +574,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.
Expand All @@ -506,10 +597,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 {
Expand All @@ -519,6 +622,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`.
Expand All @@ -541,10 +654,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 {
Expand Down
17 changes: 16 additions & 1 deletion lib/std/Build/Step.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
id: Id,
name: []const u8,
owner: *Build,
finalizeFn: FinalizeFn,
makeFn: MakeFn,

dependencies: std.ArrayList(*Step),
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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");
Expand All @@ -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.
Expand Down Expand Up @@ -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;

Expand Down
Loading
Loading