diff --git a/CompileCheck.zig b/CompileCheck.zig deleted file mode 100644 index 25e553f..0000000 --- a/CompileCheck.zig +++ /dev/null @@ -1,321 +0,0 @@ -const CompileCheck = @This(); - -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, - -include_dirs: std.ArrayList(std.Build.Module.IncludeDir) = .empty, -link_objects: std.ArrayList(std.Build.Module.LinkObject) = .empty, - -const Result = union(enum) { - pass, - fail: struct { - stderr: []const u8, - files_not_found_count: u32, - undeclared_function_count: u32, - undeclared_identifier_count: u32, - }, -}; - -pub const Kind = union(enum) { - exe: []const u8, - header: []const u8, -}; - -pub fn create(b: *std.Build, target: std.Build.ResolvedTarget, kind: Kind) *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_path = write_files.add( - switch (kind) { - .exe => "compilecheck-exe.c", - .header => "compilecheck-header.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}), - }, - .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, - opt: struct { - allow_file_not_found: bool = true, - allow_undeclared_function: bool = true, - 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, - .fail => |result| { - if (!opt.allow_file_not_found and result.files_not_found_count > 0) return check.notAllowed( - asking_step, - "file not found", - result.stderr, - ); - if (!opt.allow_undeclared_function and result.undeclared_function_count > 0) return check.notAllowed( - asking_step, - "undeclared function", - result.stderr, - ); - if (!opt.allow_undeclared_identifier and result.undeclared_identifier_count > 0) return check.notAllowed( - asking_step, - "undeclared identifier", - result.stderr, - ); - return null; - }, - }; -} - -pub fn linkLibrary(check: *CompileCheck, library: *std.Build.Step.Compile) void { - std.debug.assert(library.kind == .lib); - check.linkLibraryOrObject(library); -} -fn linkLibraryOrObject(check: *CompileCheck, other: *std.Build.Step.Compile) void { - const allocator = check.step.owner.allocator; - const bin = other.getEmittedBin(); - bin.addStepDependencies(&check.step); - - if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) { - const lib = other.getEmittedImplib(); - lib.addStepDependencies(&check.step); - } - - check.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); - check.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); - - other.getEmittedIncludeTree().addStepDependencies(&check.step); -} - -fn notAllowed( - check: *const CompileCheck, - asking_step: *std.Build.Step, - with: []const u8, - stderr: []const u8, -) error{ MakeFailed, OutOfMemory } { - return asking_step.fail( - "compile check for code:\n----\n{s}\n----\nis not allowed to fail with '{s}' but did with the following output:\n----\n{s}\n----\n", - .{ check.source_content, with, stderr }, - ); -} - -fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { - _ = options; - const b = step.owner; - const check: *CompileCheck = @fieldParentPtr("step", step); - std.debug.assert(check.result == null); - - // TODO: for faster rebuilds implement the cache check - const source_path = check.source_path.getPath2(b, step); - - const result = blk: { - 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("-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}), - } - } - } - 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, - }); - }, - } - }; - - std.debug.assert(result.stdout.len == 0); - switch (result.term) { - .exited => |code| if (code == 0) { - std.debug.assert(result.stderr.len == 0); - check.result = .pass; - } else { - 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"); - if (line.len == 0) continue; - - const error_prefix = "error: "; - const error_start = std.mem.indexOf(u8, line, error_prefix) orelse { - continue; - }; - 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 { - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // std.debug.panic("todo: parse compiler error message '{s}' from the following:\n----\n{s}\n----\n", .{ err, result.stderr }); - } - } - - check.result = .{ .fail = .{ - .stderr = result.stderr, - .files_not_found_count = files_not_found_count, - .undeclared_function_count = undeclared_function_count, - .undeclared_identifier_count = undeclared_identifier_count, - } }; - }, - inline else => return step.fail("zig {f}", .{fmtTerm(result.term)}), - } -} - -fn dependsOn(asking: *std.Build.Step, candidate: *std.Build.Step) bool { - std.debug.assert(asking != candidate); - for (asking.dependencies.items) |dep| { - if (dep == candidate or dependsOn(dep, candidate)) return true; - } - return false; -} - -fn getGeneratedFilePath(compile: *std.Build.Step.Compile, comptime tag_name: []const u8, asking_step: ?*std.Build.Step) []const u8 { - const maybe_path: ?*std.Build.GeneratedFile = @field(compile, tag_name); - - const generated_file = maybe_path orelse { - { - const stderr = std.debug.lockStderr(&.{}); - defer std.debug.unlockStderr(); - std.Build.dumpBadGetPathHelp(&compile.step, stderr.terminal(), compile.step.owner, asking_step) catch {}; - } - @panic("missing emit option for " ++ tag_name); - }; - - const path = generated_file.path orelse { - { - const stderr = std.debug.lockStderr(&.{}); - defer std.debug.unlockStderr(); - std.Build.dumpBadGetPathHelp(&compile.step, stderr.terminal(), compile.step.owner, asking_step) catch {}; - } - @panic(tag_name ++ " is null. Is there a missing step dependency?"); - }; - - return path; -} - -fn fmtTerm(term: ?std.process.Child.Term) std.fmt.Alt(?std.process.Child.Term, formatTerm) { - return .{ .data = term }; -} -fn formatTerm(term: ?std.process.Child.Term, writer: *std.Io.Writer) error{WriteFailed}!void { - if (term) |t| switch (t) { - .exited => |code| try writer.print("exited with code {}", .{code}), - .signal => |sig| try writer.print("terminated with signal {}", .{sig}), - .stopped => |sig| try writer.print("stopped with signal {}", .{sig}), - .unknown => |code| try writer.print("terminated for unknown reason with code {}", .{code}), - } else { - try writer.writeAll("exited with any code"); - } -} diff --git a/ConfigHeaderExt.zig b/ConfigHeaderExt.zig new file mode 100644 index 0000000..1fbdddd --- /dev/null +++ b/ConfigHeaderExt.zig @@ -0,0 +1,161 @@ +/// Add config values to a ConfigHeader from a lazy path. +/// The lazy path should contain lines in the format: NAME VALUE +/// TODO: upstream into ConfigHeader.zig +pub fn addLazy(config_header: *ConfigHeader, config_results: std.Build.LazyPath) void { + const b = config_header.step.owner; + // This ApplyConfigStep is just a HACK which *should work* but the proper solution is to + // add LazyPath support in ConfigHeader itself. + const apply = b.allocator.create(ApplyConfigStep) catch @panic("OOM"); + apply.* = .{ + .step = std.Build.Step.init(.{ + .id = .custom, + .name = "add configuration from lazy path", + .owner = b, + .makeFn = ApplyConfigStep.make, + }), + .config_header = config_header, + .config_results = config_results, + }; + config_results.addStepDependencies(&apply.step); + config_header.step.dependOn(&apply.step); +} + +pub const Entry = struct { + name: []const u8, + value: ConfigHeader.Value, + + pub const ParseError = error{ + MissingValue, + InvalidValue, + }; + + pub fn parse(raw_line: []const u8) ParseError!Entry { + const line = std.mem.trim(u8, raw_line, &std.ascii.whitespace); + const first_space = std.mem.indexOfScalar(u8, line, ' ') orelse return error.MissingValue; + const name = line[0..first_space]; + const raw_value = std.mem.trimStart(u8, line[first_space + 1 ..], " "); + const value, const end = parseValue(raw_value) orelse return error.InvalidValue; + if (end != raw_value.len) return error.InvalidValue; + return .{ .name = name, .value = value }; + } +}; + +pub fn parseValue(str: []const u8) ?struct { ConfigHeader.Value, usize } { + if (str.len == 0) return null; + if (matchKeyword(str, "undef")) |end| return .{ .undef, end }; + if (matchKeyword(str, "defined")) |end| return .{ .defined, end }; + if (matchKeyword(str, "true")) |end| return .{ .{ .boolean = true }, end }; + if (matchKeyword(str, "false")) |end| return .{ .{ .boolean = false }, end }; + if (str[0] == '.') { + if (str.len >= 3 and str[1] == '@' and str[2] == '"') { + const close = std.mem.indexOfScalarPos(u8, str, 3, '"') orelse return null; + return .{ .{ .ident = str[3..close] }, close + 1 }; + } + if (str.len < 2 or !isIdentStart(str[1])) return null; + var end: usize = 2; + while (end < str.len and isIdentChar(str[end])) : (end += 1) {} + return .{ .{ .ident = str[1..end] }, end }; + } + if (str[0] == '"') { + const close = std.mem.indexOfScalarPos(u8, str, 1, '"') orelse return null; + return .{ .{ .string = str[1..close] }, close + 1 }; + } + // integer: scan digits (with optional leading -) + { + var end: usize = if (str[0] == '-') 1 else 0; + if (end >= str.len or str[end] < '0' or str[end] > '9') return null; + while (end < str.len and str[end] >= '0' and str[end] <= '9') : (end += 1) {} + if (std.fmt.parseInt(i64, str[0..end], 10)) |int_val| return .{ .{ .int = int_val }, end } else |_| return null; + } +} + +fn matchKeyword(str: []const u8, keyword: []const u8) ?usize { + if (str.len < keyword.len) return null; + if (!std.mem.eql(u8, str[0..keyword.len], keyword)) return null; + if (str.len == keyword.len) return keyword.len; + if (str[keyword.len] == ' ') return keyword.len; + return null; +} + +const ApplyConfigStep = struct { + step: std.Build.Step, + config_header: *ConfigHeader, + config_results: std.Build.LazyPath, + + fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { + _ = options; + const self: *ApplyConfigStep = @fieldParentPtr("step", step); + const b = step.owner; + const results_path = self.config_results.getPath2(b, step); + const content = try std.Io.Dir.cwd().readFileAlloc(b.graph.io, results_path, b.allocator, .unlimited); + var line_it = std.mem.splitScalar(u8, content, '\n'); + while (line_it.next()) |line| { + if (line.len == 0 or line[0] == '#') continue; + const entry = Entry.parse(line) catch |err| return step.fail( + "failed to parse config '{s}' with {t}", + .{ line, err }, + ); + self.config_header.values.put(b.allocator, entry.name, entry.value) catch @panic("OOM"); + } + } +}; + +fn isIdentStart(c: u8) bool { + return switch (c) { + 'a'...'z', 'A'...'Z', '_' => true, + else => false, + }; +} + +fn isIdentChar(c: u8) bool { + return switch (c) { + 'a'...'z', 'A'...'Z', '_', '0'...'9' => true, + else => false, + }; +} + +fn expectValue(str: []const u8, expected: ConfigHeader.Value, expected_end: usize) !void { + const value, const end = parseValue(str) orelse return error.TestUnexpectedResult; + try std.testing.expectEqualDeep(expected, value); + try std.testing.expectEqual(expected_end, end); +} + +test parseValue { + // undef + try expectValue("undef", .undef, 5); + try expectValue("undef rest", .undef, 5); + // defined + try expectValue("defined", .defined, 7); + // bool + try expectValue("true", .{ .boolean = true }, 4); + try expectValue("false", .{ .boolean = false }, 5); + // int + try expectValue("4", .{ .int = 4 }, 1); + try expectValue("-1", .{ .int = -1 }, 2); + try expectValue("0", .{ .int = 0 }, 1); + try expectValue("42 rest", .{ .int = 42 }, 2); + // ident (.IDENT syntax) + try expectValue(".void", .{ .ident = "void" }, 5); + try expectValue(".some_value", .{ .ident = "some_value" }, 11); + try expectValue(".void rest", .{ .ident = "void" }, 5); + // ident (.@"..." syntax) + try expectValue(".@\"linux-gnu\"", .{ .ident = "linux-gnu" }, 13); + try expectValue(".@\"has spaces\"", .{ .ident = "has spaces" }, 14); + try expectValue(".@\"\"", .{ .ident = "" }, 4); + try expectValue(".@\"x\" rest", .{ .ident = "x" }, 5); + // string + try expectValue("\"md5,sha1\"", .{ .string = "md5,sha1" }, 10); + try expectValue("\"\"", .{ .string = "" }, 2); + try expectValue("\"hello world\"", .{ .string = "hello world" }, 13); + try expectValue("\"x\" rest", .{ .string = "x" }, 3); + // null for invalid + try std.testing.expectEqual(null, parseValue("")); + try std.testing.expectEqual(null, parseValue("notavalue")); + try std.testing.expectEqual(null, parseValue("\"unclosed")); + try std.testing.expectEqual(null, parseValue(".")); + try std.testing.expectEqual(null, parseValue(".0bad")); + try std.testing.expectEqual(null, parseValue(".@\"unclosed")); +} + +const std = @import("std"); +const ConfigHeader = std.Build.Step.ConfigHeader; diff --git a/build.zig b/build.zig index 7cd0a2e..5355c66 100644 --- a/build.zig +++ b/build.zig @@ -50,6 +50,13 @@ pub fn build(b: *std.Build) !void { .target = b.graph.host, }), }); + const configquery_exe = b.addExecutable(.{ + .name = "configquery", + .root_module = b.createModule(.{ + .root_source_file = b.path("configquery.zig"), + .target = b.graph.host, + }), + }); const makesetup_exe = b.addExecutable(.{ .name = "makesetup", .root_module = b.createModule(.{ @@ -93,7 +100,7 @@ pub fn build(b: *std.Build) !void { .replace_exe = replace_exe, .makesetup_exe = makesetup_exe, }); - const pyconfig_host = try addPyconfig(b, version, upstream, b.graph.host, .{ .zlib = null, .openssl = null }); + const pyconfig_host = try addPyconfig(b, version, upstream, b.graph.host, .{ .zlib = null, .openssl = null }, configquery_exe); const freeze_module_exe = addPythonExe(b, upstream, b.graph.host, .Debug, .{ .name = "freeze_module", @@ -202,7 +209,7 @@ pub fn build(b: *std.Build) !void { const final_exe = addPythonExe(b, upstream, target, optimize, .{ .name = "python", .makesetup_out = makesetup_target, - .pyconfig = try addPyconfig(b, version, upstream, target, libs_target), + .pyconfig = try addPyconfig(b, version, upstream, target, libs_target, configquery_exe), .stage = .{ .final = .{ .stage2 = stage2_frozen_mods, @@ -224,6 +231,7 @@ pub fn build(b: *std.Build) !void { try ci(b, version, ssl_enabled, upstream, ci_step, .{ .replace_exe = replace_exe, .makesetup_exe = makesetup_exe, + .configquery_exe = configquery_exe, .stage2_frozen_mods = stage2_frozen_mods, .frozen_headers = frozen_headers, .deepfreeze_c = deepfreeze_c, @@ -664,569 +672,13 @@ const Libs = struct { openssl: ?*std.Build.Step.Compile, }; -const header_config_set = struct { - const common = .{ - .{ .HAVE_ALLOCA_H, "alloca.h" }, - .{ .HAVE_ASM_TYPES_H, "asm/types.h" }, - .{ .HAVE_BLUETOOTH_H, "bluetooth.h" }, - .{ .HAVE_BLUETOOTH_BLUETOOTH_H, "bluetooth/bluetooth.h" }, - .{ .HAVE_BZLIB_H, "bzlib.h" }, - .{ .HAVE_CONIO_H, "conio.h" }, - .{ .HAVE_CRYPT_H, "crypt.h" }, - .{ .HAVE_CURSES_H, "curses.h" }, - .{ .HAVE_DIRECT_H, "direct.h" }, - .{ .HAVE_DIRENT_H, "dirent.h" }, - .{ .HAVE_DB_H, "db.h" }, - .{ .HAVE_DLFCN_H, "dlfcn.h" }, - .{ .HAVE_ENDIAN_H, "endian.h" }, - .{ .HAVE_ERRNO_H, "errno.h" }, - .{ .HAVE_FCNTL_H, "fcntl.h" }, - .{ .HAVE_GDBM_DASH_NDBM_H, "gdbm-ndbm.h" }, - .{ .HAVE_GDBM_H, "gdbm.h" }, - .{ .HAVE_GDBM_NDBM_H, "gdbm/ndbm.h" }, - .{ .HAVE_GRP_H, "grp.h" }, - .{ .HAVE_IEEEFP_H, "ieeefp.h" }, - .{ .HAVE_IO_H, "io.h" }, - .{ .HAVE_INTTYPES_H, "inttypes.h" }, - .{ .HAVE_LANGINFO_H, "langinfo.h" }, - .{ .HAVE_LIBINTL_H, "libintl.h" }, - .{ .HAVE_LIBUTIL_H, "libutil.h" }, - .{ .HAVE_LINUX_LIMITS_H, "linux/limits.h" }, - .{ .HAVE_NETDB_H, "netdb.h" }, - .{ .HAVE_NETINET_IN_H, "netinet/in.h" }, - .{ .HAVE_NETPACKET_PACKET_H, "netpacket/packet.h" }, - .{ .HAVE_NET_IF_H, "net/if.h" }, - .{ .HAVE_POLL_H, "poll.h" }, - .{ .HAVE_PTHREAD_H, "pthread.h" }, - .{ .HAVE_PTY_H, "pty.h" }, - .{ .HAVE_SCHED_H, "sched.h" }, - .{ .HAVE_SETJMP_H, "setjmp.h" }, - .{ .HAVE_SHADOW_H, "shadow.h" }, - .{ .HAVE_SIGNAL_H, "signal.h" }, - .{ .HAVE_SPAWN_H, "spawn.h" }, - .{ .HAVE_STDINT_H, "stdint.h" }, - .{ .HAVE_STDLIB_H, "stdlib.h" }, - .{ .HAVE_STRINGS_H, "strings.h" }, - .{ .HAVE_STRING_H, "string.h" }, - .{ .HAVE_SYSEXITS_H, "sysexits.h" }, - .{ .HAVE_SYSLOG_H, "syslog.h" }, - .{ .HAVE_SYS_AUXV_H, "sys/auxv.h" }, - .{ .HAVE_SYS_EPOLL_H, "sys/epoll.h" }, - .{ .HAVE_SYS_EVENTFD_H, "sys/eventfd.h" }, - .{ .HAVE_SYS_FILE_H, "sys/file.h" }, - .{ .HAVE_SYS_IOCTL_H, "sys/ioctl.h" }, - .{ .HAVE_SYS_MMAN_H, "sys/mman.h" }, - .{ .HAVE_SYS_PARAM_H, "sys/param.h" }, - .{ .HAVE_SYS_POLL_H, "sys/poll.h" }, - .{ .HAVE_SYS_RANDOM_H, "sys/random.h" }, - .{ .HAVE_SYS_RESOURCE_H, "sys/resource.h" }, - .{ .HAVE_SYS_SELECT_H, "sys/select.h" }, - .{ .HAVE_SYS_SENDFILE_H, "sys/sendfile.h" }, - .{ .HAVE_SYS_SOCKET_H, "sys/socket.h" }, - .{ .HAVE_SYS_SOUNDCARD_H, "sys/soundcard.h" }, - .{ .HAVE_SYS_STATVFS_H, "sys/statvfs.h" }, - .{ .HAVE_SYS_STAT_H, "sys/stat.h" }, - .{ .HAVE_SYS_SYSCALL_H, "sys/syscall.h" }, - .{ .HAVE_SYS_SYSMACROS_H, "sys/sysmacros.h" }, - .{ .HAVE_SYS_TIMES_H, "sys/times.h" }, - .{ .HAVE_SYS_TIME_H, "sys/time.h" }, - .{ .HAVE_SYS_TYPES_H, "sys/types.h" }, - .{ .HAVE_SYS_UIO_H, "sys/uio.h" }, - .{ .HAVE_SYS_UN_H, "sys/un.h" }, - .{ .HAVE_SYS_UTSNAME_H, "sys/utsname.h" }, - .{ .HAVE_SYS_WAIT_H, "sys/wait.h" }, - .{ .HAVE_SYS_XATTR_H, "sys/xattr.h" }, - .{ .HAVE_TERMIOS_H, "termios.h" }, - .{ .HAVE_UNISTD_H, "unistd.h" }, - .{ .HAVE_UTIME_H, "utime.h" }, - .{ .HAVE_UTMP_H, "utmp.h" }, - .{ .HAVE_WCHAR_H, "wchar.h" }, - .{ .HAVE_LZMA_H, "lzma.h" }, - .{ .HAVE_NCURSES_H, "ncurses.h" }, - .{ .HAVE_NDBM_H, "ndmb.h" }, - .{ .HAVE_NDIR_H, "ndir.h" }, - .{ .HAVE_NETCAN_CAN_H, "netcan/can.h" }, - .{ .HAVE_PROCESS_H, "process.h" }, - .{ .HAVE_RPC_RPC_H, "rpc/rpc.h" }, - .{ .HAVE_STROPTS_H, "stropts.h" }, - .{ .HAVE_SYS_AUDIOIO_H, "sys/audioio.h" }, - .{ .HAVE_SYS_BSDTTY_H, "sys/bsdtty.h" }, - .{ .HAVE_SYS_DEVPOLL_H, "sys/devpoll.h" }, - .{ .HAVE_SYS_DIR_H, "sys/dir.h" }, - .{ .HAVE_SYS_ENDIAN_H, "sys/endian.h" }, - .{ .HAVE_SYS_EVENT_H, "sys/event.h" }, - .{ .HAVE_SYS_KERN_CONTROL_H, "sys/kern/contro.h" }, - .{ .HAVE_SYS_LOADAVG_H, "sys/loadavg.h" }, - .{ .HAVE_SYS_LOCK_H, "sys/lock.h" }, - .{ .HAVE_SYS_MEMFD_H, "sys/memfd.h" }, - .{ .HAVE_SYS_MKDEV_H, "sys/mkdev.h" }, - .{ .HAVE_SYS_MODEM_H, "sys/modem.h" }, - .{ .HAVE_SYS_NDIR_H, "sys/ndir.h" }, - .{ .HAVE_SYS_SYS_DOMAIN_H, "sys/sys/domain.h" }, - .{ .HAVE_SYS_TERMIO_H, "sys/termio.h" }, - .{ .HAVE_TERM_H, "term.h" }, - .{ .HAVE_UTIL_H, "util.h" }, - .{ .HAVE_UUID_H, "uuid.h" }, - .{ .HAVE_UUID_UUID_H, "uuid/uuid.h" }, - .{ .HAVE_ZLIB_H, "zlib.h" }, - }; - pub const @"3.11.13" = concatConfigs(common, .{ - .{ .HAVE_MEMORY_H, "memory.h" }, - }); - pub const @"3.12.11" = concatConfigs(common, .{ - .{ .HAVE_EDITLINE_READLINE_H, "editline/readline.h" }, - .{ .HAVE_LINUX_FS_H, "linux/fs.h" }, - .{ .HAVE_MINIX_CONFIG_H, "minix/config.h" }, - .{ .HAVE_NET_ETHERNET_H, "net/ethernet.h" }, - .{ .HAVE_PANEL_H, "panel.h" }, - .{ .HAVE_READLINE_READLINE_H, "readline/readline.h" }, - .{ .HAVE_STDIO_H, "stdio.h" }, - .{ .HAVE_SYS_PIDFD_H, "sys/pidfd.h" }, - }); -}; - -const exe_config_set = struct { - const common = .{ - .{ .HAVE_ACCEPT, "#include \nint main(){accept(0, 0, 0);}" }, - .{ .HAVE_ACCEPT4, "#include \nint main(){accept4(0, 0, 0, 0);}" }, - .{ .HAVE_ACOSH, "#include \nint main(){acosh(1.0);}" }, - .{ .HAVE_ALARM, "#include \nint main(){alarm(1);}" }, - .{ .HAVE_ASINH, "#include \nint main(){asinh(1.0);}" }, - .{ .HAVE_ATANH, "#include \nint main(){atanh(0.5);}" }, - .{ .HAVE_BIND, "#include \nint main(){bind(0, 0, 0);}" }, - .{ .HAVE_BIND_TEXTDOMAIN_CODESET, "#include \nint main(){bind_textdomain_codeset(0, 0);}" }, - .{ .HAVE_CHMOD, "#include \nint main(){chmod(0, 0);}" }, - .{ .HAVE_CHOWN, "#include \nint main(){chown(0, 0, 0);}" }, - // .{ .HAVE_CHROOT, "#include \nint main(){chroot(0);}" }, - .{ .HAVE_CLOCK, "#include \nint main(){clock();}" }, - .{ .HAVE_CLOCK_GETRES, "#include \nint main(){struct timespec ts; clock_getres(CLOCK_REALTIME, &ts);}" }, - .{ .HAVE_CLOCK_GETTIME, "#include \nint main(){struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts);}" }, - .{ .HAVE_CLOCK_NANOSLEEP, "#include \nint main(){struct timespec ts = {0, 0}; clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0);}" }, - .{ .HAVE_CLOCK_SETTIME, "#include \nint main(){struct timespec ts = {0, 0}; clock_settime(CLOCK_REALTIME, &ts);}" }, - .{ .HAVE_CLOSE_RANGE, "#define _GNU_SOURCE\n#include \nint main(){close_range(0, 0, 0);}" }, - .{ .HAVE_CONFSTR, "#include \nint main(){confstr(0, 0, 0);}" }, - .{ .HAVE_CONNECT, "#include \nint main(){connect(0, 0, 0);}" }, - .{ .HAVE_COPY_FILE_RANGE, "#include \nint main(){copy_file_range(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_CTERMID, "#include \nint main(){ctermid(0);}" }, - .{ .HAVE_DLOPEN, "#include \nint main(){dlopen(0, 0);}" }, - .{ .HAVE_DUP, "#include \nint main(){dup(0);}" }, - .{ .HAVE_DUP2, "#include \nint main(){dup2(0, 0);}" }, - .{ .HAVE_DUP3, "#include \nint main(){dup3(0, 0, 0);}" }, - .{ .HAVE_EPOLL_CREATE1, "#include \nint main(){epoll_create1(0);}" }, - .{ .HAVE_ERF, "#include \nint main(){erf(1.0);}" }, - .{ .HAVE_ERFC, "#include \nint main(){erfc(1.0);}" }, - .{ .HAVE_EVENTFD, "#include \nint main(){eventfd(0, 0);}" }, - .{ .HAVE_EXECV, "#include \nint main(){execv(0, 0);}" }, - .{ .HAVE_EXPLICIT_BZERO, "#include \nint main(){explicit_bzero(0, 0);}" }, - .{ .HAVE_EXPM1, "#include \nint main(){expm1(1.0);}" }, - .{ .HAVE_FACCESSAT, "#include \nint main(){faccessat(0, 0, 0, 0);}" }, - .{ .HAVE_FCHDIR, "#include \nint main(){fchdir(0);}" }, - .{ .HAVE_FCHMOD, "#include \nint main(){fchmod(0, 0);}" }, - .{ .HAVE_FCHMODAT, "#include \nint main(){fchmodat(0, 0, 0, 0);}" }, - .{ .HAVE_FCHOWN, "#include \nint main(){fchown(0, 0, 0);}" }, - .{ .HAVE_FCHOWNAT, "#include \nint main(){fchownat(0, 0, 0, 0, 0);}" }, - .{ .HAVE_FDATASYNC, "#include \nint main(){fdatasync(0);}" }, - .{ .HAVE_FDOPENDIR, "#include \nint main(){fdopendir(0);}" }, - .{ .HAVE_FEXECVE, "#include \nint main(){fexecve(0, 0, 0);}" }, - .{ .HAVE_FLOCK, "#include \nint main(){flock(0, 0);}" }, - .{ .HAVE_FORK, "#include \nint main(){fork();}" }, - .{ .HAVE_FORKPTY, "#include \nint main(){forkpty(0, 0, 0, 0);}" }, - .{ .HAVE_FPATHCONF, "#include \nint main(){fpathconf(0, 0);}" }, - .{ .HAVE_FSEEKO, "#include \nint main(){fseeko(0, 0, 0);}" }, - .{ .HAVE_FSTATAT, "#include \nint main(){struct stat st; fstatat(0, 0, &st, 0);}" }, - .{ .HAVE_FSTATVFS, "#include \nint main(){struct statvfs st; fstatvfs(0, &st);}" }, - .{ .HAVE_FSYNC, "#include \nint main(){fsync(0);}" }, - .{ .HAVE_FTELLO, "#include \nint main(){ftello(0);}" }, - .{ .HAVE_FTIME, "#include \nint main(){struct timeb tb; ftime(&tb);}" }, - .{ .HAVE_FTRUNCATE, "#include \nint main(){ftruncate(0, 0);}" }, - .{ .HAVE_FUTIMENS, "#include \nint main(){futimens(0, 0);}" }, - .{ .HAVE_FUTIMES, "#include \nint main(){futimes(0, 0);}" }, - .{ .HAVE_FUTIMESAT, "#include \nint main(){futimesat(0, 0, 0);}" }, - .{ .HAVE_GAI_STRERROR, "#include \nint main(){gai_strerror(0);}" }, - .{ .HAVE_GETADDRINFO, "#include \nint main(){getaddrinfo(0, 0, 0, 0);}" }, - .{ .HAVE_GETC_UNLOCKED, "#include \nint main(){getc_unlocked(0);}" }, - .{ .HAVE_GETEGID, "#include \nint main(){getegid();}" }, - .{ .HAVE_GETENTROPY, "#include \nint main(){getentropy(0, 0);}" }, - .{ .HAVE_GETEUID, "#include \nint main(){geteuid();}" }, - .{ .HAVE_GETGID, "#include \nint main(){getgid();}" }, - .{ .HAVE_GETGRGID, "#include \nint main(){getgrgid(0);}" }, - .{ .HAVE_GETGRGID_R, "#include \nint main(){struct group grp; char buf[1024]; struct group *result; getgrgid_r(0, &grp, buf, sizeof(buf), &result);}" }, - .{ .HAVE_GETGRNAM_R, "#include \nint main(){struct group grp; char buf[1024]; struct group *result; getgrnam_r(0, &grp, buf, sizeof(buf), &result);}" }, - .{ .HAVE_GETGROUPLIST, "#include \nint main(){int groups; int ngroups = 1; getgrouplist(0, 0, &groups, &ngroups);}" }, - .{ .HAVE_GETGROUPS, "#include \nint main(){getgroups(0, 0);}" }, - .{ .HAVE_GETHOSTBYADDR, "#include \nint main(){gethostbyaddr(0, 0, 0);}" }, - .{ .HAVE_GETHOSTBYNAME, "#include \nint main(){gethostbyname(0);}" }, - .{ .HAVE_GETHOSTBYNAME_R, "#include \nint main(){struct hostent he; char buf[1024]; struct hostent *result; int h_errno; gethostbyname_r(0, &he, buf, sizeof(buf), &result, &h_errno);}" }, - .{ .HAVE_GETHOSTNAME, "#include \nint main(){gethostname(0, 0);}" }, - .{ .HAVE_GETITIMER, "#include \nint main(){struct itimerval it; getitimer(0, &it);}" }, - .{ .HAVE_GETLOADAVG, "#include \nint main(){double loadavg[3]; getloadavg(loadavg, 3);}" }, - .{ .HAVE_GETLOGIN, "#include \nint main(){getlogin();}" }, - .{ .HAVE_GETNAMEINFO, "#include \nint main(){getnameinfo(0, 0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_GETPAGESIZE, "#include \nint main(){getpagesize();}" }, - .{ .HAVE_GETPEERNAME, "#include \nint main(){getpeername(0, 0, 0);}" }, - .{ .HAVE_GETPGID, "#include \nint main(){getpgid(0);}" }, - .{ .HAVE_GETPGRP, "#include \nint main(){getpgrp();}" }, - .{ .HAVE_GETPID, "#include \nint main(){getpid();}" }, - .{ .HAVE_GETPPID, "#include \nint main(){getppid();}" }, - .{ .HAVE_GETPRIORITY, "#include \nint main(){getpriority(0, 0);}" }, - .{ .HAVE_GETPROTOBYNAME, "#include \nint main(){getprotobyname(0);}" }, - .{ .HAVE_GETPWENT, "#include \nint main(){getpwent();}" }, - .{ .HAVE_GETPWNAM_R, "#include \nint main(){struct passwd pw; char buf[1024]; struct passwd *result; getpwnam_r(0, &pw, buf, sizeof(buf), &result);}" }, - .{ .HAVE_GETPWUID, "#include \nint main(){getpwuid(0);}" }, - .{ .HAVE_GETPWUID_R, "#include \nint main(){struct passwd pw; char buf[1024]; struct passwd *result; getpwuid_r(0, &pw, buf, sizeof(buf), &result);}" }, - .{ .HAVE_GETRANDOM, "#include \nint main(){getrandom(0, 0, 0);}" }, - .{ .HAVE_GETRESGID, "#include \nint main(){gid_t rgid, egid, sgid; getresgid(&rgid, &egid, &sgid);}" }, - .{ .HAVE_GETRESUID, "#define _GNU_SOURCE\n#include \nint main(){uid_t ruid, euid, suid; getresuid(&ruid, &euid, &suid);}" }, - .{ .HAVE_GETRUSAGE, "#include \nint main(){struct rusage ru; getrusage(0, &ru);}" }, - .{ .HAVE_GETSERVBYNAME, "#include \nint main(){getservbyname(0, 0);}" }, - .{ .HAVE_GETSERVBYPORT, "#include \nint main(){getservbyport(0, 0);}" }, - .{ .HAVE_GETSID, "#include \nint main(){getsid(0);}" }, - .{ .HAVE_GETSOCKNAME, "#include \nint main(){getsockname(0, 0, 0);}" }, - .{ .HAVE_GETSPENT, "#include \nint main(){getspent();}" }, - .{ .HAVE_GETSPNAM, "#include \nint main(){getspnam(0);}" }, - .{ .HAVE_GETUID, "#include \nint main(){getuid();}" }, - .{ .HAVE_GETWD, "#include \nint main(){getwd(0);}" }, - .{ .HAVE_HSTRERROR, "#include \nint main(){hstrerror(0);}" }, - .{ .HAVE_HTOLE64, "#include \nint main(){htole64(0);}" }, - .{ .HAVE_IF_NAMEINDEX, "#include \nint main(){if_nameindex();}" }, - .{ .HAVE_INET_ATON, "#include \nint main(){struct in_addr addr; inet_aton(0, &addr);}" }, - .{ .HAVE_INET_NTOA, "#include \nint main(){struct in_addr addr; inet_ntoa(addr);}" }, - .{ .HAVE_INET_PTON, "#include \nint main(){inet_pton(0, 0, 0);}" }, - .{ .HAVE_INITGROUPS, "#include \nint main(){initgroups(0, 0);}" }, - .{ .HAVE_KILL, "#include \nint main(){kill(0, 0);}" }, - .{ .HAVE_KILLPG, "#include \nint main(){killpg(0, 0);}" }, - .{ .HAVE_LCHOWN, "#include \nint main(){lchown(0, 0, 0);}" }, - .{ .HAVE_LINK, "#include \nint main(){link(0, 0);}" }, - .{ .HAVE_LINKAT, "#include \nint main(){linkat(0, 0, 0, 0, 0);}" }, - .{ .HAVE_LISTEN, "#include \nint main(){listen(0, 0);}" }, - .{ .HAVE_LOCKF, "#include \nint main(){lockf(0, 0, 0);}" }, - .{ .HAVE_LOG1P, "#include \nint main(){log1p(1.0);}" }, - .{ .HAVE_LOG2, "#include \nint main(){log2(2.0);}" }, - .{ .HAVE_LOGIN_TTY, "#include \nint main(){login_tty(0);}" }, - .{ .HAVE_LSTAT, "#include \nint main(){struct stat st; lstat(0, &st);}" }, - .{ .HAVE_LUTIMES, "#include \nint main(){lutimes(0, 0);}" }, - .{ .HAVE_MADVISE, "#include \nint main(){madvise(0, 0, 0);}" }, - .{ .HAVE_MAKEDEV, "#include \nint main(){makedev(0, 0);}" }, - .{ .HAVE_MBRTOWC, "#include \nint main(){wchar_t wc; mbrtowc(&wc, 0, 0, 0);}" }, - .{ .HAVE_MEMFD_CREATE, "#include \nint main(){memfd_create(0, 0);}" }, - .{ .HAVE_MEMRCHR, "#include \nint main(){memrchr(0, 0, 0);}" }, - .{ .HAVE_MKDIRAT, "#include \nint main(){mkdirat(0, 0, 0);}" }, - .{ .HAVE_MKFIFO, "#include \nint main(){mkfifo(0, 0);}" }, - .{ .HAVE_MKFIFOAT, "#include \nint main(){mkfifoat(0, 0, 0);}" }, - .{ .HAVE_MKNOD, "#include \nint main(){mknod(0, 0, 0);}" }, - .{ .HAVE_MKNODAT, "#include \nint main(){mknodat(0, 0, 0, 0);}" }, - .{ .HAVE_MKTIME, "#include \nint main(){struct tm tm; mktime(&tm);}" }, - .{ .HAVE_MMAP, "#include \nint main(){mmap(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_MREMAP, "#define _GNU_SOURCE\n#include \nint main(){mremap(0, 0, 0, 0);}" }, - .{ .HAVE_NANOSLEEP, "#include \nint main(){struct timespec ts = {0, 0}; nanosleep(&ts, 0);}" }, - .{ .HAVE_NICE, "#include \nint main(){nice(0);}" }, - .{ .HAVE_OPENAT, "#include \nint main(){openat(0, 0, 0);}" }, - .{ .HAVE_OPENDIR, "#include \nint main(){opendir(0);}" }, - .{ .HAVE_OPENPTY, "#include \nint main(){openpty(0, 0, 0, 0, 0);}" }, - .{ .HAVE_PATHCONF, "#include \nint main(){pathconf(0, 0);}" }, - .{ .HAVE_PAUSE, "#include \nint main(){pause();}" }, - .{ .HAVE_PIPE, "#include \nint main(){int pipefd[2]; pipe(pipefd);}" }, - .{ .HAVE_PIPE2, "#include \nint main(){int pipefd[2]; pipe2(pipefd, 0);}" }, - .{ .HAVE_POLL, "#include \nint main(){poll(0, 0, 0);}" }, - .{ .HAVE_POSIX_FADVISE, "#include \nint main(){posix_fadvise(0, 0, 0, 0);}" }, - .{ .HAVE_POSIX_FALLOCATE, "#include \nint main(){posix_fallocate(0, 0, 0);}" }, - .{ .HAVE_POSIX_SPAWN, "#include \nint main(){posix_spawn(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_POSIX_SPAWNP, "#include \nint main(){posix_spawnp(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_PREAD, "#include \nint main(){pread(0, 0, 0, 0);}" }, - .{ .HAVE_PREADV, "#include \nint main(){preadv(0, 0, 0, 0);}" }, - .{ .HAVE_PREADV2, "#include \nint main(){preadv2(0, 0, 0, 0, 0);}" }, - .{ .HAVE_PRLIMIT, "#include \n#include \nint main(){prlimit(0, 0, 0, 0);}" }, - .{ .HAVE_PTHREAD_KILL, "#include \nint main(){pthread_kill(0, 0);}" }, - .{ .HAVE_PTHREAD_SIGMASK, "#include \nint main(){pthread_sigmask(0, 0, 0);}" }, - .{ .HAVE_PWRITE, "#include \nint main(){pwrite(0, 0, 0, 0);}" }, - .{ .HAVE_PWRITEV, "#include \nint main(){pwritev(0, 0, 0, 0);}" }, - .{ .HAVE_PWRITEV2, "#include \nint main(){pwritev2(0, 0, 0, 0, 0);}" }, - .{ .HAVE_READLINK, "#include \nint main(){readlink(0, 0, 0);}" }, - .{ .HAVE_READLINKAT, "#include \nint main(){readlinkat(0, 0, 0, 0);}" }, - .{ .HAVE_READV, "#include \nint main(){readv(0, 0, 0);}" }, - .{ .HAVE_REALPATH, "#include \nint main(){realpath(0, 0);}" }, - .{ .HAVE_RECVFROM, "#include \nint main(){recvfrom(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_RENAMEAT, "#include \nint main(){renameat(0, 0, 0, 0);}" }, - .{ .HAVE_SCHED_GET_PRIORITY_MAX, "#include \nint main(){sched_get_priority_max(0);}" }, - .{ .HAVE_SCHED_RR_GET_INTERVAL, "#include \nint main(){struct timespec ts; sched_rr_get_interval(0, &ts);}" }, - .{ .HAVE_SCHED_SETAFFINITY, "#define _GNU_SOURCE\n#include \nint main(){sched_setaffinity(0, 0, 0);}" }, - .{ .HAVE_SCHED_SETPARAM, "#include \nint main(){struct sched_param sp; sched_setparam(0, &sp);}" }, - .{ .HAVE_SCHED_SETSCHEDULER, "#include \nint main(){struct sched_param sp; sched_setscheduler(0, 0, &sp);}" }, - .{ .HAVE_SEM_CLOCKWAIT, "#include \nint main(){sem_clockwait(0, 0, 0);}" }, - .{ .HAVE_SEM_GETVALUE, "#include \nint main(){sem_getvalue(0, 0);}" }, - .{ .HAVE_SEM_OPEN, "#include \nint main(){sem_open(0, 0);}" }, - .{ .HAVE_SEM_TIMEDWAIT, "#include \nint main(){sem_timedwait(0, 0);}" }, - .{ .HAVE_SEM_UNLINK, "#include \nint main(){sem_unlink(0);}" }, - .{ .HAVE_SENDFILE, "#include \nint main(){sendfile(0, 0, 0, 0);}" }, - .{ .HAVE_SENDTO, "#include \nint main(){sendto(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_SETEGID, "#include \nint main(){setegid(0);}" }, - .{ .HAVE_SETEUID, "#include \nint main(){seteuid(0);}" }, - .{ .HAVE_SETGID, "#include \nint main(){setgid(0);}" }, - .{ .HAVE_SETGROUPS, "#include \nint main(){setgroups(0, 0);}" }, - .{ .HAVE_SETHOSTNAME, "#include \nint main(){sethostname(0, 0);}" }, - .{ .HAVE_SETITIMER, "#include \nint main(){struct itimerval it; setitimer(0, &it, 0);}" }, - .{ .HAVE_SETLOCALE, "#include \nint main(){setlocale(0, 0);}" }, - .{ .HAVE_SETPGID, "#include \nint main(){setpgid(0, 0);}" }, - .{ .HAVE_SETPGRP, "#include \nint main(){setpgrp();}" }, - .{ .HAVE_SETPRIORITY, "#include \nint main(){setpriority(0, 0, 0);}" }, - .{ .HAVE_SETREGID, "#include \nint main(){setregid(0, 0);}" }, - .{ .HAVE_SETRESGID, "#include \nint main(){setresgid(0, 0, 0);}" }, - .{ .HAVE_SETRESUID, "#define _GNU_SOURCE\n#include \nint main(){setresuid(0, 0, 0);}" }, - .{ .HAVE_SETREUID, "#include \nint main(){setreuid(0, 0);}" }, - .{ .HAVE_SETSID, "#include \nint main(){setsid();}" }, - .{ .HAVE_SETSOCKOPT, "#include \nint main(){setsockopt(0, 0, 0, 0, 0);}" }, - .{ .HAVE_SETUID, "#include \nint main(){setuid(0);}" }, - .{ .HAVE_SETVBUF, "#include \nint main(){setvbuf(0, 0, 0, 0);}" }, - .{ .HAVE_SHM_OPEN, "#include \nint main(){shm_open(0, 0, 0);}" }, - .{ .HAVE_SHM_UNLINK, "#include \nint main(){shm_unlink(0);}" }, - .{ .HAVE_SHUTDOWN, "#include \nint main(){shutdown(0, 0);}" }, - .{ .HAVE_SIGACTION, "#include \nint main(){struct sigaction sa; sigaction(0, &sa, 0);}" }, - .{ .HAVE_SIGALTSTACK, "#include \nint main(){sigaltstack(0, 0);}" }, - .{ .HAVE_SIGFILLSET, "#include \nint main(){sigset_t set; sigfillset(&set);}" }, - .{ .HAVE_SIGINTERRUPT, "#include \nint main(){siginterrupt(0, 0);}" }, - .{ .HAVE_SIGPENDING, "#include \nint main(){sigset_t set; sigpending(&set);}" }, - .{ .HAVE_SIGRELSE, "#include \nint main(){sigrelse(0);}" }, - .{ .HAVE_SIGTIMEDWAIT, "#include \nint main(){sigset_t set; sigtimedwait(&set, 0, 0);}" }, - .{ .HAVE_SIGWAIT, "#include \nint main(){sigset_t set; sigwait(&set, 0);}" }, - .{ .HAVE_SIGWAITINFO, "#include \nint main(){sigset_t set; sigwaitinfo(&set, 0);}" }, - .{ .HAVE_SNPRINTF, "#include \nint main(){snprintf(0, 0, 0);}" }, - .{ .HAVE_SOCKET, "#include \nint main(){socket(0, 0, 0);}" }, - .{ .HAVE_SOCKETPAIR, "#include \nint main(){int sv[2]; socketpair(0, 0, 0, sv);}" }, - .{ .HAVE_SPLICE, "#define _GNU_SOURCE\n#include \nint main(){splice(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_STATVFS, "#include \nint main(){struct statvfs st; statvfs(0, &st);}" }, - .{ .HAVE_STRFTIME, "#include \nint main(){struct tm tm; strftime(0, 0, 0, &tm);}" }, - .{ .HAVE_STRSIGNAL, "#include \nint main(){strsignal(0);}" }, - .{ .HAVE_SYMLINK, "#include \nint main(){symlink(0, 0);}" }, - .{ .HAVE_SYMLINKAT, "#include \nint main(){symlinkat(0, 0, 0);}" }, - .{ .HAVE_SYNC, "#include \nint main(){sync();}" }, - .{ .HAVE_SYSCONF, "#include \nint main(){sysconf(0);}" }, - .{ .HAVE_SYSTEM, "#include \nint main(){system(0);}" }, - .{ .HAVE_TCGETPGRP, "#include \nint main(){tcgetpgrp(0);}" }, - .{ .HAVE_TCSETPGRP, "#include \nint main(){tcsetpgrp(0, 0);}" }, - .{ .HAVE_TEMPNAM, "#include \nint main(){tempnam(0, 0);}" }, - .{ .HAVE_TIMEGM, "#include \nint main(){struct tm tm; timegm(&tm);}" }, - .{ .HAVE_TIMES, "#include \nint main(){struct tms tms; times(&tms);}" }, - .{ .HAVE_TMPFILE, "#include \nint main(){tmpfile();}" }, - .{ .HAVE_TMPNAM, "#include \nint main(){tmpnam(0);}" }, - .{ .HAVE_TMPNAM_R, "#include \nint main(){tmpnam_r(0);}" }, - .{ .HAVE_TRUNCATE, "#include \nint main(){truncate(0, 0);}" }, - .{ .HAVE_UMASK, "#include \nint main(){umask(0);}" }, - .{ .HAVE_UNAME, "#include \nint main(){struct utsname uts; uname(&uts);}" }, - .{ .HAVE_UNLINKAT, "#include \nint main(){unlinkat(0, 0, 0);}" }, - .{ .HAVE_UTIMENSAT, "#include \nint main(){utimensat(0, 0, 0, 0);}" }, - .{ .HAVE_UTIMES, "#include \nint main(){utimes(0, 0);}" }, - .{ .HAVE_VFORK, "#include \nint main(){vfork();}" }, - .{ .HAVE_WAIT, "#include \nint main(){wait(0);}" }, - .{ .HAVE_WAIT3, "#include \nint main(){wait3(0, 0, 0);}" }, - .{ .HAVE_WAIT4, "#include \nint main(){wait4(0, 0, 0, 0);}" }, - .{ .HAVE_WAITID, "#include \nint main(){waitid(0, 0, 0, 0);}" }, - .{ .HAVE_WAITPID, "#include \nint main(){waitpid(0, 0, 0);}" }, - .{ .HAVE_WCSCOLL, "#include \nint main(){wcscoll(0, 0);}" }, - .{ .HAVE_WCSFTIME, "#include \nint main(){wcsftime(0, 0, 0, 0);}" }, - .{ .HAVE_WCSXFRM, "#include \nint main(){wcsxfrm(0, 0, 0);}" }, - .{ .HAVE_WMEMCMP, "#include \nint main(){wmemcmp(0, 0, 0);}" }, - .{ .HAVE_WRITEV, "#include \nint main(){writev(0, 0, 0);}" }, - .{ .HAVE_UUID_CREATE, "#include \nint main(){uuid_create(0,0);}" }, - // BSD/macOS specific functions - .{ .HAVE_CHFLAGS, "#include \nint main(){chflags(0, 0);}" }, - .{ .HAVE_LCHFLAGS, "#include \nint main(){lchflags(0, 0);}" }, - .{ .HAVE_LCHMOD, "#include \nint main(){lchmod(0, 0);}" }, - .{ .HAVE_KQUEUE, "#include \nint main(){kqueue();}" }, - .{ .HAVE_FDWALK, "#include \nint main(){fdwalk(0, 0);}" }, - .{ .HAVE_FORK1, "#include \n#include\nint main(){fork1();}" }, - .{ .HAVE_GETPGRP, "#include \nint main(){getpgrp();}" }, - .{ .HAVE_PLOCK, "#include \nint main(){plock(0);}" }, - .{ .HAVE_RTPSPAWN, "#include \nint main(){rtpspawn(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_STRLCPY, "#include \nint main(){strlcpy(0, 0, 0);}" }, - .{ .HAVE__GETPTY, "#include \nint main(){_getpty(0, 0, 0, 0);}" }, - // Additional math functions - .{ .HAVE_FSEEK64, "#include \nint main(){fseek64(0, 0, 0);}" }, - .{ .HAVE_FTELL64, "#include \nint main(){ftell64(0);}" }, - // Library functions - .{ .HAVE_CRYPT_R, "#include \nint main(){struct crypt_data cd; crypt_r(0, 0, &cd);}" }, - .{ .HAVE_CTERMID_R, "#include \nint main(){ctermid_r(0, 0);}" }, - .{ .HAVE_EXPLICIT_MEMSET, "#include \nint main(){explicit_memset(0, 0, 0);}" }, - // UUID functions - .{ .HAVE_UUID_ENC_BE, "#include \nint main(){uuid_enc_be(0, 0);}" }, - .{ .HAVE_UUID_GENERATE_TIME_SAFE, "#include \nint main(){uuid_generate_time_safe(0);}" }, - // Various other functions - .{ .HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH, "#include \nint main(){_dyld_shared_cache_contains_path(0);}" }, - // Curses functions - .{ .HAVE_CURSES_FILTER, "#include \nint main(){filter();}" }, - .{ .HAVE_CURSES_HAS_KEY, "#include \nint main(){has_key(0);}" }, - .{ .HAVE_CURSES_IMMEDOK, "#include \nint main(){immedok(0, 0);}" }, - .{ .HAVE_CURSES_IS_PAD, "#include \nint main(){is_pad(0);}" }, - .{ .HAVE_CURSES_IS_TERM_RESIZED, "#include \nint main(){is_term_resized(0, 0);}" }, - .{ .HAVE_CURSES_RESIZETERM, "#include \nint main(){resizeterm(0, 0);}" }, - .{ .HAVE_CURSES_RESIZE_TERM, "#include \nint main(){resize_term(0, 0);}" }, - .{ .HAVE_CURSES_SYNCOK, "#include \nint main(){syncok(0, 0);}" }, - .{ .HAVE_CURSES_TYPEAHEAD, "#include \nint main(){typeahead(0);}" }, - .{ .HAVE_CURSES_USE_ENV, "#include \nint main(){use_env(0);}" }, - .{ .HAVE_CURSES_WCHGAT, "#include \nint main(){wchgat(0, 0, 0, 0, 0, 0);}" }, - // Readline functions - .{ .HAVE_RL_APPEND_HISTORY, "#include \nint main(){append_history(0, 0);}" }, - .{ .HAVE_RL_CATCH_SIGNAL, "#include \nint main(){rl_catch_signals = 0;}" }, - .{ .HAVE_RL_COMPLETION_MATCHES, "#include \nint main(){rl_completion_matches(0, 0);}" }, - .{ .HAVE_RL_RESIZE_TERMINAL, "#include \nint main(){rl_resize_terminal();}" }, - // Pthread functions - .{ .HAVE_PTHREAD_CONDATTR_SETCLOCK, "#include \nint main(){pthread_condattr_t attr; pthread_condattr_setclock(&attr, 0);}" }, - .{ .HAVE_PTHREAD_GETCPUCLOCKID, "#include \nint main(){clockid_t clk; pthread_getcpuclockid(0, &clk);}" }, - // Header includes and type checks - .{ .HAVE_ADDRINFO, "#include \nint main(){struct addrinfo ai; return 0;}" }, - .{ .HAVE_ALIGNED_REQUIRED, "#include \nint main(){char a[1]; return ((size_t)a) & 1;}" }, - .{ .HAVE_ALTZONE, "#include \nint main(){return altzone;}" }, - - // Broken function tests (these typically compile but have runtime issues) - // .{ .HAVE_BROKEN_MBSTOWCS, "#include \n#include \nint main(){return mbstowcs(NULL, \"test\", 0) == 0;}" }, - // .{ .HAVE_BROKEN_NICE, "#include \nint main(){nice(1); return 0;}" }, - // .{ .HAVE_BROKEN_PIPE_BUF, "#include \nint main(){return PIPE_BUF;}" }, - // .{ .HAVE_BROKEN_POLL, "#include \nint main(){struct pollfd pfd; poll(&pfd, 1, 0); return 0;}" }, - // .{ .HAVE_BROKEN_POSIX_SEMAPHORES, "#include \nint main(){sem_t sem; return 0;}" }, - // .{ .HAVE_BROKEN_PTHREAD_SIGMASK, "#include \n#include \nint main(){sigset_t set; pthread_sigmask(SIG_BLOCK, &set, NULL); return 0;}" }, - // .{ .HAVE_BROKEN_SEM_GETVALUE, "#include \nint main(){sem_t sem; int val; sem_getvalue(&sem, &val); return 0;}" }, - // .{ .HAVE_BROKEN_UNSETENV, "#include \nint main(){unsetenv(\"TEST\"); return 0;}" }, - - // Compiler builtin features - .{ .HAVE_BUILTIN_ATOMIC, "int main(){int x = 0; __atomic_store_n(&x, 1, __ATOMIC_SEQ_CST); return __atomic_load_n(&x, __ATOMIC_SEQ_CST);}" }, - .{ .HAVE_COMPUTED_GOTOS, "int main(){void *ptr = &&label; goto *ptr; label: return 0;}" }, - - // RTLD declarations - .{ .HAVE_DECL_RTLD_DEEPBIND, "#include \nint main(){return RTLD_DEEPBIND;}" }, - .{ .HAVE_DECL_RTLD_GLOBAL, "#include \nint main(){return RTLD_GLOBAL;}" }, - .{ .HAVE_DECL_RTLD_LAZY, "#include \nint main(){return RTLD_LAZY;}" }, - .{ .HAVE_DECL_RTLD_LOCAL, "#include \nint main(){return RTLD_LOCAL;}" }, - .{ .HAVE_DECL_RTLD_MEMBER, "#include \nint main(){return RTLD_MEMBER;}" }, - .{ .HAVE_DECL_RTLD_NODELETE, "#include \nint main(){return RTLD_NODELETE;}" }, - .{ .HAVE_DECL_RTLD_NOLOAD, "#include \nint main(){return RTLD_NOLOAD;}" }, - .{ .HAVE_DECL_RTLD_NOW, "#include \nint main(){return RTLD_NOW;}" }, - .{ .HAVE_DECL_TZNAME, "#include \nint main(){return tzname[0] != NULL;}" }, - - // Device and file system features - .{ .HAVE_DEVICE_MACROS, "#include \n#include \nint main(){dev_t d = 0; return major(d) | minor(d);}" }, - .{ .HAVE_DEV_PTC, "#include \nint main(){return open(\"/dev/ptc\", O_RDWR);}" }, - .{ .HAVE_DEV_PTMX, "#include \nint main(){return open(\"/dev/ptmx\", O_RDWR);}" }, - .{ .HAVE_DIRENT_D_TYPE, "#include \nint main(){struct dirent d; return d.d_type;}" }, - .{ .HAVE_DIRFD, "#include \nint main(){DIR *d = NULL; return dirfd(d);}" }, - - // System features - .{ .HAVE_DYNAMIC_LOADING, "#include \nint main(){dlopen(\"test\", RTLD_NOW); return 0;}" }, - .{ .HAVE_EPOLL, "#include \nint main(){return epoll_create(1);}" }, - - // GCC assembly features - .{ .HAVE_GCC_ASM_FOR_MC68881, "int main(){unsigned int fpcr; __asm__(\"fmove.l %%fpcr,%0\" : \"=dm\" (fpcr)); return 0;}" }, - .{ .HAVE_GCC_ASM_FOR_X64, "int main(){unsigned long rax; __asm__(\"movq %%rax, %0\" : \"=r\" (rax)); return 0;}" }, - .{ .HAVE_GCC_ASM_FOR_X87, "int main(){unsigned int cw; __asm__(\"fnstcw %0\" : \"=m\" (cw)); return 0;}" }, - .{ .HAVE_GCC_UINT128_T, "int main(){__uint128_t x = 0; return (int)x;}" }, - - // gethostbyname_r variants - .{ .HAVE_GETHOSTBYNAME_R_3_ARG, "#include \nint main(){struct hostent_data hed; gethostbyname_r(\"localhost\", NULL, &hed); return 0;}" }, - .{ .HAVE_GETHOSTBYNAME_R_5_ARG, "#include \nint main(){struct hostent he; char buf[1024]; gethostbyname_r(\"localhost\", &he, buf, sizeof(buf), NULL); return 0;}" }, - .{ .HAVE_GETHOSTBYNAME_R_6_ARG, "#include \nint main(){struct hostent he; char buf[1024]; struct hostent *result; int h_errno; gethostbyname_r(\"localhost\", &he, buf, sizeof(buf), &result, &h_errno); return 0;}" }, - - // System calls and features - .{ .HAVE_GETRANDOM_SYSCALL, "#include \n#include \nint main(){return syscall(SYS_getrandom, NULL, 0, 0);}" }, - - // Bug detection - .{ .HAVE_GLIBC_MEMMOVE_BUG, "#include \nint main(){char buf[10]; memmove(buf+1, buf, 5); return 0;}" }, - .{ .HAVE_IPA_PURE_CONST_BUG, "int main(){return 0;}" }, // Compiler-specific, hard to test - - // File and library support - .{ .HAVE_LARGEFILE_SUPPORT, "#include \nint main(){off_t offset = 0; return sizeof(offset) > 4;}" }, - .{ .HAVE_LIBB2, "#include \nint main(){blake2b_state state; blake2s_final(&state, 0, 0); return 0;}" }, - .{ .HAVE_LIBDB, "#include \nint main(){DB *db; return 0;}" }, - .{ .HAVE_LIBDL, "#include \nint main(){dlopen(\"test\", RTLD_NOW); return 0;}" }, - .{ .HAVE_LIBDLD, "int main(){return 0;}" }, // DLD library check - .{ .HAVE_LIBIEEE, "int main(){return 0;}" }, // IEEE math library - .{ .HAVE_LIBRESOLV, "#include \nint main(){res_init(); return 0;}" }, - .{ .HAVE_LIBSENDFILE, "#include \nint main(){sendfile(0, 0, NULL, 0); return 0;}" }, - .{ .HAVE_LIBSQLITE3, "#include \nint main(){sqlite3 *db; return 0;}" }, - - // Linux-specific headers - .{ .HAVE_LINUX_AUXVEC_H, "#include \nint main(){return AT_NULL;}" }, - .{ .HAVE_LINUX_CAN_BCM_H, "#include \nint main(){struct bcm_msg_head msg; return 0;}" }, - .{ .HAVE_LINUX_CAN_H, "#include \nint main(){struct can_frame frame; return 0;}" }, - .{ .HAVE_LINUX_CAN_J1939_H, "#include \nint main(){return J1939_MAX_UNICAST_ADDR;}" }, - .{ .HAVE_LINUX_CAN_RAW_FD_FRAMES, "#include \nint main(){return CAN_RAW_FD_FRAMES;}" }, - .{ .HAVE_LINUX_CAN_RAW_H, "#include \nint main(){return CAN_RAW_FILTER;}" }, - .{ .HAVE_LINUX_CAN_RAW_JOIN_FILTERS, "#include \nint main(){return CAN_RAW_JOIN_FILTERS;}" }, - .{ .HAVE_LINUX_MEMFD_H, "#include \nint main(){return MFD_CLOEXEC;}" }, - .{ .HAVE_LINUX_NETLINK_H, "#include \nint main(){struct nlmsghdr nlh; return 0;}" }, - .{ .HAVE_LINUX_QRTR_H, "#include \nint main(){struct sockaddr_qrtr sq; return 0;}" }, - .{ .HAVE_LINUX_RANDOM_H, "#include \nint main(){return GRND_NONBLOCK;}" }, - .{ .HAVE_LINUX_SOUNDCARD_H, "#include \nint main(){return SOUND_VERSION;}" }, - .{ .HAVE_LINUX_TIPC_H, "#include \nint main(){struct sockaddr_tipc addr; return 0;}" }, - .{ .HAVE_LINUX_VM_SOCKETS_H, "#include \nint main(){return VMADDR_CID_ANY;}" }, - .{ .HAVE_LINUX_WAIT_H, "#include \nint main(){return 0;}" }, - - // Type support - .{ .HAVE_LONG_DOUBLE, "int main(){long double x = 0.0L; return (int)x;}" }, - // .{ .HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION, "#include \nint main(){wchar_t w = L'\\x80'; return (int)w;}" }, - .{ .HAVE_PROTOTYPES, "int test(int x); int main(){return test(0);} int test(int x){return x;}" }, - - // pthread features - .{ .HAVE_PTHREAD_DESTRUCTOR, "#include \nvoid destructor(void *); int main(){pthread_key_create(NULL, destructor); return 0;} void destructor(void *p){}" }, - .{ .HAVE_PTHREAD_INIT, "#include \nint main(){pthread_init(); return 0;}" }, - // .{ .HAVE_PTHREAD_STUBS, "#include \nint main(){pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; return 0;}" }, - - // Readline features - .{ .HAVE_RL_COMPLETION_APPEND_CHARACTER, "#include \nint main(){rl_completion_append_character = ' '; return 0;}" }, - .{ .HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK, "#include \nint main(){rl_completion_display_matches_hook = NULL; return 0;}" }, - .{ .HAVE_RL_COMPLETION_SUPPRESS_APPEND, "#include \nint main(){rl_completion_suppress_append = 1; return 0;}" }, - .{ .HAVE_RL_PRE_INPUT_HOOK, "#include \nint main(){rl_pre_input_hook = NULL; return 0;}" }, - - // Signal and socket features - .{ .HAVE_SIGINFO_T_SI_BAND, "#include \nint main(){siginfo_t si; return si.si_band;}" }, - .{ .HAVE_SOCKADDR_ALG, "#include \nint main(){struct sockaddr_alg sa; return 0;}" }, - .{ .HAVE_SOCKADDR_SA_LEN, "#include \nint main(){struct sockaddr sa; return sa.sa_len;}" }, - .{ .HAVE_SOCKADDR_STORAGE, "#include \nint main(){struct sockaddr_storage ss; return 0;}" }, - - // Type definitions - .{ .HAVE_SSIZE_T, "#include \nint main(){ssize_t s = 0; return (int)s;}" }, - .{ .HAVE_STAT_TV_NSEC, "#include \nint main(){struct stat st; return st.st_mtim.tv_nsec;}" }, - .{ .HAVE_STAT_TV_NSEC2, "#include \nint main(){struct stat st; return st.st_mtimensec;}" }, - .{ .HAVE_STD_ATOMIC, "#include \nint main(){atomic_int x; atomic_uintptr_t y; return 0;}" }, - - // Struct member checks - .{ .HAVE_STRUCT_PASSWD_PW_GECOS, "#include \nint main(){struct passwd pw; return pw.pw_gecos != NULL;}" }, - .{ .HAVE_STRUCT_PASSWD_PW_PASSWD, "#include \nint main(){struct passwd pw; return pw.pw_passwd != NULL;}" }, - .{ .HAVE_STRUCT_STAT_ST_BIRTHTIME, "#include \nint main(){struct stat st; return st.st_birthtime;}" }, - .{ .HAVE_STRUCT_STAT_ST_BLKSIZE, "#include \nint main(){struct stat st; return st.st_blksize;}" }, - .{ .HAVE_STRUCT_STAT_ST_BLOCKS, "#include \nint main(){struct stat st; return st.st_blocks;}" }, - .{ .HAVE_STRUCT_STAT_ST_FLAGS, "#include \nint main(){struct stat st; return st.st_flags;}" }, - .{ .HAVE_STRUCT_STAT_ST_GEN, "#include \nint main(){struct stat st; return st.st_gen;}" }, - .{ .HAVE_STRUCT_STAT_ST_RDEV, "#include \nint main(){struct stat st; return st.st_rdev;}" }, - .{ .HAVE_STRUCT_TM_TM_ZONE, "#include \nint main(){struct tm t; return t.tm_zone != NULL;}" }, - - // Time and timezone features - .{ .HAVE_TM_ZONE, "#include \nint main(){struct tm t; return t.tm_zone != NULL;}" }, - .{ .HAVE_TZNAME, "#include \nint main(){return tzname[0] != NULL;}" }, - // .{ .HAVE_USABLE_WCHAR_T, "#include \nint main(){wchar_t w = L'A'; return sizeof(w) >= 2;}" }, - .{ .HAVE_WORKING_TZSET, "#include \nint main(){tzset(); return 0;}" }, - .{ .HAVE_ZLIB_COPY, "#include \nint main(){z_stream strm; inflateCopy(&strm, &strm); return 0;}" }, - }; - pub const @"3.11.13" = concatConfigs(common, .{ - .{ .HAVE_TTYNAME, "#include \nint main(){ttyname(0);}" }, - .{ .HAVE_LIBGDBM_COMPAT, "#include \nint main(){GDBM_FILE gf; return 0;}" }, - .{ .HAVE_LIBNDBM, "#include \nint main(){DBM *db; return 0;}" }, - .{ .HAVE_LIBREADLINE, "#include \nint main(){readline(\"prompt\"); return 0;}" }, - .{ .HAVE_STDARG_PROTOTYPES, "#include \nvoid test(int x, ...); int main(){test(1, 2); return 0;} void test(int x, ...){va_list ap; va_start(ap, x); va_end(ap);}" }, - }); - pub const @"3.12.11" = concatConfigs(common, .{ - .{ .HAVE_FFI_CLOSURE_ALLOC, "#include \nint main(){ffi_closure_alloc(0, 0);}" }, - .{ .HAVE_FFI_PREP_CIF_VAR, "#include \nint main(){ffi_prep_cif_var(0, 0, 0, 0, 0, 0);}" }, - .{ .HAVE_FFI_PREP_CLOSURE_LOC, "#include \nint main(){ffi_prep_closure_loc(0, 0, 0, 0, 0);}" }, - .{ .HAVE_SETNS, "#define _GNU_SOURCE\n#include \nint main(){setns(0, 0);}" }, - .{ .HAVE_TTYNAME_R, "#include \nint main(){char buf[256]; ttyname_r(0, buf, sizeof(buf));}" }, - .{ .HAVE_UNSHARE, "#define _GNU_SOURCE\n#include \nint main(){unshare(0);}" }, - }); -}; - fn addPyconfig( b: *std.Build, version: Version, upstream: *std.Build.Dependency, target: std.Build.ResolvedTarget, libs: Libs, + configquery_exe: *std.Build.Step.Compile, ) !Pyconfig { const t = target.result; if (t.os.tag == .windows) return .{ @@ -1367,6 +819,9 @@ fn addPyconfig( .HAVE_USABLE_WCHAR_T = null, .HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION = null, .HAVE_CHROOT = have(t.os.tag == .linux), + .HAVE_GCC_ASM_FOR_MC68881 = have(t.cpu.arch == .m68k), + .HAVE_GCC_ASM_FOR_X64 = have(t.cpu.arch == .x86_64), + .HAVE_GCC_ASM_FOR_X87 = have(t.cpu.arch == .x86_64 or t.cpu.arch == .x86), // Readline type (static configuration, not function test) .HAVE_RL_COMPDISP_FUNC_T = null, }); @@ -1403,65 +858,23 @@ fn addPyconfig( }), } - const header_configs: []const Config = switch (version) { - .@"3.11.13" => &header_config_set.@"3.11.13", - .@"3.12.11" => &header_config_set.@"3.12.11", - }; - const exe_configs: []const Config = switch (version) { - .@"3.11.13" => &exe_config_set.@"3.11.13", - .@"3.12.11" => &exe_config_set.@"3.12.11", - }; - { - 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, .{})); - } - } - }.make; - const add_values = b.allocator.create(AddValues) catch @panic("OOM"); - add_values.* = .{ - .step = std.Build.Step.init(.{ - .id = .custom, - .name = "add dynamic values to pyconfig header", - .owner = b, - .makeFn = &add_values_make, - }), - .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 }); - if (libs.zlib) |zlib| check.*.linkLibrary(zlib); - if (libs.openssl) |openssl| check.*.linkLibrary(openssl); - add_values.step.dependOn(&check.*.step); + const run = b.addRunArtifact(configquery_exe); + run.addArg("--zig-exe"); + run.addArg(b.graph.zig_exe); + run.addArg("--target"); + run.addArg(try target.query.zigTriple(b.allocator)); + if (b.cache_root.path) |cache_root| { + run.addArg("--cache-dir"); + run.addArg(cache_root); } - config_header.step.dependOn(&add_values.step); + run.addFileArg(b.path("config-common")); + run.addFileArg(switch (version) { + .@"3.11.13" => b.path("config-3.11.13"), + .@"3.12.11" => b.path("config-3.12.11"), + }); + run.addArg("-o"); + ConfigHeaderExt.addLazy(config_header, run.addOutputFileArg("config")); } return .{ @@ -1719,6 +1132,7 @@ fn ci( args: struct { replace_exe: *std.Build.Step.Compile, makesetup_exe: *std.Build.Step.Compile, + configquery_exe: *std.Build.Step.Compile, stage2_frozen_mods: Stage2FrozenMods, frozen_headers: []const std.Build.LazyPath, deepfreeze_c: std.Build.LazyPath, @@ -1772,7 +1186,7 @@ fn ci( const exe = addPythonExe(b, upstream, target, optimize, .{ .name = "python", .makesetup_out = makesetup, - .pyconfig = try addPyconfig(b, version, upstream, target, libs), + .pyconfig = try addPyconfig(b, version, upstream, target, libs, args.configquery_exe), .stage = .{ .final = .{ .stage2 = args.stage2_frozen_mods, .frozen_headers = args.frozen_headers, @@ -1804,4 +1218,4 @@ fn concat(allocator: std.mem.Allocator, lists: []const []const []const u8) []con } const std = @import("std"); -const CompileCheck = @import("CompileCheck.zig"); +const ConfigHeaderExt = @import("ConfigHeaderExt.zig"); diff --git a/build.zig.zon b/build.zig.zon index e56e7d9..97def5a 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -27,8 +27,10 @@ .paths = .{ "build.zig", "build.zig.zon", + "configquery.zig", "makesetup.zig", "replace.zig", - "CompileCheck.zig", + "ConfigHeaderExt.zig", + "config-*", }, } diff --git a/config-3.11.13 b/config-3.11.13 new file mode 100644 index 0000000..016a6e6 --- /dev/null +++ b/config-3.11.13 @@ -0,0 +1,13 @@ +################################################################################ +# Headers +################################################################################ +HAVE_MEMORY_H ? 1|undef include=memory.h + +################################################################################ +# Functions +################################################################################ +HAVE_TTYNAME ? 1|undef include=unistd.h int main(){ttyname(0);} +HAVE_LIBGDBM_COMPAT ? 1|undef include=gdbm.h int main(){GDBM_FILE gf; return 0;} +HAVE_LIBNDBM ? 1|undef include=ndbm.h int main(){DBM *db; return 0;} +HAVE_LIBREADLINE ? 1|undef include=readline/readline.h int main(){readline("prompt"); return 0;} +HAVE_STDARG_PROTOTYPES ? 1|undef include=stdarg.h void test(int x, ...){va_list ap; va_start(ap, x); va_end(ap);} int main(){test(1, 2); return 0;} diff --git a/config-3.12.11 b/config-3.12.11 new file mode 100644 index 0000000..e89f4b1 --- /dev/null +++ b/config-3.12.11 @@ -0,0 +1,21 @@ +################################################################################ +# Headers +################################################################################ +HAVE_EDITLINE_READLINE_H ? 1|undef include=editline/readline.h +HAVE_LINUX_FS_H ? 1|undef include=linux/fs.h +HAVE_MINIX_CONFIG_H ? 1|undef include=minix/config.h +HAVE_NET_ETHERNET_H ? 1|undef include=net/ethernet.h +HAVE_PANEL_H ? 1|undef include=panel.h +HAVE_READLINE_READLINE_H ? 1|undef include=readline/readline.h +HAVE_STDIO_H ? 1|undef include=stdio.h +HAVE_SYS_PIDFD_H ? 1|undef include=sys/pidfd.h + +################################################################################ +# Functions +################################################################################ +HAVE_FFI_CLOSURE_ALLOC ? 1|undef include=ffi.h int main(){ffi_closure_alloc(0, 0);} +HAVE_FFI_PREP_CIF_VAR ? 1|undef include=ffi.h int main(){ffi_prep_cif_var(0, 0, 0, 0, 0, 0);} +HAVE_FFI_PREP_CLOSURE_LOC ? 1|undef include=ffi.h int main(){ffi_prep_closure_loc(0, 0, 0, 0, 0);} +HAVE_SETNS ? 1|undef define=_GNU_SOURCE include=sched.h int main(){setns(0, 0);} +HAVE_TTYNAME_R ? 1|undef include=unistd.h int main(){char buf[256]; ttyname_r(0, buf, sizeof(buf));} +HAVE_UNSHARE ? 1|undef define=_GNU_SOURCE include=unistd.h int main(){unshare(0);} diff --git a/config-common b/config-common new file mode 100644 index 0000000..bd82d5b --- /dev/null +++ b/config-common @@ -0,0 +1,529 @@ +################################################################################ +# Headers +################################################################################ +HAVE_ALLOCA_H ? 1|undef include=alloca.h +HAVE_ASM_TYPES_H ? 1|undef include=asm/types.h +HAVE_BLUETOOTH_H ? 1|undef include=bluetooth.h +HAVE_BLUETOOTH_BLUETOOTH_H ? 1|undef include=bluetooth/bluetooth.h +HAVE_BZLIB_H ? 1|undef include=bzlib.h +HAVE_CONIO_H ? 1|undef include=conio.h +HAVE_CRYPT_H ? 1|undef include=crypt.h +HAVE_CURSES_H ? 1|undef include=curses.h +HAVE_DIRECT_H ? 1|undef include=direct.h +HAVE_DIRENT_H ? 1|undef include=dirent.h +HAVE_DB_H ? 1|undef include=db.h +HAVE_DLFCN_H ? 1|undef include=dlfcn.h +HAVE_ENDIAN_H ? 1|undef include=endian.h +HAVE_ERRNO_H ? 1|undef include=errno.h +HAVE_FCNTL_H ? 1|undef include=fcntl.h +HAVE_GDBM_DASH_NDBM_H ? 1|undef include=gdbm-ndbm.h +HAVE_GDBM_H ? 1|undef include=gdbm.h +HAVE_GDBM_NDBM_H ? 1|undef include=gdbm/ndbm.h +HAVE_GRP_H ? 1|undef include=grp.h +HAVE_IEEEFP_H ? 1|undef include=ieeefp.h +HAVE_IO_H ? 1|undef include=io.h +HAVE_INTTYPES_H ? 1|undef include=inttypes.h +HAVE_LANGINFO_H ? 1|undef include=langinfo.h +HAVE_LIBINTL_H ? 1|undef include=libintl.h +HAVE_LIBUTIL_H ? 1|undef include=libutil.h +HAVE_LINUX_LIMITS_H ? 1|undef include=linux/limits.h +HAVE_NETDB_H ? 1|undef include=netdb.h +HAVE_NETINET_IN_H ? 1|undef include=netinet/in.h +HAVE_NETPACKET_PACKET_H ? 1|undef include=netpacket/packet.h +HAVE_NET_IF_H ? 1|undef include=net/if.h +HAVE_POLL_H ? 1|undef include=poll.h +HAVE_PTHREAD_H ? 1|undef include=pthread.h +HAVE_PTY_H ? 1|undef include=pty.h +HAVE_SCHED_H ? 1|undef include=sched.h +HAVE_SETJMP_H ? 1|undef include=setjmp.h +HAVE_SHADOW_H ? 1|undef include=shadow.h +HAVE_SIGNAL_H ? 1|undef include=signal.h +HAVE_SPAWN_H ? 1|undef include=spawn.h +HAVE_STDINT_H ? 1|undef include=stdint.h +HAVE_STDLIB_H ? 1|undef include=stdlib.h +HAVE_STRINGS_H ? 1|undef include=strings.h +HAVE_STRING_H ? 1|undef include=string.h +HAVE_SYSEXITS_H ? 1|undef include=sysexits.h +HAVE_SYSLOG_H ? 1|undef include=syslog.h +HAVE_SYS_AUXV_H ? 1|undef include=sys/auxv.h +HAVE_SYS_EPOLL_H ? 1|undef include=sys/epoll.h +HAVE_SYS_EVENTFD_H ? 1|undef include=sys/eventfd.h +HAVE_SYS_FILE_H ? 1|undef include=sys/file.h +HAVE_SYS_IOCTL_H ? 1|undef include=sys/ioctl.h +HAVE_SYS_MMAN_H ? 1|undef include=sys/mman.h +HAVE_SYS_PARAM_H ? 1|undef include=sys/param.h +HAVE_SYS_POLL_H ? 1|undef include=sys/poll.h +HAVE_SYS_RANDOM_H ? 1|undef include=sys/random.h +HAVE_SYS_RESOURCE_H ? 1|undef include=sys/resource.h +HAVE_SYS_SELECT_H ? 1|undef include=sys/select.h +HAVE_SYS_SENDFILE_H ? 1|undef include=sys/sendfile.h +HAVE_SYS_SOCKET_H ? 1|undef include=sys/socket.h +HAVE_SYS_SOUNDCARD_H ? 1|undef include=sys/soundcard.h +HAVE_SYS_STATVFS_H ? 1|undef include=sys/statvfs.h +HAVE_SYS_STAT_H ? 1|undef include=sys/stat.h +HAVE_SYS_SYSCALL_H ? 1|undef include=sys/syscall.h +HAVE_SYS_SYSMACROS_H ? 1|undef include=sys/sysmacros.h +HAVE_SYS_TIMES_H ? 1|undef include=sys/times.h +HAVE_SYS_TIME_H ? 1|undef include=sys/time.h +HAVE_SYS_TYPES_H ? 1|undef include=sys/types.h +HAVE_SYS_UIO_H ? 1|undef include=sys/uio.h +HAVE_SYS_UN_H ? 1|undef include=sys/un.h +HAVE_SYS_UTSNAME_H ? 1|undef include=sys/utsname.h +HAVE_SYS_WAIT_H ? 1|undef include=sys/wait.h +HAVE_SYS_XATTR_H ? 1|undef include=sys/xattr.h +HAVE_TERMIOS_H ? 1|undef include=termios.h +HAVE_UNISTD_H ? 1|undef include=unistd.h +HAVE_UTIME_H ? 1|undef include=utime.h +HAVE_UTMP_H ? 1|undef include=utmp.h +HAVE_WCHAR_H ? 1|undef include=wchar.h +HAVE_LZMA_H ? 1|undef include=lzma.h +HAVE_NCURSES_H ? 1|undef include=ncurses.h +HAVE_NDBM_H ? 1|undef include=ndmb.h +HAVE_NDIR_H ? 1|undef include=ndir.h +HAVE_NETCAN_CAN_H ? 1|undef include=netcan/can.h +HAVE_PROCESS_H ? 1|undef include=process.h +HAVE_RPC_RPC_H ? 1|undef include=rpc/rpc.h +HAVE_STROPTS_H ? 1|undef include=stropts.h +HAVE_SYS_AUDIOIO_H ? 1|undef include=sys/audioio.h +HAVE_SYS_BSDTTY_H ? 1|undef include=sys/bsdtty.h +HAVE_SYS_DEVPOLL_H ? 1|undef include=sys/devpoll.h +HAVE_SYS_DIR_H ? 1|undef include=sys/dir.h +HAVE_SYS_ENDIAN_H ? 1|undef include=sys/endian.h +HAVE_SYS_EVENT_H ? 1|undef include=sys/event.h +HAVE_SYS_KERN_CONTROL_H ? 1|undef include=sys/kern/contro.h +HAVE_SYS_LOADAVG_H ? 1|undef include=sys/loadavg.h +HAVE_SYS_LOCK_H ? 1|undef include=sys/lock.h +HAVE_SYS_MEMFD_H ? 1|undef include=sys/memfd.h +HAVE_SYS_MKDEV_H ? 1|undef include=sys/mkdev.h +HAVE_SYS_MODEM_H ? 1|undef include=sys/modem.h +HAVE_SYS_NDIR_H ? 1|undef include=sys/ndir.h +HAVE_SYS_SYS_DOMAIN_H ? 1|undef include=sys/sys/domain.h +HAVE_SYS_TERMIO_H ? 1|undef include=sys/termio.h +HAVE_TERM_H ? 1|undef include=term.h +HAVE_UTIL_H ? 1|undef include=util.h +HAVE_UUID_H ? 1|undef include=uuid.h +HAVE_UUID_UUID_H ? 1|undef include=uuid/uuid.h +HAVE_ZLIB_H ? 1|undef include=zlib.h + +################################################################################ +# Functions +################################################################################ +HAVE_ACCEPT ? 1|undef include=sys/socket.h int main(){accept(0, 0, 0);} +HAVE_ACCEPT4 ? 1|undef include=sys/socket.h int main(){accept4(0, 0, 0, 0);} +HAVE_ACOSH ? 1|undef include=math.h int main(){acosh(1.0);} +HAVE_ALARM ? 1|undef include=unistd.h int main(){alarm(1);} +HAVE_ASINH ? 1|undef include=math.h int main(){asinh(1.0);} +HAVE_ATANH ? 1|undef include=math.h int main(){atanh(0.5);} +HAVE_BIND ? 1|undef include=sys/socket.h int main(){bind(0, 0, 0);} +HAVE_BIND_TEXTDOMAIN_CODESET ? 1|undef include=libintl.h int main(){bind_textdomain_codeset(0, 0);} +HAVE_CHMOD ? 1|undef include=sys/stat.h int main(){chmod(0, 0);} +HAVE_CHOWN ? 1|undef include=unistd.h int main(){chown(0, 0, 0);} +HAVE_CLOCK ? 1|undef include=time.h int main(){clock();} +HAVE_CLOCK_GETRES ? 1|undef include=time.h int main(){struct timespec ts; clock_getres(CLOCK_REALTIME, &ts);} +HAVE_CLOCK_GETTIME ? 1|undef include=time.h int main(){struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts);} +HAVE_CLOCK_NANOSLEEP ? 1|undef include=time.h int main(){struct timespec ts = {0, 0}; clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0);} +HAVE_CLOCK_SETTIME ? 1|undef include=time.h int main(){struct timespec ts = {0, 0}; clock_settime(CLOCK_REALTIME, &ts);} +HAVE_CLOSE_RANGE ? 1|undef define=_GNU_SOURCE include=unistd.h int main(){close_range(0, 0, 0);} +HAVE_CONFSTR ? 1|undef include=unistd.h int main(){confstr(0, 0, 0);} +HAVE_CONNECT ? 1|undef include=sys/socket.h int main(){connect(0, 0, 0);} +HAVE_COPY_FILE_RANGE ? 1|undef include=unistd.h int main(){copy_file_range(0, 0, 0, 0, 0, 0);} +HAVE_CTERMID ? 1|undef include=stdio.h int main(){ctermid(0);} +HAVE_DLOPEN ? 1|undef include=dlfcn.h int main(){dlopen(0, 0);} +HAVE_DUP ? 1|undef include=unistd.h int main(){dup(0);} +HAVE_DUP2 ? 1|undef include=unistd.h int main(){dup2(0, 0);} +HAVE_DUP3 ? 1|undef include=unistd.h int main(){dup3(0, 0, 0);} +HAVE_EPOLL_CREATE1 ? 1|undef include=sys/epoll.h int main(){epoll_create1(0);} +HAVE_ERF ? 1|undef include=math.h int main(){erf(1.0);} +HAVE_ERFC ? 1|undef include=math.h int main(){erfc(1.0);} +HAVE_EVENTFD ? 1|undef include=sys/eventfd.h int main(){eventfd(0, 0);} +HAVE_EXECV ? 1|undef include=unistd.h int main(){execv(0, 0);} +HAVE_EXPLICIT_BZERO ? 1|undef include=strings.h int main(){explicit_bzero(0, 0);} +HAVE_EXPM1 ? 1|undef include=math.h int main(){expm1(1.0);} +HAVE_FACCESSAT ? 1|undef include=unistd.h int main(){faccessat(0, 0, 0, 0);} +HAVE_FCHDIR ? 1|undef include=unistd.h int main(){fchdir(0);} +HAVE_FCHMOD ? 1|undef include=sys/stat.h int main(){fchmod(0, 0);} +HAVE_FCHMODAT ? 1|undef include=sys/stat.h int main(){fchmodat(0, 0, 0, 0);} +HAVE_FCHOWN ? 1|undef include=unistd.h int main(){fchown(0, 0, 0);} +HAVE_FCHOWNAT ? 1|undef include=unistd.h int main(){fchownat(0, 0, 0, 0, 0);} +HAVE_FDATASYNC ? 1|undef include=unistd.h int main(){fdatasync(0);} +HAVE_FDOPENDIR ? 1|undef include=dirent.h int main(){fdopendir(0);} +HAVE_FEXECVE ? 1|undef include=unistd.h int main(){fexecve(0, 0, 0);} +HAVE_FLOCK ? 1|undef include=sys/file.h int main(){flock(0, 0);} +HAVE_FORK ? 1|undef include=unistd.h int main(){fork();} +HAVE_FORKPTY ? 1|undef include=pty.h int main(){forkpty(0, 0, 0, 0);} +HAVE_FPATHCONF ? 1|undef include=unistd.h int main(){fpathconf(0, 0);} +HAVE_FSEEKO ? 1|undef include=stdio.h int main(){fseeko(0, 0, 0);} +HAVE_FSTATAT ? 1|undef include=sys/stat.h int main(){struct stat st; fstatat(0, 0, &st, 0);} +HAVE_FSTATVFS ? 1|undef include=sys/statvfs.h int main(){struct statvfs st; fstatvfs(0, &st);} +HAVE_FSYNC ? 1|undef include=unistd.h int main(){fsync(0);} +HAVE_FTELLO ? 1|undef include=stdio.h int main(){ftello(0);} +HAVE_FTIME ? 1|undef include=sys/timeb.h int main(){struct timeb tb; ftime(&tb);} +HAVE_FTRUNCATE ? 1|undef include=unistd.h int main(){ftruncate(0, 0);} +HAVE_FUTIMENS ? 1|undef include=sys/stat.h int main(){futimens(0, 0);} +HAVE_FUTIMES ? 1|undef include=sys/time.h int main(){futimes(0, 0);} +HAVE_FUTIMESAT ? 1|undef include=sys/time.h int main(){futimesat(0, 0, 0);} +HAVE_GAI_STRERROR ? 1|undef include=netdb.h int main(){gai_strerror(0);} +HAVE_GETADDRINFO ? 1|undef include=netdb.h int main(){getaddrinfo(0, 0, 0, 0);} +HAVE_GETC_UNLOCKED ? 1|undef include=stdio.h int main(){getc_unlocked((FILE*)0);} +HAVE_GETEGID ? 1|undef include=unistd.h int main(){getegid();} +HAVE_GETENTROPY ? 1|undef include=unistd.h int main(){getentropy(0, 0);} +HAVE_GETEUID ? 1|undef include=unistd.h int main(){geteuid();} +HAVE_GETGID ? 1|undef include=unistd.h int main(){getgid();} +HAVE_GETGRGID ? 1|undef include=grp.h int main(){getgrgid(0);} +HAVE_GETGRGID_R ? 1|undef include=grp.h int main(){struct group grp; char buf[1024]; struct group *result; getgrgid_r(0, &grp, buf, sizeof(buf), &result);} +HAVE_GETGRNAM_R ? 1|undef include=grp.h int main(){struct group grp; char buf[1024]; struct group *result; getgrnam_r(0, &grp, buf, sizeof(buf), &result);} +HAVE_GETGROUPLIST ? 1|undef include=grp.h int main(){int groups; int ngroups = 1; getgrouplist(0, 0, &groups, &ngroups);} +HAVE_GETGROUPS ? 1|undef include=unistd.h int main(){getgroups(0, 0);} +HAVE_GETHOSTBYADDR ? 1|undef include=netdb.h int main(){gethostbyaddr(0, 0, 0);} +HAVE_GETHOSTBYNAME ? 1|undef include=netdb.h int main(){gethostbyname(0);} +HAVE_GETHOSTBYNAME_R ? 1|undef include=netdb.h int main(){struct hostent he; char buf[1024]; struct hostent *result; int h_errno; gethostbyname_r(0, &he, buf, sizeof(buf), &result, &h_errno);} +HAVE_GETHOSTNAME ? 1|undef include=unistd.h int main(){gethostname(0, 0);} +HAVE_GETITIMER ? 1|undef include=sys/time.h int main(){struct itimerval it; getitimer(0, &it);} +HAVE_GETLOADAVG ? 1|undef include=stdlib.h int main(){double loadavg[3]; getloadavg(loadavg, 3);} +HAVE_GETLOGIN ? 1|undef include=unistd.h int main(){getlogin();} +HAVE_GETNAMEINFO ? 1|undef include=netdb.h int main(){getnameinfo(0, 0, 0, 0, 0, 0, 0);} +HAVE_GETPAGESIZE ? 1|undef include=unistd.h int main(){getpagesize();} +HAVE_GETPEERNAME ? 1|undef include=sys/socket.h int main(){getpeername(0, 0, 0);} +HAVE_GETPGID ? 1|undef include=unistd.h int main(){getpgid(0);} +HAVE_GETPGRP ? 1|undef include=unistd.h int main(){getpgrp();} +HAVE_GETPID ? 1|undef include=unistd.h int main(){getpid();} +HAVE_GETPPID ? 1|undef include=unistd.h int main(){getppid();} +HAVE_GETPRIORITY ? 1|undef include=sys/resource.h int main(){getpriority(0, 0);} +HAVE_GETPROTOBYNAME ? 1|undef include=netdb.h int main(){getprotobyname(0);} +HAVE_GETPWENT ? 1|undef include=pwd.h int main(){getpwent();} +HAVE_GETPWNAM_R ? 1|undef include=pwd.h int main(){struct passwd pw; char buf[1024]; struct passwd *result; getpwnam_r(0, &pw, buf, sizeof(buf), &result);} +HAVE_GETPWUID ? 1|undef include=pwd.h int main(){getpwuid(0);} +HAVE_GETPWUID_R ? 1|undef include=pwd.h int main(){struct passwd pw; char buf[1024]; struct passwd *result; getpwuid_r(0, &pw, buf, sizeof(buf), &result);} +HAVE_GETRANDOM ? 1|undef include=sys/random.h int main(){getrandom(0, 0, 0);} +HAVE_GETRESGID ? 1|undef include=unistd.h int main(){gid_t rgid, egid, sgid; getresgid(&rgid, &egid, &sgid);} +HAVE_GETRESUID ? 1|undef define=_GNU_SOURCE include=unistd.h int main(){uid_t ruid, euid, suid; getresuid(&ruid, &euid, &suid);} +HAVE_GETRUSAGE ? 1|undef include=sys/resource.h int main(){struct rusage ru; getrusage(0, &ru);} +HAVE_GETSERVBYNAME ? 1|undef include=netdb.h int main(){getservbyname(0, 0);} +HAVE_GETSERVBYPORT ? 1|undef include=netdb.h int main(){getservbyport(0, 0);} +HAVE_GETSID ? 1|undef include=unistd.h int main(){getsid(0);} +HAVE_GETSOCKNAME ? 1|undef include=sys/socket.h int main(){getsockname(0, 0, 0);} +HAVE_GETSPENT ? 1|undef include=shadow.h int main(){getspent();} +HAVE_GETSPNAM ? 1|undef include=shadow.h int main(){getspnam(0);} +HAVE_GETUID ? 1|undef include=unistd.h int main(){getuid();} +HAVE_GETWD ? 1|undef include=unistd.h int main(){getwd(0);} +HAVE_HSTRERROR ? 1|undef include=netdb.h int main(){hstrerror(0);} +HAVE_HTOLE64 ? 1|undef include=endian.h int main(){htole64(0);} +HAVE_IF_NAMEINDEX ? 1|undef include=net/if.h int main(){if_nameindex();} +HAVE_INET_ATON ? 1|undef include=arpa/inet.h int main(){struct in_addr addr; inet_aton(0, &addr);} +HAVE_INET_NTOA ? 1|undef include=arpa/inet.h int main(){struct in_addr addr; inet_ntoa(addr);} +HAVE_INET_PTON ? 1|undef include=arpa/inet.h int main(){inet_pton(0, 0, 0);} +HAVE_INITGROUPS ? 1|undef include=grp.h int main(){initgroups(0, 0);} +HAVE_KILL ? 1|undef include=signal.h int main(){kill(0, 0);} +HAVE_KILLPG ? 1|undef include=signal.h int main(){killpg(0, 0);} +HAVE_LCHOWN ? 1|undef include=unistd.h int main(){lchown(0, 0, 0);} +HAVE_LINK ? 1|undef include=unistd.h int main(){link(0, 0);} +HAVE_LINKAT ? 1|undef include=unistd.h int main(){linkat(0, 0, 0, 0, 0);} +HAVE_LISTEN ? 1|undef include=sys/socket.h int main(){listen(0, 0);} +HAVE_LOCKF ? 1|undef include=unistd.h int main(){lockf(0, 0, 0);} +HAVE_LOG1P ? 1|undef include=math.h int main(){log1p(1.0);} +HAVE_LOG2 ? 1|undef include=math.h int main(){log2(2.0);} +HAVE_LOGIN_TTY ? 1|undef include=utmp.h int main(){login_tty(0);} +HAVE_LSTAT ? 1|undef include=sys/stat.h int main(){struct stat st; lstat(0, &st);} +HAVE_LUTIMES ? 1|undef include=sys/time.h int main(){lutimes(0, 0);} +HAVE_MADVISE ? 1|undef include=sys/mman.h int main(){madvise(0, 0, 0);} +HAVE_MAKEDEV ? 1|undef include=sys/types.h int main(){makedev(0, 0);} +HAVE_MBRTOWC ? 1|undef include=wchar.h int main(){wchar_t wc; mbrtowc(&wc, 0, 0, 0);} +HAVE_MEMFD_CREATE ? 1|undef include=sys/mman.h int main(){memfd_create(0, 0);} +HAVE_MEMRCHR ? 1|undef include=string.h int main(){memrchr(0, 0, 0);} +HAVE_MKDIRAT ? 1|undef include=sys/stat.h int main(){mkdirat(0, 0, 0);} +HAVE_MKFIFO ? 1|undef include=sys/stat.h int main(){mkfifo(0, 0);} +HAVE_MKFIFOAT ? 1|undef include=sys/stat.h int main(){mkfifoat(0, 0, 0);} +HAVE_MKNOD ? 1|undef include=sys/stat.h int main(){mknod(0, 0, 0);} +HAVE_MKNODAT ? 1|undef include=sys/stat.h int main(){mknodat(0, 0, 0, 0);} +HAVE_MKTIME ? 1|undef include=time.h int main(){struct tm tm; mktime(&tm);} +HAVE_MMAP ? 1|undef include=sys/mman.h int main(){mmap(0, 0, 0, 0, 0, 0);} +HAVE_MREMAP ? 1|undef define=_GNU_SOURCE include=sys/mman.h int main(){mremap(0, 0, 0, 0);} +HAVE_NANOSLEEP ? 1|undef include=time.h int main(){struct timespec ts = {0, 0}; nanosleep(&ts, 0);} +HAVE_NICE ? 1|undef include=unistd.h int main(){nice(0);} +HAVE_OPENAT ? 1|undef include=fcntl.h int main(){openat(0, 0, 0);} +HAVE_OPENDIR ? 1|undef include=dirent.h int main(){opendir(0);} +HAVE_OPENPTY ? 1|undef include=pty.h int main(){openpty(0, 0, 0, 0, 0);} +HAVE_PATHCONF ? 1|undef include=unistd.h int main(){pathconf(0, 0);} +HAVE_PAUSE ? 1|undef include=unistd.h int main(){pause();} +HAVE_PIPE ? 1|undef include=unistd.h int main(){int pipefd[2]; pipe(pipefd);} +HAVE_PIPE2 ? 1|undef include=unistd.h int main(){int pipefd[2]; pipe2(pipefd, 0);} +HAVE_POLL ? 1|undef include=poll.h int main(){poll(0, 0, 0);} +HAVE_POSIX_FADVISE ? 1|undef include=fcntl.h int main(){posix_fadvise(0, 0, 0, 0);} +HAVE_POSIX_FALLOCATE ? 1|undef include=fcntl.h int main(){posix_fallocate(0, 0, 0);} +HAVE_POSIX_SPAWN ? 1|undef include=spawn.h int main(){posix_spawn(0, 0, 0, 0, 0, 0);} +HAVE_POSIX_SPAWNP ? 1|undef include=spawn.h int main(){posix_spawnp(0, 0, 0, 0, 0, 0);} +HAVE_PREAD ? 1|undef include=unistd.h int main(){pread(0, 0, 0, 0);} +HAVE_PREADV ? 1|undef include=sys/uio.h int main(){preadv(0, 0, 0, 0);} +HAVE_PREADV2 ? 1|undef include=sys/uio.h int main(){preadv2(0, 0, 0, 0, 0);} +HAVE_PRLIMIT ? 1|undef include=sys/time.h,sys/resource.h int main(){prlimit(0, 0, 0, 0);} +HAVE_PTHREAD_KILL ? 1|undef include=signal.h int main(){pthread_kill(0, 0);} +HAVE_PTHREAD_SIGMASK ? 1|undef include=pthread.h int main(){pthread_sigmask(0, 0, 0);} +HAVE_PWRITE ? 1|undef include=unistd.h int main(){pwrite(0, 0, 0, 0);} +HAVE_PWRITEV ? 1|undef include=sys/uio.h int main(){pwritev(0, 0, 0, 0);} +HAVE_PWRITEV2 ? 1|undef include=sys/uio.h int main(){pwritev2(0, 0, 0, 0, 0);} +HAVE_READLINK ? 1|undef include=unistd.h int main(){readlink(0, 0, 0);} +HAVE_READLINKAT ? 1|undef include=unistd.h int main(){readlinkat(0, 0, 0, 0);} +HAVE_READV ? 1|undef include=sys/uio.h int main(){readv(0, 0, 0);} +HAVE_REALPATH ? 1|undef include=stdlib.h int main(){realpath(0, 0);} +HAVE_RECVFROM ? 1|undef include=sys/socket.h int main(){recvfrom(0, 0, 0, 0, 0, 0);} +HAVE_RENAMEAT ? 1|undef include=stdio.h int main(){renameat(0, 0, 0, 0);} +HAVE_SCHED_GET_PRIORITY_MAX ? 1|undef include=sched.h int main(){sched_get_priority_max(0);} +HAVE_SCHED_RR_GET_INTERVAL ? 1|undef include=sched.h int main(){struct timespec ts; sched_rr_get_interval(0, &ts);} +HAVE_SCHED_SETAFFINITY ? 1|undef define=_GNU_SOURCE include=sched.h int main(){sched_setaffinity(0, 0, 0);} +HAVE_SCHED_SETPARAM ? 1|undef include=sched.h int main(){struct sched_param sp; sched_setparam(0, &sp);} +HAVE_SCHED_SETSCHEDULER ? 1|undef include=sched.h int main(){struct sched_param sp; sched_setscheduler(0, 0, &sp);} +HAVE_SEM_CLOCKWAIT ? 1|undef include=semaphore.h int main(){sem_clockwait(0, 0, 0);} +HAVE_SEM_GETVALUE ? 1|undef include=semaphore.h int main(){sem_getvalue(0, 0);} +HAVE_SEM_OPEN ? 1|undef include=semaphore.h int main(){sem_open(0, 0);} +HAVE_SEM_TIMEDWAIT ? 1|undef include=semaphore.h int main(){sem_timedwait(0, 0);} +HAVE_SEM_UNLINK ? 1|undef include=semaphore.h int main(){sem_unlink(0);} +HAVE_SENDFILE ? 1|undef include=sys/sendfile.h int main(){sendfile(0, 0, 0, 0);} +HAVE_SENDTO ? 1|undef include=sys/socket.h int main(){sendto(0, 0, 0, 0, 0, 0);} +HAVE_SETEGID ? 1|undef include=unistd.h int main(){setegid(0);} +HAVE_SETEUID ? 1|undef include=unistd.h int main(){seteuid(0);} +HAVE_SETGID ? 1|undef include=unistd.h int main(){setgid(0);} +HAVE_SETGROUPS ? 1|undef include=grp.h int main(){setgroups(0, 0);} +HAVE_SETHOSTNAME ? 1|undef include=unistd.h int main(){sethostname(0, 0);} +HAVE_SETITIMER ? 1|undef include=sys/time.h int main(){struct itimerval it; setitimer(0, &it, 0);} +HAVE_SETLOCALE ? 1|undef include=locale.h int main(){setlocale(0, 0);} +HAVE_SETPGID ? 1|undef include=unistd.h int main(){setpgid(0, 0);} +HAVE_SETPGRP ? 1|undef include=unistd.h int main(){setpgrp();} +HAVE_SETPRIORITY ? 1|undef include=sys/resource.h int main(){setpriority(0, 0, 0);} +HAVE_SETREGID ? 1|undef include=unistd.h int main(){setregid(0, 0);} +HAVE_SETRESGID ? 1|undef include=unistd.h int main(){setresgid(0, 0, 0);} +HAVE_SETRESUID ? 1|undef define=_GNU_SOURCE include=unistd.h int main(){setresuid(0, 0, 0);} +HAVE_SETREUID ? 1|undef include=unistd.h int main(){setreuid(0, 0);} +HAVE_SETSID ? 1|undef include=unistd.h int main(){setsid();} +HAVE_SETSOCKOPT ? 1|undef include=sys/socket.h int main(){setsockopt(0, 0, 0, 0, 0);} +HAVE_SETUID ? 1|undef include=unistd.h int main(){setuid(0);} +HAVE_SETVBUF ? 1|undef include=stdio.h int main(){setvbuf(0, 0, 0, 0);} +HAVE_SHM_OPEN ? 1|undef include=sys/mman.h int main(){shm_open(0, 0, 0);} +HAVE_SHM_UNLINK ? 1|undef include=sys/mman.h int main(){shm_unlink(0);} +HAVE_SHUTDOWN ? 1|undef include=sys/socket.h int main(){shutdown(0, 0);} +HAVE_SIGACTION ? 1|undef include=signal.h int main(){struct sigaction sa; sigaction(0, &sa, 0);} +HAVE_SIGALTSTACK ? 1|undef include=signal.h int main(){sigaltstack(0, 0);} +HAVE_SIGFILLSET ? 1|undef include=signal.h int main(){sigset_t set; sigfillset(&set);} +HAVE_SIGINTERRUPT ? 1|undef include=signal.h int main(){siginterrupt(0, 0);} +HAVE_SIGPENDING ? 1|undef include=signal.h int main(){sigset_t set; sigpending(&set);} +HAVE_SIGRELSE ? 1|undef include=signal.h int main(){sigrelse(0);} +HAVE_SIGTIMEDWAIT ? 1|undef include=signal.h int main(){sigset_t set; sigtimedwait(&set, 0, 0);} +HAVE_SIGWAIT ? 1|undef include=signal.h int main(){sigset_t set; sigwait(&set, 0);} +HAVE_SIGWAITINFO ? 1|undef include=signal.h int main(){sigset_t set; sigwaitinfo(&set, 0);} +HAVE_SNPRINTF ? 1|undef include=stdio.h int main(){snprintf(0, 0, 0);} +HAVE_SOCKET ? 1|undef include=sys/socket.h int main(){socket(0, 0, 0);} +HAVE_SOCKETPAIR ? 1|undef include=sys/socket.h int main(){int sv[2]; socketpair(0, 0, 0, sv);} +HAVE_SPLICE ? 1|undef define=_GNU_SOURCE include=fcntl.h int main(){splice(0, 0, 0, 0, 0, 0);} +HAVE_STATVFS ? 1|undef include=sys/statvfs.h int main(){struct statvfs st; statvfs(0, &st);} +HAVE_STRFTIME ? 1|undef include=time.h int main(){struct tm tm; strftime(0, 0, 0, &tm);} +HAVE_STRSIGNAL ? 1|undef include=string.h int main(){strsignal(0);} +HAVE_SYMLINK ? 1|undef include=unistd.h int main(){symlink(0, 0);} +HAVE_SYMLINKAT ? 1|undef include=unistd.h int main(){symlinkat(0, 0, 0);} +HAVE_SYNC ? 1|undef include=unistd.h int main(){sync();} +HAVE_SYSCONF ? 1|undef include=unistd.h int main(){sysconf(0);} +HAVE_SYSTEM ? 1|undef include=stdlib.h int main(){system(0);} +HAVE_TCGETPGRP ? 1|undef include=unistd.h int main(){tcgetpgrp(0);} +HAVE_TCSETPGRP ? 1|undef include=unistd.h int main(){tcsetpgrp(0, 0);} +HAVE_TEMPNAM ? 1|undef include=stdio.h int main(){tempnam(0, 0);} +HAVE_TIMEGM ? 1|undef include=time.h int main(){struct tm tm; timegm(&tm);} +HAVE_TIMES ? 1|undef include=sys/times.h int main(){struct tms tms; times(&tms);} +HAVE_TMPFILE ? 1|undef include=stdio.h int main(){tmpfile();} +HAVE_TMPNAM ? 1|undef include=stdio.h int main(){tmpnam(0);} +HAVE_TMPNAM_R ? 1|undef include=stdio.h int main(){tmpnam_r(0);} +HAVE_TRUNCATE ? 1|undef include=unistd.h int main(){truncate(0, 0);} +HAVE_UMASK ? 1|undef include=sys/stat.h int main(){umask(0);} +HAVE_UNAME ? 1|undef include=sys/utsname.h int main(){struct utsname uts; uname(&uts);} +HAVE_UNLINKAT ? 1|undef include=unistd.h int main(){unlinkat(0, 0, 0);} +HAVE_UTIMENSAT ? 1|undef include=sys/stat.h int main(){utimensat(0, 0, 0, 0);} +HAVE_UTIMES ? 1|undef include=sys/time.h int main(){utimes(0, 0);} +HAVE_VFORK ? 1|undef include=unistd.h int main(){vfork();} +HAVE_WAIT ? 1|undef include=sys/wait.h int main(){wait(0);} +HAVE_WAIT3 ? 1|undef include=sys/wait.h int main(){wait3(0, 0, 0);} +HAVE_WAIT4 ? 1|undef include=sys/wait.h int main(){wait4(0, 0, 0, 0);} +HAVE_WAITID ? 1|undef include=sys/wait.h int main(){waitid(0, 0, 0, 0);} +HAVE_WAITPID ? 1|undef include=sys/wait.h int main(){waitpid(0, 0, 0);} +HAVE_WCSCOLL ? 1|undef include=wchar.h int main(){wcscoll(0, 0);} +HAVE_WCSFTIME ? 1|undef include=wchar.h int main(){wcsftime(0, 0, 0, 0);} +HAVE_WCSXFRM ? 1|undef include=wchar.h int main(){wcsxfrm(0, 0, 0);} +HAVE_WMEMCMP ? 1|undef include=wchar.h int main(){wmemcmp(0, 0, 0);} +HAVE_WRITEV ? 1|undef include=sys/uio.h int main(){writev(0, 0, 0);} +HAVE_UUID_CREATE ? 1|undef include=uuid.h int main(){uuid_create(0,0);} + +# BSD/macOS specific functions +HAVE_CHFLAGS ? 1|undef include=sys/stat.h int main(){chflags(0, 0);} +HAVE_LCHFLAGS ? 1|undef include=sys/stat.h int main(){lchflags(0, 0);} +HAVE_LCHMOD ? 1|undef include=sys/stat.h int main(){lchmod(0, 0);} +HAVE_KQUEUE ? 1|undef include=sys/event.h int main(){kqueue();} +HAVE_FDWALK ? 1|undef include=fcntl.h int main(){fdwalk(0, 0);} +HAVE_FORK1 ? 1|undef include=sys/types.h,sys/proc.h int main(){fork1();} +HAVE_GETPGRP ? 1|undef include=unistd.h int main(){getpgrp();} +HAVE_PLOCK ? 1|undef include=sys/lock.h int main(){plock(0);} +HAVE_RTPSPAWN ? 1|undef include=spawn.h int main(){rtpspawn(0, 0, 0, 0, 0, 0);} +HAVE_STRLCPY ? 1|undef include=string.h int main(){strlcpy(0, 0, 0);} +HAVE__GETPTY ? 1|undef include=stdlib.h int main(){_getpty(0, 0, 0, 0);} + +# Additional math functions +HAVE_FSEEK64 ? 1|undef include=stdio.h int main(){fseek64(0, 0, 0);} +HAVE_FTELL64 ? 1|undef include=stdio.h int main(){ftell64(0);} + +# Library functions +HAVE_CRYPT_R ? 1|undef include=crypt.h int main(){struct crypt_data cd; crypt_r(0, 0, &cd);} +HAVE_CTERMID_R ? 1|undef include=stdio.h int main(){ctermid_r((char*)0);} +HAVE_EXPLICIT_MEMSET ? 1|undef include=string.h int main(){explicit_memset(0, 0, 0);} + +# UUID functions +HAVE_UUID_ENC_BE ? 1|undef include=uuid.h int main(){uuid_enc_be(0, 0);} +HAVE_UUID_GENERATE_TIME_SAFE ? 1|undef include=uuid.h int main(){uuid_generate_time_safe(0);} + +# Various other functions +HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH ? 1|undef include=mach-o/dyld.h int main(){_dyld_shared_cache_contains_path(0);} + +# Curses functions +HAVE_CURSES_FILTER ? 1|undef include=curses.h int main(){filter();} +HAVE_CURSES_HAS_KEY ? 1|undef include=curses.h int main(){has_key(0);} +HAVE_CURSES_IMMEDOK ? 1|undef include=curses.h int main(){immedok(0, 0);} +HAVE_CURSES_IS_PAD ? 1|undef include=curses.h int main(){is_pad(0);} +HAVE_CURSES_IS_TERM_RESIZED ? 1|undef include=curses.h int main(){is_term_resized(0, 0);} +HAVE_CURSES_RESIZETERM ? 1|undef include=curses.h int main(){resizeterm(0, 0);} +HAVE_CURSES_RESIZE_TERM ? 1|undef include=curses.h int main(){resize_term(0, 0);} +HAVE_CURSES_SYNCOK ? 1|undef include=curses.h int main(){syncok(0, 0);} +HAVE_CURSES_TYPEAHEAD ? 1|undef include=curses.h int main(){typeahead(0);} +HAVE_CURSES_USE_ENV ? 1|undef include=curses.h int main(){use_env(0);} +HAVE_CURSES_WCHGAT ? 1|undef include=curses.h int main(){wchgat(0, 0, 0, 0, 0);} +# Readline functions +HAVE_RL_APPEND_HISTORY ? 1|undef include=readline/history.h int main(){append_history(0, 0);} +HAVE_RL_CATCH_SIGNAL ? 1|undef include=readline/readline.h int main(){rl_catch_signals = 0;} +HAVE_RL_COMPLETION_MATCHES ? 1|undef include=readline/readline.h int main(){rl_completion_matches(0, 0);} +HAVE_RL_RESIZE_TERMINAL ? 1|undef include=readline/readline.h int main(){rl_resize_terminal();} + +# Pthread functions +HAVE_PTHREAD_CONDATTR_SETCLOCK ? 1|undef include=pthread.h int main(){pthread_condattr_t attr; pthread_condattr_setclock(&attr, 0);} +HAVE_PTHREAD_GETCPUCLOCKID ? 1|undef include=pthread.h int main(){clockid_t clk; pthread_getcpuclockid(0, &clk);} + +# Header includes and type checks +HAVE_ADDRINFO ? 1|undef include=netdb.h int main(){struct addrinfo ai; return 0;} +HAVE_ALIGNED_REQUIRED ? 1|undef include=stddef.h int main(){char a[1]; return ((size_t)a) & 1;} +HAVE_ALTZONE ? 1|undef include=time.h int main(){return altzone;} + +# Broken function tests (these typically compile but have runtime issues) +# HAVE_BROKEN_MBSTOWCS ?u1 $(include=stdlib.h,wchar.h int main(){return mbstowcs(NULL, "test", 0) == 0;}) +# HAVE_BROKEN_NICE ?u1 $(include=unistd.h int main(){nice(1); return 0;}) +# HAVE_BROKEN_PIPE_BUF ?u1 $(include=limits.h int main(){return PIPE_BUF;}) +# HAVE_BROKEN_POLL ?u1 $(include=poll.h int main(){struct pollfd pfd; poll(&pfd, 1, 0); return 0;}) +# HAVE_BROKEN_POSIX_SEMAPHORES ?u1 $(include=semaphore.h int main(){sem_t sem; return 0;}) +# HAVE_BROKEN_PTHREAD_SIGMASK ?u1 $(include=pthread.h,signal.h int main(){sigset_t set; pthread_sigmask(SIG_BLOCK, &set, NULL); return 0;}) +# HAVE_BROKEN_SEM_GETVALUE ?u1 $(include=semaphore.h int main(){sem_t sem; int val; sem_getvalue(&sem, &val); return 0;}) +# HAVE_BROKEN_UNSETENV ?u1 $(include=stdlib.h int main(){unsetenv("TEST"); return 0;}) + +# Compiler builtin features +HAVE_BUILTIN_ATOMIC ? 1|undef int main(){int x = 0; __atomic_store_n(&x, 1, __ATOMIC_SEQ_CST); return __atomic_load_n(&x, __ATOMIC_SEQ_CST);} +HAVE_COMPUTED_GOTOS ? 1|undef int main(){void *ptr = &&label; goto *ptr; label: return 0;} + +# RTLD declarations +HAVE_DECL_RTLD_DEEPBIND ? 1|undef include=dlfcn.h int main(){return RTLD_DEEPBIND;} +HAVE_DECL_RTLD_GLOBAL ? 1|undef include=dlfcn.h int main(){return RTLD_GLOBAL;} +HAVE_DECL_RTLD_LAZY ? 1|undef include=dlfcn.h int main(){return RTLD_LAZY;} +HAVE_DECL_RTLD_LOCAL ? 1|undef include=dlfcn.h int main(){return RTLD_LOCAL;} +HAVE_DECL_RTLD_MEMBER ? 1|undef include=dlfcn.h int main(){return RTLD_MEMBER;} +HAVE_DECL_RTLD_NODELETE ? 1|undef include=dlfcn.h int main(){return RTLD_NODELETE;} +HAVE_DECL_RTLD_NOLOAD ? 1|undef include=dlfcn.h int main(){return RTLD_NOLOAD;} +HAVE_DECL_RTLD_NOW ? 1|undef include=dlfcn.h int main(){return RTLD_NOW;} +HAVE_DECL_TZNAME ? 1|undef include=time.h int main(){return tzname[0] != NULL;} + +# Device and file system features +HAVE_DEVICE_MACROS ? 1|undef include=sys/types.h,sys/stat.h int main(){dev_t d = 0; return major(d) | minor(d);} +HAVE_DEV_PTC ? 1|undef include=fcntl.h int main(){return open("/dev/ptc", O_RDWR);} +HAVE_DEV_PTMX ? 1|undef include=fcntl.h int main(){return open("/dev/ptmx", O_RDWR);} +HAVE_DIRENT_D_TYPE ? 1|undef include=dirent.h int main(){struct dirent d; return d.d_type;} +HAVE_DIRFD ? 1|undef include=dirent.h int main(){DIR *d = NULL; return dirfd(d);} + +# System features +HAVE_DYNAMIC_LOADING ? 1|undef include=dlfcn.h int main(){dlopen("test", RTLD_NOW); return 0;} +HAVE_EPOLL ? 1|undef include=sys/epoll.h int main(){return epoll_create(1);} + +# GCC assembly features +HAVE_GCC_UINT128_T ? 1|undef int main(){__uint128_t x = 0; return (int)x;} + +# gethostbyname_r variants +HAVE_GETHOSTBYNAME_R_3_ARG ? 1|undef include=netdb.h int main(){struct hostent_data hed; gethostbyname_r("localhost", NULL, &hed); return 0;} +HAVE_GETHOSTBYNAME_R_5_ARG ? 1|undef include=netdb.h int main(){struct hostent he; char buf[1024]; gethostbyname_r("localhost", &he, buf, sizeof(buf), NULL); return 0;} +HAVE_GETHOSTBYNAME_R_6_ARG ? 1|undef include=netdb.h int main(){struct hostent he; char buf[1024]; struct hostent *result; int h_errno; gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &h_errno); return 0;} + +# System calls and features +HAVE_GETRANDOM_SYSCALL ? 1|undef include=sys/syscall.h,unistd.h int main(){return syscall(SYS_getrandom, NULL, 0, 0);} + +# Bug detection +HAVE_GLIBC_MEMMOVE_BUG ? 1|undef include=string.h int main(){char buf[10]; memmove(buf+1, buf, 5); return 0;} +HAVE_IPA_PURE_CONST_BUG ? 1|undef int main(){return 0;} + +# File and library support +HAVE_LARGEFILE_SUPPORT ? 1|undef include=sys/types.h int main(){off_t offset = 0; return sizeof(offset) > 4;} +HAVE_LIBB2 ? 1|undef include=blake2.h int main(){blake2b_state state; blake2s_final(&state, 0, 0); return 0;} +HAVE_LIBDB ? 1|undef include=db.h int main(){DB *db; return 0;} +HAVE_LIBDL ? 1|undef include=dlfcn.h int main(){dlopen("test", RTLD_NOW); return 0;} +HAVE_LIBDLD ? 1|undef int main(){return 0;} +HAVE_LIBIEEE ? 1|undef int main(){return 0;} +HAVE_LIBRESOLV ? 1|undef include=resolv.h int main(){res_init(); return 0;} +HAVE_LIBSENDFILE ? 1|undef include=sys/sendfile.h int main(){sendfile(0, 0, NULL, 0); return 0;} +HAVE_LIBSQLITE3 ? 1|undef include=sqlite3.h int main(){sqlite3 *db; return 0;} + +# Linux-specific headers +HAVE_LINUX_AUXVEC_H ? 1|undef include=linux/auxvec.h int main(){return AT_NULL;} +HAVE_LINUX_CAN_BCM_H ? 1|undef include=linux/can/bcm.h int main(){struct bcm_msg_head msg; return 0;} +HAVE_LINUX_CAN_H ? 1|undef include=linux/can.h int main(){struct can_frame frame; return 0;} +HAVE_LINUX_CAN_J1939_H ? 1|undef include=linux/can/j1939.h int main(){return J1939_MAX_UNICAST_ADDR;} +HAVE_LINUX_CAN_RAW_FD_FRAMES ? 1|undef include=linux/can/raw.h int main(){return CAN_RAW_FD_FRAMES;} +HAVE_LINUX_CAN_RAW_H ? 1|undef include=linux/can/raw.h int main(){return CAN_RAW_FILTER;} +HAVE_LINUX_CAN_RAW_JOIN_FILTERS ? 1|undef include=linux/can/raw.h int main(){return CAN_RAW_JOIN_FILTERS;} +HAVE_LINUX_MEMFD_H ? 1|undef include=linux/memfd.h int main(){return MFD_CLOEXEC;} +HAVE_LINUX_NETLINK_H ? 1|undef include=linux/netlink.h int main(){struct nlmsghdr nlh; return 0;} +HAVE_LINUX_QRTR_H ? 1|undef include=linux/qrtr.h int main(){struct sockaddr_qrtr sq; return 0;} +HAVE_LINUX_RANDOM_H ? 1|undef include=linux/random.h int main(){return GRND_NONBLOCK;} +HAVE_LINUX_SOUNDCARD_H ? 1|undef include=linux/soundcard.h int main(){return SOUND_VERSION;} +HAVE_LINUX_TIPC_H ? 1|undef include=linux/tipc.h int main(){struct sockaddr_tipc addr; return 0;} +HAVE_LINUX_VM_SOCKETS_H ? 1|undef include=linux/vm_sockets.h int main(){return VMADDR_CID_ANY;} +HAVE_LINUX_WAIT_H ? 1|undef include=linux/wait.h int main(){return 0;} + +# Type support +HAVE_LONG_DOUBLE ? 1|undef int main(){long double x = 0.0L; return (int)x;} +# HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION ?u1 $(include=wchar.h int main(){wchar_t w = L'\x80'; return (int)w;}) +HAVE_PROTOTYPES ? 1|undef int test(int x); int main(){return test(0);} int test(int x){return x;} + +# pthread features +HAVE_PTHREAD_DESTRUCTOR ? 1|undef include=pthread.h void destructor(void *); int main(){pthread_key_create(NULL, destructor); return 0;} void destructor(void *p){} +HAVE_PTHREAD_INIT ? 1|undef include=pthread.h int main(){pthread_init(); return 0;} + +# Readline features +HAVE_RL_COMPLETION_APPEND_CHARACTER ? 1|undef include=readline/readline.h int main(){rl_completion_append_character = ' '; return 0;} +HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK ? 1|undef include=readline/readline.h int main(){rl_completion_display_matches_hook = NULL; return 0;} +HAVE_RL_COMPLETION_SUPPRESS_APPEND ? 1|undef include=readline/readline.h int main(){rl_completion_suppress_append = 1; return 0;} +HAVE_RL_PRE_INPUT_HOOK ? 1|undef include=readline/readline.h int main(){rl_pre_input_hook = NULL; return 0;} + +# Signal and socket features +HAVE_SIGINFO_T_SI_BAND ? 1|undef include=signal.h int main(){siginfo_t si; return si.si_band;} +HAVE_SOCKADDR_ALG ? 1|undef include=linux/if_alg.h int main(){struct sockaddr_alg sa; return 0;} +HAVE_SOCKADDR_SA_LEN ? 1|undef include=sys/socket.h int main(){struct sockaddr sa; return sa.sa_len;} +HAVE_SOCKADDR_STORAGE ? 1|undef include=sys/socket.h int main(){struct sockaddr_storage ss; return 0;} + +# Type definitions +HAVE_SSIZE_T ? 1|undef include=sys/types.h int main(){ssize_t s = 0; return (int)s;} +HAVE_STAT_TV_NSEC ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_mtim.tv_nsec;} +HAVE_STAT_TV_NSEC2 ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_mtimensec;} +HAVE_STD_ATOMIC ? 1|undef include=stdatomic.h int main(){atomic_int x; atomic_uintptr_t y; return 0;} + +# Struct member checks +HAVE_STRUCT_PASSWD_PW_GECOS ? 1|undef include=pwd.h int main(){struct passwd pw; return pw.pw_gecos != NULL;} +HAVE_STRUCT_PASSWD_PW_PASSWD ? 1|undef include=pwd.h int main(){struct passwd pw; return pw.pw_passwd != NULL;} +HAVE_STRUCT_STAT_ST_BIRTHTIME ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_birthtime;} +HAVE_STRUCT_STAT_ST_BLKSIZE ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_blksize;} +HAVE_STRUCT_STAT_ST_BLOCKS ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_blocks;} +HAVE_STRUCT_STAT_ST_FLAGS ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_flags;} +HAVE_STRUCT_STAT_ST_GEN ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_gen;} +HAVE_STRUCT_STAT_ST_RDEV ? 1|undef include=sys/stat.h int main(){struct stat st; return st.st_rdev;} +HAVE_STRUCT_TM_TM_ZONE ? 1|undef include=time.h int main(){struct tm t; return t.tm_zone != NULL;} + +# Time and timezone features +HAVE_TM_ZONE ? 1|undef include=time.h int main(){struct tm t; return t.tm_zone != NULL;} +HAVE_TZNAME ? 1|undef include=time.h int main(){return tzname[0] != NULL;} +# HAVE_USABLE_WCHAR_T ?u1 $(include=wchar.h int main(){wchar_t w = L'A'; return sizeof(w) >= 2;}) +HAVE_WORKING_TZSET ? 1|undef include=time.h int main(){tzset(); return 0;} +HAVE_ZLIB_COPY ? 1|undef include=zlib.h int main(){z_stream strm; inflateCopy(&strm, &strm); return 0;} diff --git a/configquery.zig b/configquery.zig new file mode 100644 index 0000000..3814a8e --- /dev/null +++ b/configquery.zig @@ -0,0 +1,479 @@ +pub fn main(init: std.process.Init) !void { + const arena = init.arena.allocator(); + const io = init.io; + + var template_files: std.ArrayList([]const u8) = .empty; + var output_path: ?[]const u8 = null; + var config: Config = .{}; + + { + var args = try init.minimal.args.iterateAllocator(arena); + _ = args.next(); + while (args.next()) |arg| { + if (std.mem.eql(u8, arg, "-o")) { + output_path = args.next() orelse fatal("-o requires an argument", .{}); + } else if (std.mem.eql(u8, arg, "--zig-exe")) { + config.zig_exe = args.next() orelse fatal("--zig-exe requires an argument", .{}); + } else if (std.mem.eql(u8, arg, "--target")) { + config.target_triple = args.next() orelse fatal("--target requires an argument", .{}); + } else if (std.mem.eql(u8, arg, "--cache-dir")) { + config.cache_dir = args.next() orelse fatal("--cache-dir requires an argument", .{}); + } else { + template_files.append(arena, arg) catch |e| oom(e); + } + } + } + + if (template_files.items.len == 0) { + const usage = + \\Usage: configquery [options] ... + \\ + \\Processes template files line by line. Lines starting with # are + \\comments. Each line is NAME VALUE where VALUE is either a literal + \\or a query starting with ?. + \\ + \\Options: + \\ -o Write output to file (default: stdout) + \\ --zig-exe Path to zig executable (for compile queries) + \\ --target Target triple (for compile queries) + \\ --cache-dir Cache directory (for compile temp files) + \\ + \\Literal values: + \\ SIZEOF_INT 4 + \\ RETSIGTYPE .void + \\ PY_HASH "md5,sha1" + \\ HAVE_FEATURE defined + \\ MISSING_FEATURE undef + \\ + \\Query syntax: NAME ? PASS_VALUE|FAIL_VALUE [define=DEF1,DEF2,...] [include=HDR1,HDR2,...] [COMPILE_BODY] + \\ + \\ HAVE_ALLOCA_H ? 1|undef include=alloca.h + \\ HAVE_FORK ? 1|undef include=unistd.h int main(){fork();} + \\ HAVE_PRLIMIT ? 1|undef include=sys/time.h,sys/resource.h int main(){prlimit(0,0,0,0);} + \\ HAVE_CLOSE_RANGE ? 1|undef define=_GNU_SOURCE include=unistd.h int main(){close_range(0,0,0);} + \\ + ; + var stderr = std.Io.File.stderr().writer(io, &.{}); + stderr.interface.writeAll(usage) catch return stderr.err.?; + stderr.interface.flush() catch return stderr.err.?; + std.process.exit(1); + } + + var out_buf: [4096]u8 = undefined; + var out_file = if (output_path) |path| std.Io.Dir.cwd().createFile(io, path, .{}) catch |err| fatal( + "failed to create '{s}' with {t}", + .{ path, err }, + ) else std.Io.File.stdout(); + + defer if (output_path != null) out_file.close(io); + var out_writer = out_file.writer(io, &out_buf); + + var error_count: u32 = 0; + + for (template_files.items) |template_path| { + const content = std.Io.Dir.cwd().readFileAlloc(io, template_path, arena, .unlimited) catch |err| fatal( + "failed to read '{s}' with {t}", + .{ template_path, err }, + ); + defer arena.free(content); + processFile( + arena, + io, + &config, + &error_count, + &out_writer.interface, + template_path, + content, + ) catch |err| switch (err) { + error.WriteFailed => return out_writer.err.?, + else => |e| return e, + }; + } + + out_writer.interface.flush() catch return out_writer.err.?; + if (error_count > 0) fatal("{} errors", .{error_count}); +} + +const Config = struct { + zig_exe: ?[]const u8 = null, + cache_dir: ?[]const u8 = null, + target_triple: ?[]const u8 = null, + include_dirs: std.ArrayList([]const u8) = .empty, +}; + +fn processFile( + arena: std.mem.Allocator, + io: std.Io, + config: *const Config, + error_count: *u32, + out: *std.Io.Writer, + template_path: []const u8, + content: []const u8, +) !void { + var line_it = std.mem.splitScalar(u8, content, '\n'); + var line_num: u32 = 0; + while (line_it.next()) |line_untrimmed| { + line_num += 1; + const line = std.mem.trim(u8, line_untrimmed, &std.ascii.whitespace); + if (line.len == 0 or line[0] == '#') continue; + + const first_space = std.mem.indexOfScalar(u8, line, ' ') orelse { + reportError(io, template_path, line_num, "expected 'NAME VALUE'", .{}); + error_count.* += 1; + continue; + }; + const config_name = line[0..first_space]; + const value_str = std.mem.trimStart(u8, line[first_space + 1 ..], " "); + + if (!std.mem.startsWith(u8, value_str, "?")) { + try out.print("{s} {s}\n", .{ config_name, value_str }); + _ = ConfigHeaderExt.parseValue(value_str) orelse { + reportError(io, template_path, line_num, "invalid config value '{s}'", .{value_str}); + error_count.* += 1; + }; + continue; + } + + // Query: NAME ? PASS|FAIL [define=...] [include=...] [COMPILE_BODY] + const query_str = std.mem.trimStart(u8, value_str[1..], " "); + _, const pass_end = ConfigHeaderExt.parseValue(query_str) orelse { + reportError(io, template_path, line_num, "expected PASS_VALUE after ? but got '{s}'", .{query_str}); + error_count.* += 1; + continue; + }; + const pass_text = query_str[0..pass_end]; + if (pass_end >= query_str.len or query_str[pass_end] != '|') { + reportError(io, template_path, line_num, "expected '|' after PASS_VALUE but got '{s}'", .{query_str[pass_end..]}); + error_count.* += 1; + continue; + } + const after_pipe = query_str[pass_end + 1 ..]; + _, const fail_end = ConfigHeaderExt.parseValue(after_pipe) orelse { + reportError(io, template_path, line_num, "expected FAIL_VALUE after '|' but got '{s}'", .{after_pipe}); + error_count.* += 1; + continue; + }; + const fail_text = after_pipe[0..fail_end]; + const query_expr = std.mem.trimStart(u8, after_pipe[fail_end..], " "); + const success = try evalQuery( + arena, + io, + config, + out, + config_name, + query_expr, + template_path, + line_num, + error_count, + ); + try out.print("{s} {s}\n", .{ config_name, if (success) pass_text else fail_text }); + } +} + +const ExprIterator = struct { + const State = union(enum) { + define: Common, + include: Common, + compile, + done, + }; + + const Common = union(enum) { + check, + values: std.mem.SplitIterator(u8, .scalar), + }; + + pub const Error = error{ Empty, Unexpected, OutOfOrder }; + + expr: []const u8, + pos: usize = 0, + state: State = .{ .define = .check }, + + fn nextDefine(self: *ExprIterator) Error!?[]const u8 { + const common = &self.state.define; + if (self.nextCommon("define=", common)) |value| return value; + self.state = .{ .include = .check }; + return null; + } + + fn nextInclude(self: *ExprIterator) Error!?[]const u8 { + const common = &self.state.include; + if (self.nextCommon("include=", common)) |value| return value; + self.state = .compile; + return null; + } + + fn compile(self: *ExprIterator) ?[]const u8 { + std.debug.assert(self.state == .compile); + self.state = .done; + const rem = std.mem.trimStart(u8, self.expr[self.pos..], " "); + if (rem.len == 0) return null; + self.pos = self.expr.len; + return rem; + } + + fn nextCommon(self: *ExprIterator, prefix: []const u8, common: *Common) ?[]const u8 { + switch (common.*) { + .check => { + const token, const new_pos = lex(self.expr, self.pos) orelse return null; + if (!std.mem.startsWith(u8, token, prefix)) return null; + common.* = .{ .values = std.mem.splitScalar(u8, token[prefix.len..], ',') }; + self.pos = new_pos; + }, + .values => {}, + } + return common.values.next(); + } + + pub const FmtError = struct { + it: *const ExprIterator, + err: Error, + + pub fn format(f: *const FmtError, writer: *std.Io.Writer) error{WriteFailed}!void { + switch (f.err) { + error.Empty => try writer.writeAll("expression cannot be empty"), + error.Unexpected => try writer.print( + "unexpected content at position {}: '{s}'", + .{ f.it.pos, f.it.expr[f.it.pos..] }, + ), + error.OutOfOrder => try writer.print( + "clauses out of order at position {}: '{s}' (expected: [define ...] [include ...] [compile ...])", + .{ f.it.pos, f.it.expr[f.it.pos..] }, + ), + } + } + }; + + pub fn fmtError(it: *const ExprIterator, err: Error) FmtError { + return .{ .it = it, .err = err }; + } +}; + +/// Parse a value starting at pos. Values can be: +/// bare: undef, defined, true, false, INTEGER +/// ident: 'LITERAL' +/// string: "LITERAL" +/// Returns the value (including quotes) and the position after it. +fn lex(s: []const u8, pos: usize) ?struct { []const u8, usize } { + const start = std.mem.indexOfNonePos(u8, s, pos, " ") orelse return null; + if (std.mem.indexOfScalarPos(u8, s, start, ' ')) |end| return .{ s[start..end], end + 1 }; + return .{ s[start..], s.len }; +} + +test ExprIterator { + // include only + { + var it = ExprIterator{ .expr = "include=alloca.h" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expectEqualStrings("alloca.h", (try it.nextInclude()).?); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expect(it.compile() == null); + } + // multiple includes with commas + { + var it = ExprIterator{ .expr = "include=sys/time.h,sys/resource.h" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expectEqualStrings("sys/time.h", (try it.nextInclude()).?); + try std.testing.expectEqualStrings("sys/resource.h", (try it.nextInclude()).?); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expect(it.compile() == null); + } + // define + include + compile body + { + var it = ExprIterator{ + .expr = "define=_GNU_SOURCE include=unistd.h int main(){close_range(0,0,0);}", + }; + try std.testing.expectEqualStrings("_GNU_SOURCE", (try it.nextDefine()).?); + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expectEqualStrings("unistd.h", (try it.nextInclude()).?); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expectEqualStrings("int main(){close_range(0,0,0);}", it.compile().?); + } + // compile body only + { + var it = ExprIterator{ .expr = "int main(){return 0;}" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expectEqualStrings("int main(){return 0;}", it.compile().?); + } + // empty + { + var it = ExprIterator{ .expr = "" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expect(it.compile() == null); + } +} + +fn evalQuery( + arena: std.mem.Allocator, + io: std.Io, + config: *const Config, + out: *std.Io.Writer, + config_name: []const u8, + expr: []const u8, + file_path: []const u8, + line_num: u32, + errors: *u32, +) error{WriteFailed}!bool { + var it = ExprIterator{ .expr = expr }; + + while (it.nextDefine() catch |err| { + exprErr(io, file_path, line_num, errors, &it, err); + return false; + }) |_| {} + + var has_headers = false; + while (it.nextInclude() catch |err| { + exprErr(io, file_path, line_num, errors, &it, err); + return false; + }) |header| { + _ = header; + has_headers = true; + } + const maybe_compile = it.compile(); + if (!has_headers and maybe_compile == null) return true; + return try doCompile( + arena, + io, + config.zig_exe orelse fatal("compile query requires --zig-exe", .{}), + config.target_triple orelse fatal("compile query requires --target", .{}), + config.cache_dir orelse fatal("compile query requires --cache-dir", .{}), + out, + config_name, + expr, + file_path, + line_num, + errors, + ); +} + +// assumes expr is valid has as a compile block +fn writeSource(w: *std.Io.Writer, expr: []const u8) error{WriteFailed}!void { + var it = ExprIterator{ .expr = expr }; + while (it.nextDefine() catch unreachable) |d| { + try w.print("#define {s}\n", .{d}); + } + while (it.nextInclude() catch unreachable) |h| { + try w.print("#include <{s}>\n", .{h}); + } + if (it.compile()) |c| try w.writeAll(c); + try w.flush(); +} + +fn exprErr(io: std.Io, file_path: []const u8, line_num: u32, errors: *u32, it: *const ExprIterator, err: ExprIterator.Error) void { + reportError(io, file_path, line_num, "bad expression: {f}", .{it.fmtError(err)}); + errors.* += 1; +} + +fn doCompile( + arena: std.mem.Allocator, + io: std.Io, + zig_exe: []const u8, + target_triple: []const u8, + cache_dir: []const u8, + out: *std.Io.Writer, + config_name: []const u8, + expr: []const u8, + file_path: []const u8, + line_num: u32, + errors: *u32, +) error{WriteFailed}!bool { + const sep = std.fs.path.sep_str; + const pid = switch (@import("builtin").os.tag) { + .linux => std.os.linux.getpid(), + .macos, .ios => std.c.getpid(), + else => @compileError("unsupported OS"), + }; + const tid = std.Thread.getCurrentId(); + var path_buf: [std.fs.max_path_bytes]u8 = undefined; + const source_path = std.fmt.bufPrint(&path_buf, "{s}" ++ sep ++ "tmp" ++ sep ++ "configquery-{}-{}.c", .{ cache_dir, pid, tid }) catch unreachable; + std.Io.Dir.cwd().createDirPath(io, std.fs.path.dirname(source_path).?) catch |err| + fatal("failed to create '{s}': {t}", .{ std.fs.path.dirname(source_path).?, err }); + + { + var file = std.Io.Dir.cwd().createFile(io, source_path, .{}) catch |err| + fatal("failed to create '{s}' with {t}", .{ source_path, err }); + defer file.close(io); + var buf: [4096]u8 = undefined; + var w = file.writer(io, &buf); + writeSource(&w.interface, expr) catch fatal("failed to write source: {t}", .{w.err.?}); + std.debug.assert(w.interface.end == 0); + } + + const result = std.process.run(arena, io, .{ + .argv = &.{ zig_exe, "build-exe", "-lc", "-target", target_triple, source_path, "-fno-emit-bin" }, + }) catch |err| fatal("zig build-exe failed with {t}", .{err}); + var delete_source = true; + defer if (delete_source) std.Io.Dir.cwd().deleteFile(io, source_path) catch |err| + fatal("failed to delete '{s}': {t}", .{ source_path, err }); + switch (result.term) { + .exited => |code| if (code == 0) return true, + inline else => |sig, kind| { + reportError(io, file_path, line_num, "zig build-exe terminated ({t}) with {}", .{ kind, sig }); + errors.* += 1; + return false; + }, + } + + // Write compile errors as comments in the output + try out.print("# compilation for '{s}' failed with the following:\n", .{config_name}); + var stderr_it = std.mem.splitScalar(u8, result.stderr, '\n'); + while (stderr_it.next()) |stderr_line| { + if (stderr_line.len > 0) try out.print("# {s}\n", .{stderr_line}); + } + + // Expected errors (file not found, undeclared function, etc.) mean the + // feature is not available. Unexpected errors indicate a bad template. + var has_expected_error = false; + var has_unexpected_error = false; + var err_it = std.mem.splitScalar(u8, result.stderr, '\n'); + while (err_it.next()) |err_line_raw| { + const err_line = std.mem.trimEnd(u8, err_line_raw, "\r"); + if (err_line.len == 0) continue; + const error_prefix = "error: "; + const error_start = std.mem.indexOf(u8, err_line, error_prefix) orelse continue; + const err_msg = err_line[error_start + error_prefix.len ..]; + if (std.mem.endsWith(u8, err_msg, "file not found") or + std.mem.startsWith(u8, err_msg, "call to undeclared function ") or + std.mem.startsWith(u8, err_msg, "call to undeclared library function ") or + std.mem.startsWith(u8, err_msg, "use of undeclared identifier") or + std.mem.startsWith(u8, err_msg, "invalid instruction mnemonic") or + std.mem.startsWith(u8, err_msg, "function definition is not allowed") or + std.mem.startsWith(u8, err_msg, "unknown type name") or + std.mem.startsWith(u8, err_msg, "no member named") or + std.mem.startsWith(u8, err_msg, "incomplete definition of type") or + std.mem.startsWith(u8, err_msg, "implicit declaration of function") or + std.mem.startsWith(u8, err_msg, "redefinition of")) + { + has_expected_error = true; + } else { + has_unexpected_error = true; + } + } + + if (has_unexpected_error and !has_expected_error) { + delete_source = false; + reportError(io, file_path, line_num, "compile check failed with unexpected error(s)", .{}); + errors.* += 1; + } + + return false; +} + +fn reportError(io: std.Io, file: []const u8, line: u32, comptime fmt: []const u8, args: anytype) void { + var stderr = std.Io.File.stderr().writer(io, &.{}); + stderr.interface.print("{s}:{}: " ++ fmt ++ "\n", .{ file, line } ++ args) catch {}; +} + +fn oom(err: error{OutOfMemory}) noreturn { + fatal("{s}", .{@errorName(err)}); +} + +fn fatal(comptime fmt: []const u8, args: anytype) noreturn { + std.log.err(fmt, args); + std.process.exit(1); +} + +const std = @import("std"); +const ConfigHeaderExt = @import("ConfigHeaderExt.zig");