Skip to content
Open
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
147 changes: 44 additions & 103 deletions CompileCheck.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const std = @import("std");

step: std.Build.Step,
target: std.Build.ResolvedTarget,
kind: Kind,
source_content: []const u8,
source_path: std.Build.LazyPath,
result: ?Result = null,
Expand All @@ -22,56 +21,29 @@ const Result = union(enum) {
},
};

pub const Kind = union(enum) {
exe: []const u8,
header: []const u8,
};

pub fn create(b: *std.Build, target: std.Build.ResolvedTarget, kind: Kind) *CompileCheck {
pub fn create(b: *std.Build, target: std.Build.ResolvedTarget, source: []const u8) *CompileCheck {
const write_files = b.addWriteFiles();
const source_duped = switch (kind) {
.exe => |src| b.dupe(src),
.header => |h| b.fmt("#include <{s}>", .{h}),
};
const source_duped = b.dupe(source);
const source_path = write_files.add(
switch (kind) {
.exe => "compilecheck-exe.c",
.header => "compilecheck-header.c",
},
"compilecheck.c",
source_duped,
);
const check = b.allocator.create(CompileCheck) catch @panic("OOM");
check.* = .{
.step = std.Build.Step.init(.{
.id = .custom,
.name = switch (kind) {
.exe => "compile check exe",
.header => |h| b.fmt("compile check header '{s}'", .{h}),
},
.name = "compile check exe",
.owner = b,
.makeFn = make,
}),
.target = target,
.kind = switch (kind) {
.exe => .{ .exe = source_duped },
.header => |h| .{ .header = b.dupe(h) },
},
.source_content = source_duped,
.source_path = source_path,
};
source_path.addStepDependencies(&check.step);
return check;
}

pub fn haveHeader(check: *CompileCheck, asking_step: *std.Build.Step) ?u1 {
std.debug.assert(check.kind == .header);
if (!dependsOn(asking_step, &check.step)) std.debug.panic("haveHeader called on CompileCheck without a dependency", .{});
return switch (check.result.?) {
.pass => 1,
.fail => null,
};
}

pub fn compiled(
check: *CompileCheck,
asking_step: *std.Build.Step,
Expand All @@ -81,7 +53,6 @@ pub fn compiled(
allow_undeclared_identifier: bool = true,
},
) !?u1 {
std.debug.assert(check.kind == .exe);
if (!dependsOn(asking_step, &check.step)) std.debug.panic("compiled called on CompileCheck without a dependency", .{});
return switch (check.result.?) {
.pass => 1,
Expand Down Expand Up @@ -151,72 +122,55 @@ fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!voi
var zig_args: std.array_list.Managed([]const u8) = .init(b.allocator);
defer zig_args.deinit();
try zig_args.append(b.graph.zig_exe);
try zig_args.append(switch (check.kind) {
.exe => "build-exe",
.header => "build-obj",
});
try zig_args.append("build-exe");
try zig_args.append("-lc");
try zig_args.append("-target");
try zig_args.append(try check.target.query.zigTriple(b.allocator));
try zig_args.append(source_path);
for (check.include_dirs.items) |include_dir| {
try include_dir.appendZigProcessFlags(b, &zig_args, step);
}
const links = switch (check.kind) {
.exe => true,
.header => false,
};
if (links) {
for (check.link_objects.items) |link_object| {
switch (link_object) {
.other_step => |other| {
switch (other.kind) {
.exe => return step.fail("cannot link with an executable build artifact", .{}),
.@"test", .test_obj => return step.fail("cannot link with a test", .{}),
.obj => {
try zig_args.append(other.getEmittedBin().getPath2(b, step));
},
.lib => {
const other_produces_implib = other.producesImplib();
// For DLLs, we must link against the implib.
// For everything else, we directly link
// against the library file.
const full_path_lib = if (other_produces_implib)
getGeneratedFilePath(other, "generated_implib", step)
else
getGeneratedFilePath(other, "generated_bin", step);
try zig_args.append(full_path_lib);
},
}
},
else => |o| std.debug.panic("todo: handle link object {t}", .{o}),
}

for (check.link_objects.items) |link_object| {
switch (link_object) {
.other_step => |other| {
switch (other.kind) {
.exe => return step.fail("cannot link with an executable build artifact", .{}),
.@"test", .test_obj => return step.fail("cannot link with a test", .{}),
.obj => {
try zig_args.append(other.getEmittedBin().getPath2(b, step));
},
.lib => {
const other_produces_implib = other.producesImplib();
// For DLLs, we must link against the implib.
// For everything else, we directly link
// against the library file.
const full_path_lib = if (other_produces_implib)
getGeneratedFilePath(other, "generated_implib", step)
else
getGeneratedFilePath(other, "generated_bin", step);
try zig_args.append(full_path_lib);
},
}
},
else => |o| std.debug.panic("todo: handle link object {t}", .{o}),
}
}
switch (check.kind) {
.exe => {
var rand_int: u64 = undefined;
b.graph.io.random(std.mem.asBytes(&rand_int));
const path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(rand_int);
b.cache_root.handle.createDirPath(b.graph.io, path) catch |err| return step.fail(
"create dir '{f}{s}' failed with {t}",
.{ b.cache_root, path, err },
);
const emit_bin_path = try b.cache_root.join(b.allocator, &.{ path, "compilecheck-exe" });
try zig_args.append(b.fmt("-femit-bin={s}", .{emit_bin_path}));
const result = try std.process.run(b.allocator, b.graph.io, .{
.argv = zig_args.items,
});
try b.cache_root.handle.deleteTree(b.graph.io, path);
break :blk result;
},
.header => {
try zig_args.append("-fno-emit-bin");
break :blk try std.process.run(b.allocator, b.graph.io, .{
.argv = zig_args.items,
});
},
}

var rand_int: u64 = undefined;
b.graph.io.random(std.mem.asBytes(&rand_int));
const path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(rand_int);
b.cache_root.handle.createDirPath(b.graph.io, path) catch |err| return step.fail(
"create dir '{f}{s}' failed with {t}",
.{ b.cache_root, path, err },
);
const emit_bin_path = try b.cache_root.join(b.allocator, &.{ path, "compilecheck-exe" });
try zig_args.append(b.fmt("-femit-bin={s}", .{emit_bin_path}));
const result = try std.process.run(b.allocator, b.graph.io, .{
.argv = zig_args.items,
});
try b.cache_root.handle.deleteTree(b.graph.io, path);
break :blk result;
};

std.debug.assert(result.stdout.len == 0);
Expand All @@ -228,7 +182,6 @@ fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!voi
var files_not_found_count: u32 = 0;
var undeclared_function_count: u32 = 0;
var undeclared_identifier_count: u32 = 0;
var found_header = false;
var line_it = std.mem.splitScalar(u8, result.stderr, '\n');
while (line_it.next()) |line_untrimmed| {
const line = std.mem.trimEnd(u8, line_untrimmed, "\r");
Expand All @@ -240,22 +193,10 @@ fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!voi
};
const err = line[error_start + error_prefix.len ..];
if (std.mem.endsWith(u8, err, "file not found")) {
switch (check.kind) {
.exe => {},
.header => |h| found_header = found_header or (std.mem.indexOf(u8, err, h) != null),
}
files_not_found_count += 1;
} else if (std.mem.startsWith(u8, err, "call to undeclared function ")) {
switch (check.kind) {
.exe => {},
.header => return check.notAllowed(step, "undeclared function", result.stderr),
}
undeclared_function_count += 1;
} else if (std.mem.startsWith(u8, err, "use of undeclared identifier")) {
switch (check.kind) {
.exe => {},
.header => return check.notAllowed(step, "undeclared identifier", result.stderr),
}
undeclared_identifier_count += 1;
} else {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Expand Down
56 changes: 42 additions & 14 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1412,23 +1412,41 @@ fn addPyconfig(
.@"3.12.11" => &exe_config_set.@"3.12.11",
};

const libc_dirs = try std.zig.LibCDirs.detect(
b.allocator,
b.graph.io,
b.graph.zig_lib_directory.path.?,
&target.result,
target.query.isNativeAbi(),
true, // link_libc
null, // libc_installation
&b.graph.environ_map,
);
switch (version) {
inline else => |v| {
const Enum = @field(header_enum, @tagName(v));
const headers = try systemquery.HeaderSet(Enum).scanDirs(b.graph.io, libc_dirs.libc_include_dir_list);
for (header_configs) |config| {
const header = std.meta.stringToEnum(Enum, config.string).?;
const assume = if (header == .@"zlib.h") (libs.zlib != null) else false;
const found = headers.contains(header);
config_header.addValue(config.name, ?u1, if (found or assume) 1 else null);
}
},
}

{
const AddValues = struct {
step: std.Build.Step,
version: Version,
config_header: *std.Build.Step.ConfigHeader,
header_configs: []const Config,
header_checks: []*CompileCheck,
exe_configs: []const Config,
exe_checks: []*CompileCheck,
};
const add_values_make = struct {
fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void {
_ = options;
const self: *AddValues = @fieldParentPtr("step", step);
for (self.header_configs, self.header_checks) |config, check| {
self.config_header.addValue(config.name, ?u1, check.haveHeader(step));
}
for (self.exe_configs, self.exe_checks) |config, check| {
self.config_header.addValue(config.name, ?u1, try check.compiled(step, .{}));
}
Expand All @@ -1444,19 +1462,11 @@ fn addPyconfig(
}),
.version = version,
.config_header = config_header,
.header_configs = header_configs,
.header_checks = b.allocator.alloc(*CompileCheck, header_configs.len) catch @panic("OOM"),
.exe_configs = exe_configs,
.exe_checks = b.allocator.alloc(*CompileCheck, exe_configs.len) catch @panic("OOM"),
};
for (header_configs, add_values.header_checks) |config, *check| {
check.* = CompileCheck.create(b, target, .{ .header = config.string });
if (libs.zlib) |zlib| check.*.linkLibrary(zlib);
if (libs.openssl) |openssl| check.*.linkLibrary(openssl);
add_values.step.dependOn(&check.*.step);
}
for (exe_configs, add_values.exe_checks) |config, *check| {
check.* = CompileCheck.create(b, target, .{ .exe = config.string });
check.* = CompileCheck.create(b, target, config.string);
if (libs.zlib) |zlib| check.*.linkLibrary(zlib);
if (libs.openssl) |openssl| check.*.linkLibrary(openssl);
add_values.step.dependOn(&check.*.step);
Expand All @@ -1476,6 +1486,23 @@ fn have(x: bool) ?u1 {
}

const Config = struct { name: []const u8, string: []const u8 };

const header_enum = struct {
pub const @"3.11.13" = HeaderEnum(header_config_set.@"3.11.13");
pub const @"3.12.11" = HeaderEnum(header_config_set.@"3.12.11");
};

fn HeaderEnum(comptime configs: anytype) type {
const TagInt = std.math.IntFittingRange(0, configs.len - 1);
var names: [configs.len][]const u8 = undefined;
var values: [configs.len]TagInt = undefined;
for (configs, 0..) |config, i| {
names[i] = config.string;
values[i] = i;
}
return @Enum(TagInt, .exhaustive, &names, &values);
}

fn concatConfigs(comptime first: anytype, comptime second: anytype) [std.meta.fields(@TypeOf(first)).len + std.meta.fields(@TypeOf(second)).len]Config {
const first_len = std.meta.fields(@TypeOf(first)).len;
var result: [first_len + std.meta.fields(@TypeOf(second)).len]Config = undefined;
Expand Down Expand Up @@ -1804,4 +1831,5 @@ fn concat(allocator: std.mem.Allocator, lists: []const []const []const u8) []con
}

const std = @import("std");
const systemquery = @import("systemquery.zig");
const CompileCheck = @import("CompileCheck.zig");
1 change: 1 addition & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"build.zig.zon",
"makesetup.zig",
"replace.zig",
"systemquery.zig",
"CompileCheck.zig",
},
}
Loading
Loading