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..29397d2 --- /dev/null +++ b/ConfigHeaderExt.zig @@ -0,0 +1,167 @@ +/// Add config values to a ConfigHeader from a lazy path. +/// The lazy path should contain lines in the format: NAME TYPE [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); +} + +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) 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"); + } + } +}; + +pub const Entry = struct { + name: []const u8, + value: ConfigHeader.Value, + + pub const ParseError = error{ + MissingType, + UnknownType, + InvalidInt, + UnexpectedValue, + }; + + /// Parse a line of output in the format: NAME TYPE [VALUE] + 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.MissingType; + const name = line[0..first_space]; + const after_name = std.mem.trimStart(u8, line[first_space + 1 ..], " "); + const second_space = std.mem.indexOfScalar(u8, after_name, ' '); + const type_str = if (second_space) |s| after_name[0..s] else after_name; + const raw_value = if (second_space) |s| std.mem.trimStart(u8, after_name[s + 1 ..], " ") else ""; + + if (@as(?ConfigHeader.Value, blk: { + if (std.mem.eql(u8, type_str, "undef")) break :blk .undef; + if (std.mem.eql(u8, type_str, "defined")) break :blk .defined; + break :blk null; + })) |value| { + if (raw_value.len > 0) return error.UnexpectedValue; + return .{ .name = name, .value = value }; + } + const value: ConfigHeader.Value = if (std.mem.eql(u8, type_str, "bool")) + .{ .boolean = std.mem.eql(u8, raw_value, "true") } + else if (std.mem.eql(u8, type_str, "int")) + .{ .int = std.fmt.parseInt(i64, raw_value, 10) catch return error.InvalidInt } + else if (std.mem.eql(u8, type_str, "ident")) + .{ .ident = raw_value } + else if (std.mem.eql(u8, type_str, "string")) + .{ .string = raw_value } + else if (std.mem.eql(u8, type_str, "?u1")) + if (std.mem.eql(u8, raw_value, "1")) + ConfigHeader.Value{ .int = 1 } + else + ConfigHeader.Value.undef + else + return error.UnknownType; + + return .{ .name = name, .value = value }; + } +}; + +test "undef" { + const e = try Entry.parse("FOO undef"); + try std.testing.expectEqualStrings("FOO", e.name); + try std.testing.expect(e.value == .undef); + try std.testing.expectError(error.UnexpectedValue, Entry.parse("FOO undef something")); +} + +test "defined" { + const e = try Entry.parse("FOO defined"); + try std.testing.expectEqualStrings("FOO", e.name); + try std.testing.expect(e.value == .defined); + try std.testing.expectError(error.UnexpectedValue, Entry.parse("FOO defined something")); +} + +test "bool" { + const t = try Entry.parse("FOO bool true"); + try std.testing.expect(t.value.boolean == true); + + const f = try Entry.parse("FOO bool false"); + try std.testing.expect(f.value.boolean == false); +} + +test "int" { + const r = try Entry.parse("SIZEOF_INT int 4"); + try std.testing.expect(r.value.int == 4); + + const neg = try Entry.parse("FOO int -1"); + try std.testing.expect(neg.value.int == -1); + + const zero = try Entry.parse("FOO int 0"); + try std.testing.expect(zero.value.int == 0); + + // non-numeric + try std.testing.expectError(error.InvalidInt, Entry.parse("FOO int notanumber")); +} + +test "ident" { + const r = try Entry.parse("RETSIGTYPE ident void"); + try std.testing.expectEqualStrings("void", r.value.ident); + + const e = try Entry.parse("FOO ident"); + try std.testing.expectEqualStrings("", e.value.ident); +} + +test "string" { + const r = try Entry.parse("PY_HASH string md5,sha1"); + try std.testing.expectEqualStrings("md5,sha1", r.value.string); + + const e = try Entry.parse("FOO string"); + try std.testing.expectEqualStrings("", e.value.string); + + const s = try Entry.parse("FOO string hello world"); + try std.testing.expectEqualStrings("hello world", s.value.string); +} + +test "?u1" { + const one = try Entry.parse("HAVE_FORK ?u1 1"); + try std.testing.expect(one.value.int == 1); + + const u = try Entry.parse("HAVE_FORK ?u1 undef"); + try std.testing.expect(u.value == .undef); +} + +test "missing type" { + try std.testing.expectError(error.MissingType, Entry.parse("FOO")); +} + +test "unknown type" { + try std.testing.expectError(error.UnknownType, Entry.parse("FOO badtype 42")); +} + +const std = @import("std"); +const ConfigHeader = std.Build.Step.ConfigHeader; diff --git a/build.zig b/build.zig index 7cd0a2e..74d9fa7 100644 --- a/build.zig +++ b/build.zig @@ -50,6 +50,14 @@ 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, + }), + }); + b.step("configquery", "build configquery tool").dependOn(&configquery_exe.step); const makesetup_exe = b.addExecutable(.{ .name = "makesetup", .root_module = b.createModule(.{ @@ -93,7 +101,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 +210,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 +232,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 +673,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 +820,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 +859,38 @@ 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, .{})); - } + const run = b.addRunArtifact(configquery_exe); + { + const libc_dirs = try std.zig.LibCDirs.detect( + b.allocator, + b.graph.io, + b.graph.zig_lib_directory.path.?, + &target.result, + target.query.isNativeAbi(), + true, + null, + &b.graph.environ_map, + ); + for (libc_dirs.libc_include_dir_list) |dir| { + run.addArg(b.fmt("-I{s}", .{dir})); } - }.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); + 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 +1148,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 +1202,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 +1234,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..7dfce10 --- /dev/null +++ b/config-3.11.13 @@ -0,0 +1,13 @@ +################################################################################ +# Headers +################################################################################ +HAVE_MEMORY_H ?u1 $(include memory.h) + +################################################################################ +# Functions +################################################################################ +HAVE_TTYNAME ?u1 $(include unistd.h compile int main(){ttyname(0);}) +HAVE_LIBGDBM_COMPAT ?u1 $(include gdbm.h compile int main(){GDBM_FILE gf; return 0;}) +HAVE_LIBNDBM ?u1 $(include ndbm.h compile int main(){DBM *db; return 0;}) +HAVE_LIBREADLINE ?u1 $(include readline/readline.h compile int main(){readline("prompt"); return 0;}) +HAVE_STDARG_PROTOTYPES ?u1 $(compile #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);}) diff --git a/config-3.12.11 b/config-3.12.11 new file mode 100644 index 0000000..a395041 --- /dev/null +++ b/config-3.12.11 @@ -0,0 +1,21 @@ +################################################################################ +# Headers +################################################################################ +HAVE_EDITLINE_READLINE_H ?u1 $(include editline/readline.h) +HAVE_LINUX_FS_H ?u1 $(include linux/fs.h) +HAVE_MINIX_CONFIG_H ?u1 $(include minix/config.h) +HAVE_NET_ETHERNET_H ?u1 $(include net/ethernet.h) +HAVE_PANEL_H ?u1 $(include panel.h) +HAVE_READLINE_READLINE_H ?u1 $(include readline/readline.h) +HAVE_STDIO_H ?u1 $(include stdio.h) +HAVE_SYS_PIDFD_H ?u1 $(include sys/pidfd.h) + +################################################################################ +# Functions +################################################################################ +HAVE_FFI_CLOSURE_ALLOC ?u1 $(include ffi.h compile int main(){ffi_closure_alloc(0, 0);}) +HAVE_FFI_PREP_CIF_VAR ?u1 $(include ffi.h compile int main(){ffi_prep_cif_var(0, 0, 0, 0, 0, 0);}) +HAVE_FFI_PREP_CLOSURE_LOC ?u1 $(include ffi.h compile int main(){ffi_prep_closure_loc(0, 0, 0, 0, 0);}) +HAVE_SETNS ?u1 $(define _GNU_SOURCE include sched.h compile int main(){setns(0, 0);}) +HAVE_TTYNAME_R ?u1 $(include unistd.h compile int main(){char buf[256]; ttyname_r(0, buf, sizeof(buf));}) +HAVE_UNSHARE ?u1 $(define _GNU_SOURCE include unistd.h compile int main(){unshare(0);}) diff --git a/config-common b/config-common new file mode 100644 index 0000000..3d343aa --- /dev/null +++ b/config-common @@ -0,0 +1,529 @@ +################################################################################ +# Headers +################################################################################ +HAVE_ALLOCA_H ?u1 $(include alloca.h) +HAVE_ASM_TYPES_H ?u1 $(include asm/types.h) +HAVE_BLUETOOTH_H ?u1 $(include bluetooth.h) +HAVE_BLUETOOTH_BLUETOOTH_H ?u1 $(include bluetooth/bluetooth.h) +HAVE_BZLIB_H ?u1 $(include bzlib.h) +HAVE_CONIO_H ?u1 $(include conio.h) +HAVE_CRYPT_H ?u1 $(include crypt.h) +HAVE_CURSES_H ?u1 $(include curses.h) +HAVE_DIRECT_H ?u1 $(include direct.h) +HAVE_DIRENT_H ?u1 $(include dirent.h) +HAVE_DB_H ?u1 $(include db.h) +HAVE_DLFCN_H ?u1 $(include dlfcn.h) +HAVE_ENDIAN_H ?u1 $(include endian.h) +HAVE_ERRNO_H ?u1 $(include errno.h) +HAVE_FCNTL_H ?u1 $(include fcntl.h) +HAVE_GDBM_DASH_NDBM_H ?u1 $(include gdbm-ndbm.h) +HAVE_GDBM_H ?u1 $(include gdbm.h) +HAVE_GDBM_NDBM_H ?u1 $(include gdbm/ndbm.h) +HAVE_GRP_H ?u1 $(include grp.h) +HAVE_IEEEFP_H ?u1 $(include ieeefp.h) +HAVE_IO_H ?u1 $(include io.h) +HAVE_INTTYPES_H ?u1 $(include inttypes.h) +HAVE_LANGINFO_H ?u1 $(include langinfo.h) +HAVE_LIBINTL_H ?u1 $(include libintl.h) +HAVE_LIBUTIL_H ?u1 $(include libutil.h) +HAVE_LINUX_LIMITS_H ?u1 $(include linux/limits.h) +HAVE_NETDB_H ?u1 $(include netdb.h) +HAVE_NETINET_IN_H ?u1 $(include netinet/in.h) +HAVE_NETPACKET_PACKET_H ?u1 $(include netpacket/packet.h) +HAVE_NET_IF_H ?u1 $(include net/if.h) +HAVE_POLL_H ?u1 $(include poll.h) +HAVE_PTHREAD_H ?u1 $(include pthread.h) +HAVE_PTY_H ?u1 $(include pty.h) +HAVE_SCHED_H ?u1 $(include sched.h) +HAVE_SETJMP_H ?u1 $(include setjmp.h) +HAVE_SHADOW_H ?u1 $(include shadow.h) +HAVE_SIGNAL_H ?u1 $(include signal.h) +HAVE_SPAWN_H ?u1 $(include spawn.h) +HAVE_STDINT_H ?u1 $(include stdint.h) +HAVE_STDLIB_H ?u1 $(include stdlib.h) +HAVE_STRINGS_H ?u1 $(include strings.h) +HAVE_STRING_H ?u1 $(include string.h) +HAVE_SYSEXITS_H ?u1 $(include sysexits.h) +HAVE_SYSLOG_H ?u1 $(include syslog.h) +HAVE_SYS_AUXV_H ?u1 $(include sys/auxv.h) +HAVE_SYS_EPOLL_H ?u1 $(include sys/epoll.h) +HAVE_SYS_EVENTFD_H ?u1 $(include sys/eventfd.h) +HAVE_SYS_FILE_H ?u1 $(include sys/file.h) +HAVE_SYS_IOCTL_H ?u1 $(include sys/ioctl.h) +HAVE_SYS_MMAN_H ?u1 $(include sys/mman.h) +HAVE_SYS_PARAM_H ?u1 $(include sys/param.h) +HAVE_SYS_POLL_H ?u1 $(include sys/poll.h) +HAVE_SYS_RANDOM_H ?u1 $(include sys/random.h) +HAVE_SYS_RESOURCE_H ?u1 $(include sys/resource.h) +HAVE_SYS_SELECT_H ?u1 $(include sys/select.h) +HAVE_SYS_SENDFILE_H ?u1 $(include sys/sendfile.h) +HAVE_SYS_SOCKET_H ?u1 $(include sys/socket.h) +HAVE_SYS_SOUNDCARD_H ?u1 $(include sys/soundcard.h) +HAVE_SYS_STATVFS_H ?u1 $(include sys/statvfs.h) +HAVE_SYS_STAT_H ?u1 $(include sys/stat.h) +HAVE_SYS_SYSCALL_H ?u1 $(include sys/syscall.h) +HAVE_SYS_SYSMACROS_H ?u1 $(include sys/sysmacros.h) +HAVE_SYS_TIMES_H ?u1 $(include sys/times.h) +HAVE_SYS_TIME_H ?u1 $(include sys/time.h) +HAVE_SYS_TYPES_H ?u1 $(include sys/types.h) +HAVE_SYS_UIO_H ?u1 $(include sys/uio.h) +HAVE_SYS_UN_H ?u1 $(include sys/un.h) +HAVE_SYS_UTSNAME_H ?u1 $(include sys/utsname.h) +HAVE_SYS_WAIT_H ?u1 $(include sys/wait.h) +HAVE_SYS_XATTR_H ?u1 $(include sys/xattr.h) +HAVE_TERMIOS_H ?u1 $(include termios.h) +HAVE_UNISTD_H ?u1 $(include unistd.h) +HAVE_UTIME_H ?u1 $(include utime.h) +HAVE_UTMP_H ?u1 $(include utmp.h) +HAVE_WCHAR_H ?u1 $(include wchar.h) +HAVE_LZMA_H ?u1 $(include lzma.h) +HAVE_NCURSES_H ?u1 $(include ncurses.h) +HAVE_NDBM_H ?u1 $(include ndmb.h) +HAVE_NDIR_H ?u1 $(include ndir.h) +HAVE_NETCAN_CAN_H ?u1 $(include netcan/can.h) +HAVE_PROCESS_H ?u1 $(include process.h) +HAVE_RPC_RPC_H ?u1 $(include rpc/rpc.h) +HAVE_STROPTS_H ?u1 $(include stropts.h) +HAVE_SYS_AUDIOIO_H ?u1 $(include sys/audioio.h) +HAVE_SYS_BSDTTY_H ?u1 $(include sys/bsdtty.h) +HAVE_SYS_DEVPOLL_H ?u1 $(include sys/devpoll.h) +HAVE_SYS_DIR_H ?u1 $(include sys/dir.h) +HAVE_SYS_ENDIAN_H ?u1 $(include sys/endian.h) +HAVE_SYS_EVENT_H ?u1 $(include sys/event.h) +HAVE_SYS_KERN_CONTROL_H ?u1 $(include sys/kern/contro.h) +HAVE_SYS_LOADAVG_H ?u1 $(include sys/loadavg.h) +HAVE_SYS_LOCK_H ?u1 $(include sys/lock.h) +HAVE_SYS_MEMFD_H ?u1 $(include sys/memfd.h) +HAVE_SYS_MKDEV_H ?u1 $(include sys/mkdev.h) +HAVE_SYS_MODEM_H ?u1 $(include sys/modem.h) +HAVE_SYS_NDIR_H ?u1 $(include sys/ndir.h) +HAVE_SYS_SYS_DOMAIN_H ?u1 $(include sys/sys/domain.h) +HAVE_SYS_TERMIO_H ?u1 $(include sys/termio.h) +HAVE_TERM_H ?u1 $(include term.h) +HAVE_UTIL_H ?u1 $(include util.h) +HAVE_UUID_H ?u1 $(include uuid.h) +HAVE_UUID_UUID_H ?u1 $(include uuid/uuid.h) +HAVE_ZLIB_H ?u1 $(include zlib.h) + +################################################################################ +# Functions +################################################################################ +HAVE_ACCEPT ?u1 $(include sys/socket.h compile int main(){accept(0, 0, 0);}) +HAVE_ACCEPT4 ?u1 $(include sys/socket.h compile int main(){accept4(0, 0, 0, 0);}) +HAVE_ACOSH ?u1 $(include math.h compile int main(){acosh(1.0);}) +HAVE_ALARM ?u1 $(include unistd.h compile int main(){alarm(1);}) +HAVE_ASINH ?u1 $(include math.h compile int main(){asinh(1.0);}) +HAVE_ATANH ?u1 $(include math.h compile int main(){atanh(0.5);}) +HAVE_BIND ?u1 $(include sys/socket.h compile int main(){bind(0, 0, 0);}) +HAVE_BIND_TEXTDOMAIN_CODESET ?u1 $(include libintl.h compile int main(){bind_textdomain_codeset(0, 0);}) +HAVE_CHMOD ?u1 $(include sys/stat.h compile int main(){chmod(0, 0);}) +HAVE_CHOWN ?u1 $(include unistd.h compile int main(){chown(0, 0, 0);}) +HAVE_CLOCK ?u1 $(include time.h compile int main(){clock();}) +HAVE_CLOCK_GETRES ?u1 $(include time.h compile int main(){struct timespec ts; clock_getres(CLOCK_REALTIME, &ts);}) +HAVE_CLOCK_GETTIME ?u1 $(include time.h compile int main(){struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts);}) +HAVE_CLOCK_NANOSLEEP ?u1 $(include time.h compile int main(){struct timespec ts = {0, 0}; clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0);}) +HAVE_CLOCK_SETTIME ?u1 $(include time.h compile int main(){struct timespec ts = {0, 0}; clock_settime(CLOCK_REALTIME, &ts);}) +HAVE_CLOSE_RANGE ?u1 $(define _GNU_SOURCE include unistd.h compile int main(){close_range(0, 0, 0);}) +HAVE_CONFSTR ?u1 $(include unistd.h compile int main(){confstr(0, 0, 0);}) +HAVE_CONNECT ?u1 $(include sys/socket.h compile int main(){connect(0, 0, 0);}) +HAVE_COPY_FILE_RANGE ?u1 $(include unistd.h compile int main(){copy_file_range(0, 0, 0, 0, 0, 0);}) +HAVE_CTERMID ?u1 $(include stdio.h compile int main(){ctermid(0);}) +HAVE_DLOPEN ?u1 $(include dlfcn.h compile int main(){dlopen(0, 0);}) +HAVE_DUP ?u1 $(include unistd.h compile int main(){dup(0);}) +HAVE_DUP2 ?u1 $(include unistd.h compile int main(){dup2(0, 0);}) +HAVE_DUP3 ?u1 $(include unistd.h compile int main(){dup3(0, 0, 0);}) +HAVE_EPOLL_CREATE1 ?u1 $(include sys/epoll.h compile int main(){epoll_create1(0);}) +HAVE_ERF ?u1 $(include math.h compile int main(){erf(1.0);}) +HAVE_ERFC ?u1 $(include math.h compile int main(){erfc(1.0);}) +HAVE_EVENTFD ?u1 $(include sys/eventfd.h compile int main(){eventfd(0, 0);}) +HAVE_EXECV ?u1 $(include unistd.h compile int main(){execv(0, 0);}) +HAVE_EXPLICIT_BZERO ?u1 $(include strings.h compile int main(){explicit_bzero(0, 0);}) +HAVE_EXPM1 ?u1 $(include math.h compile int main(){expm1(1.0);}) +HAVE_FACCESSAT ?u1 $(include unistd.h compile int main(){faccessat(0, 0, 0, 0);}) +HAVE_FCHDIR ?u1 $(include unistd.h compile int main(){fchdir(0);}) +HAVE_FCHMOD ?u1 $(include sys/stat.h compile int main(){fchmod(0, 0);}) +HAVE_FCHMODAT ?u1 $(include sys/stat.h compile int main(){fchmodat(0, 0, 0, 0);}) +HAVE_FCHOWN ?u1 $(include unistd.h compile int main(){fchown(0, 0, 0);}) +HAVE_FCHOWNAT ?u1 $(include unistd.h compile int main(){fchownat(0, 0, 0, 0, 0);}) +HAVE_FDATASYNC ?u1 $(include unistd.h compile int main(){fdatasync(0);}) +HAVE_FDOPENDIR ?u1 $(include dirent.h compile int main(){fdopendir(0);}) +HAVE_FEXECVE ?u1 $(include unistd.h compile int main(){fexecve(0, 0, 0);}) +HAVE_FLOCK ?u1 $(include sys/file.h compile int main(){flock(0, 0);}) +HAVE_FORK ?u1 $(include unistd.h compile int main(){fork();}) +HAVE_FORKPTY ?u1 $(include pty.h compile int main(){forkpty(0, 0, 0, 0);}) +HAVE_FPATHCONF ?u1 $(include unistd.h compile int main(){fpathconf(0, 0);}) +HAVE_FSEEKO ?u1 $(include stdio.h compile int main(){fseeko(0, 0, 0);}) +HAVE_FSTATAT ?u1 $(include sys/stat.h compile int main(){struct stat st; fstatat(0, 0, &st, 0);}) +HAVE_FSTATVFS ?u1 $(include sys/statvfs.h compile int main(){struct statvfs st; fstatvfs(0, &st);}) +HAVE_FSYNC ?u1 $(include unistd.h compile int main(){fsync(0);}) +HAVE_FTELLO ?u1 $(include stdio.h compile int main(){ftello(0);}) +HAVE_FTIME ?u1 $(include sys/timeb.h compile int main(){struct timeb tb; ftime(&tb);}) +HAVE_FTRUNCATE ?u1 $(include unistd.h compile int main(){ftruncate(0, 0);}) +HAVE_FUTIMENS ?u1 $(include sys/stat.h compile int main(){futimens(0, 0);}) +HAVE_FUTIMES ?u1 $(include sys/time.h compile int main(){futimes(0, 0);}) +HAVE_FUTIMESAT ?u1 $(include sys/time.h compile int main(){futimesat(0, 0, 0);}) +HAVE_GAI_STRERROR ?u1 $(include netdb.h compile int main(){gai_strerror(0);}) +HAVE_GETADDRINFO ?u1 $(include netdb.h compile int main(){getaddrinfo(0, 0, 0, 0);}) +HAVE_GETC_UNLOCKED ?u1 $(include stdio.h compile int main(){getc_unlocked((FILE*)0);}) +HAVE_GETEGID ?u1 $(include unistd.h compile int main(){getegid();}) +HAVE_GETENTROPY ?u1 $(include unistd.h compile int main(){getentropy(0, 0);}) +HAVE_GETEUID ?u1 $(include unistd.h compile int main(){geteuid();}) +HAVE_GETGID ?u1 $(include unistd.h compile int main(){getgid();}) +HAVE_GETGRGID ?u1 $(include grp.h compile int main(){getgrgid(0);}) +HAVE_GETGRGID_R ?u1 $(include grp.h compile int main(){struct group grp; char buf[1024]; struct group *result; getgrgid_r(0, &grp, buf, sizeof(buf), &result);}) +HAVE_GETGRNAM_R ?u1 $(include grp.h compile int main(){struct group grp; char buf[1024]; struct group *result; getgrnam_r(0, &grp, buf, sizeof(buf), &result);}) +HAVE_GETGROUPLIST ?u1 $(include grp.h compile int main(){int groups; int ngroups = 1; getgrouplist(0, 0, &groups, &ngroups);}) +HAVE_GETGROUPS ?u1 $(include unistd.h compile int main(){getgroups(0, 0);}) +HAVE_GETHOSTBYADDR ?u1 $(include netdb.h compile int main(){gethostbyaddr(0, 0, 0);}) +HAVE_GETHOSTBYNAME ?u1 $(include netdb.h compile int main(){gethostbyname(0);}) +HAVE_GETHOSTBYNAME_R ?u1 $(include netdb.h compile 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 ?u1 $(include unistd.h compile int main(){gethostname(0, 0);}) +HAVE_GETITIMER ?u1 $(include sys/time.h compile int main(){struct itimerval it; getitimer(0, &it);}) +HAVE_GETLOADAVG ?u1 $(include stdlib.h compile int main(){double loadavg[3]; getloadavg(loadavg, 3);}) +HAVE_GETLOGIN ?u1 $(include unistd.h compile int main(){getlogin();}) +HAVE_GETNAMEINFO ?u1 $(include netdb.h compile int main(){getnameinfo(0, 0, 0, 0, 0, 0, 0);}) +HAVE_GETPAGESIZE ?u1 $(include unistd.h compile int main(){getpagesize();}) +HAVE_GETPEERNAME ?u1 $(include sys/socket.h compile int main(){getpeername(0, 0, 0);}) +HAVE_GETPGID ?u1 $(include unistd.h compile int main(){getpgid(0);}) +HAVE_GETPGRP ?u1 $(include unistd.h compile int main(){getpgrp();}) +HAVE_GETPID ?u1 $(include unistd.h compile int main(){getpid();}) +HAVE_GETPPID ?u1 $(include unistd.h compile int main(){getppid();}) +HAVE_GETPRIORITY ?u1 $(include sys/resource.h compile int main(){getpriority(0, 0);}) +HAVE_GETPROTOBYNAME ?u1 $(include netdb.h compile int main(){getprotobyname(0);}) +HAVE_GETPWENT ?u1 $(include pwd.h compile int main(){getpwent();}) +HAVE_GETPWNAM_R ?u1 $(include pwd.h compile int main(){struct passwd pw; char buf[1024]; struct passwd *result; getpwnam_r(0, &pw, buf, sizeof(buf), &result);}) +HAVE_GETPWUID ?u1 $(include pwd.h compile int main(){getpwuid(0);}) +HAVE_GETPWUID_R ?u1 $(include pwd.h compile int main(){struct passwd pw; char buf[1024]; struct passwd *result; getpwuid_r(0, &pw, buf, sizeof(buf), &result);}) +HAVE_GETRANDOM ?u1 $(include sys/random.h compile int main(){getrandom(0, 0, 0);}) +HAVE_GETRESGID ?u1 $(include unistd.h compile int main(){gid_t rgid, egid, sgid; getresgid(&rgid, &egid, &sgid);}) +HAVE_GETRESUID ?u1 $(define _GNU_SOURCE include unistd.h compile int main(){uid_t ruid, euid, suid; getresuid(&ruid, &euid, &suid);}) +HAVE_GETRUSAGE ?u1 $(include sys/resource.h compile int main(){struct rusage ru; getrusage(0, &ru);}) +HAVE_GETSERVBYNAME ?u1 $(include netdb.h compile int main(){getservbyname(0, 0);}) +HAVE_GETSERVBYPORT ?u1 $(include netdb.h compile int main(){getservbyport(0, 0);}) +HAVE_GETSID ?u1 $(include unistd.h compile int main(){getsid(0);}) +HAVE_GETSOCKNAME ?u1 $(include sys/socket.h compile int main(){getsockname(0, 0, 0);}) +HAVE_GETSPENT ?u1 $(include shadow.h compile int main(){getspent();}) +HAVE_GETSPNAM ?u1 $(include shadow.h compile int main(){getspnam(0);}) +HAVE_GETUID ?u1 $(include unistd.h compile int main(){getuid();}) +HAVE_GETWD ?u1 $(include unistd.h compile int main(){getwd(0);}) +HAVE_HSTRERROR ?u1 $(include netdb.h compile int main(){hstrerror(0);}) +HAVE_HTOLE64 ?u1 $(include endian.h compile int main(){htole64(0);}) +HAVE_IF_NAMEINDEX ?u1 $(include net/if.h compile int main(){if_nameindex();}) +HAVE_INET_ATON ?u1 $(include arpa/inet.h compile int main(){struct in_addr addr; inet_aton(0, &addr);}) +HAVE_INET_NTOA ?u1 $(include arpa/inet.h compile int main(){struct in_addr addr; inet_ntoa(addr);}) +HAVE_INET_PTON ?u1 $(include arpa/inet.h compile int main(){inet_pton(0, 0, 0);}) +HAVE_INITGROUPS ?u1 $(include grp.h compile int main(){initgroups(0, 0);}) +HAVE_KILL ?u1 $(include signal.h compile int main(){kill(0, 0);}) +HAVE_KILLPG ?u1 $(include signal.h compile int main(){killpg(0, 0);}) +HAVE_LCHOWN ?u1 $(include unistd.h compile int main(){lchown(0, 0, 0);}) +HAVE_LINK ?u1 $(include unistd.h compile int main(){link(0, 0);}) +HAVE_LINKAT ?u1 $(include unistd.h compile int main(){linkat(0, 0, 0, 0, 0);}) +HAVE_LISTEN ?u1 $(include sys/socket.h compile int main(){listen(0, 0);}) +HAVE_LOCKF ?u1 $(include unistd.h compile int main(){lockf(0, 0, 0);}) +HAVE_LOG1P ?u1 $(include math.h compile int main(){log1p(1.0);}) +HAVE_LOG2 ?u1 $(include math.h compile int main(){log2(2.0);}) +HAVE_LOGIN_TTY ?u1 $(include utmp.h compile int main(){login_tty(0);}) +HAVE_LSTAT ?u1 $(include sys/stat.h compile int main(){struct stat st; lstat(0, &st);}) +HAVE_LUTIMES ?u1 $(include sys/time.h compile int main(){lutimes(0, 0);}) +HAVE_MADVISE ?u1 $(include sys/mman.h compile int main(){madvise(0, 0, 0);}) +HAVE_MAKEDEV ?u1 $(include sys/types.h compile int main(){makedev(0, 0);}) +HAVE_MBRTOWC ?u1 $(include wchar.h compile int main(){wchar_t wc; mbrtowc(&wc, 0, 0, 0);}) +HAVE_MEMFD_CREATE ?u1 $(include sys/mman.h compile int main(){memfd_create(0, 0);}) +HAVE_MEMRCHR ?u1 $(include string.h compile int main(){memrchr(0, 0, 0);}) +HAVE_MKDIRAT ?u1 $(include sys/stat.h compile int main(){mkdirat(0, 0, 0);}) +HAVE_MKFIFO ?u1 $(include sys/stat.h compile int main(){mkfifo(0, 0);}) +HAVE_MKFIFOAT ?u1 $(include sys/stat.h compile int main(){mkfifoat(0, 0, 0);}) +HAVE_MKNOD ?u1 $(include sys/stat.h compile int main(){mknod(0, 0, 0);}) +HAVE_MKNODAT ?u1 $(include sys/stat.h compile int main(){mknodat(0, 0, 0, 0);}) +HAVE_MKTIME ?u1 $(include time.h compile int main(){struct tm tm; mktime(&tm);}) +HAVE_MMAP ?u1 $(include sys/mman.h compile int main(){mmap(0, 0, 0, 0, 0, 0);}) +HAVE_MREMAP ?u1 $(define _GNU_SOURCE include sys/mman.h compile int main(){mremap(0, 0, 0, 0);}) +HAVE_NANOSLEEP ?u1 $(include time.h compile int main(){struct timespec ts = {0, 0}; nanosleep(&ts, 0);}) +HAVE_NICE ?u1 $(include unistd.h compile int main(){nice(0);}) +HAVE_OPENAT ?u1 $(include fcntl.h compile int main(){openat(0, 0, 0);}) +HAVE_OPENDIR ?u1 $(include dirent.h compile int main(){opendir(0);}) +HAVE_OPENPTY ?u1 $(include pty.h compile int main(){openpty(0, 0, 0, 0, 0);}) +HAVE_PATHCONF ?u1 $(include unistd.h compile int main(){pathconf(0, 0);}) +HAVE_PAUSE ?u1 $(include unistd.h compile int main(){pause();}) +HAVE_PIPE ?u1 $(include unistd.h compile int main(){int pipefd[2]; pipe(pipefd);}) +HAVE_PIPE2 ?u1 $(include unistd.h compile int main(){int pipefd[2]; pipe2(pipefd, 0);}) +HAVE_POLL ?u1 $(include poll.h compile int main(){poll(0, 0, 0);}) +HAVE_POSIX_FADVISE ?u1 $(include fcntl.h compile int main(){posix_fadvise(0, 0, 0, 0);}) +HAVE_POSIX_FALLOCATE ?u1 $(include fcntl.h compile int main(){posix_fallocate(0, 0, 0);}) +HAVE_POSIX_SPAWN ?u1 $(include spawn.h compile int main(){posix_spawn(0, 0, 0, 0, 0, 0);}) +HAVE_POSIX_SPAWNP ?u1 $(include spawn.h compile int main(){posix_spawnp(0, 0, 0, 0, 0, 0);}) +HAVE_PREAD ?u1 $(include unistd.h compile int main(){pread(0, 0, 0, 0);}) +HAVE_PREADV ?u1 $(include sys/uio.h compile int main(){preadv(0, 0, 0, 0);}) +HAVE_PREADV2 ?u1 $(include sys/uio.h compile int main(){preadv2(0, 0, 0, 0, 0);}) +HAVE_PRLIMIT ?u1 $(include sys/time.h,sys/resource.h compile int main(){prlimit(0, 0, 0, 0);}) +HAVE_PTHREAD_KILL ?u1 $(include signal.h compile int main(){pthread_kill(0, 0);}) +HAVE_PTHREAD_SIGMASK ?u1 $(include pthread.h compile int main(){pthread_sigmask(0, 0, 0);}) +HAVE_PWRITE ?u1 $(include unistd.h compile int main(){pwrite(0, 0, 0, 0);}) +HAVE_PWRITEV ?u1 $(include sys/uio.h compile int main(){pwritev(0, 0, 0, 0);}) +HAVE_PWRITEV2 ?u1 $(include sys/uio.h compile int main(){pwritev2(0, 0, 0, 0, 0);}) +HAVE_READLINK ?u1 $(include unistd.h compile int main(){readlink(0, 0, 0);}) +HAVE_READLINKAT ?u1 $(include unistd.h compile int main(){readlinkat(0, 0, 0, 0);}) +HAVE_READV ?u1 $(include sys/uio.h compile int main(){readv(0, 0, 0);}) +HAVE_REALPATH ?u1 $(include stdlib.h compile int main(){realpath(0, 0);}) +HAVE_RECVFROM ?u1 $(include sys/socket.h compile int main(){recvfrom(0, 0, 0, 0, 0, 0);}) +HAVE_RENAMEAT ?u1 $(include stdio.h compile int main(){renameat(0, 0, 0, 0);}) +HAVE_SCHED_GET_PRIORITY_MAX ?u1 $(include sched.h compile int main(){sched_get_priority_max(0);}) +HAVE_SCHED_RR_GET_INTERVAL ?u1 $(include sched.h compile int main(){struct timespec ts; sched_rr_get_interval(0, &ts);}) +HAVE_SCHED_SETAFFINITY ?u1 $(define _GNU_SOURCE include sched.h compile int main(){sched_setaffinity(0, 0, 0);}) +HAVE_SCHED_SETPARAM ?u1 $(include sched.h compile int main(){struct sched_param sp; sched_setparam(0, &sp);}) +HAVE_SCHED_SETSCHEDULER ?u1 $(include sched.h compile int main(){struct sched_param sp; sched_setscheduler(0, 0, &sp);}) +HAVE_SEM_CLOCKWAIT ?u1 $(include semaphore.h compile int main(){sem_clockwait(0, 0, 0);}) +HAVE_SEM_GETVALUE ?u1 $(include semaphore.h compile int main(){sem_getvalue(0, 0);}) +HAVE_SEM_OPEN ?u1 $(include semaphore.h compile int main(){sem_open(0, 0);}) +HAVE_SEM_TIMEDWAIT ?u1 $(include semaphore.h compile int main(){sem_timedwait(0, 0);}) +HAVE_SEM_UNLINK ?u1 $(include semaphore.h compile int main(){sem_unlink(0);}) +HAVE_SENDFILE ?u1 $(include sys/sendfile.h compile int main(){sendfile(0, 0, 0, 0);}) +HAVE_SENDTO ?u1 $(include sys/socket.h compile int main(){sendto(0, 0, 0, 0, 0, 0);}) +HAVE_SETEGID ?u1 $(include unistd.h compile int main(){setegid(0);}) +HAVE_SETEUID ?u1 $(include unistd.h compile int main(){seteuid(0);}) +HAVE_SETGID ?u1 $(include unistd.h compile int main(){setgid(0);}) +HAVE_SETGROUPS ?u1 $(include grp.h compile int main(){setgroups(0, 0);}) +HAVE_SETHOSTNAME ?u1 $(include unistd.h compile int main(){sethostname(0, 0);}) +HAVE_SETITIMER ?u1 $(include sys/time.h compile int main(){struct itimerval it; setitimer(0, &it, 0);}) +HAVE_SETLOCALE ?u1 $(include locale.h compile int main(){setlocale(0, 0);}) +HAVE_SETPGID ?u1 $(include unistd.h compile int main(){setpgid(0, 0);}) +HAVE_SETPGRP ?u1 $(include unistd.h compile int main(){setpgrp();}) +HAVE_SETPRIORITY ?u1 $(include sys/resource.h compile int main(){setpriority(0, 0, 0);}) +HAVE_SETREGID ?u1 $(include unistd.h compile int main(){setregid(0, 0);}) +HAVE_SETRESGID ?u1 $(include unistd.h compile int main(){setresgid(0, 0, 0);}) +HAVE_SETRESUID ?u1 $(define _GNU_SOURCE include unistd.h compile int main(){setresuid(0, 0, 0);}) +HAVE_SETREUID ?u1 $(include unistd.h compile int main(){setreuid(0, 0);}) +HAVE_SETSID ?u1 $(include unistd.h compile int main(){setsid();}) +HAVE_SETSOCKOPT ?u1 $(include sys/socket.h compile int main(){setsockopt(0, 0, 0, 0, 0);}) +HAVE_SETUID ?u1 $(include unistd.h compile int main(){setuid(0);}) +HAVE_SETVBUF ?u1 $(include stdio.h compile int main(){setvbuf(0, 0, 0, 0);}) +HAVE_SHM_OPEN ?u1 $(include sys/mman.h compile int main(){shm_open(0, 0, 0);}) +HAVE_SHM_UNLINK ?u1 $(include sys/mman.h compile int main(){shm_unlink(0);}) +HAVE_SHUTDOWN ?u1 $(include sys/socket.h compile int main(){shutdown(0, 0);}) +HAVE_SIGACTION ?u1 $(include signal.h compile int main(){struct sigaction sa; sigaction(0, &sa, 0);}) +HAVE_SIGALTSTACK ?u1 $(include signal.h compile int main(){sigaltstack(0, 0);}) +HAVE_SIGFILLSET ?u1 $(include signal.h compile int main(){sigset_t set; sigfillset(&set);}) +HAVE_SIGINTERRUPT ?u1 $(include signal.h compile int main(){siginterrupt(0, 0);}) +HAVE_SIGPENDING ?u1 $(include signal.h compile int main(){sigset_t set; sigpending(&set);}) +HAVE_SIGRELSE ?u1 $(include signal.h compile int main(){sigrelse(0);}) +HAVE_SIGTIMEDWAIT ?u1 $(include signal.h compile int main(){sigset_t set; sigtimedwait(&set, 0, 0);}) +HAVE_SIGWAIT ?u1 $(include signal.h compile int main(){sigset_t set; sigwait(&set, 0);}) +HAVE_SIGWAITINFO ?u1 $(include signal.h compile int main(){sigset_t set; sigwaitinfo(&set, 0);}) +HAVE_SNPRINTF ?u1 $(include stdio.h compile int main(){snprintf(0, 0, 0);}) +HAVE_SOCKET ?u1 $(include sys/socket.h compile int main(){socket(0, 0, 0);}) +HAVE_SOCKETPAIR ?u1 $(include sys/socket.h compile int main(){int sv[2]; socketpair(0, 0, 0, sv);}) +HAVE_SPLICE ?u1 $(define _GNU_SOURCE include fcntl.h compile int main(){splice(0, 0, 0, 0, 0, 0);}) +HAVE_STATVFS ?u1 $(include sys/statvfs.h compile int main(){struct statvfs st; statvfs(0, &st);}) +HAVE_STRFTIME ?u1 $(include time.h compile int main(){struct tm tm; strftime(0, 0, 0, &tm);}) +HAVE_STRSIGNAL ?u1 $(include string.h compile int main(){strsignal(0);}) +HAVE_SYMLINK ?u1 $(include unistd.h compile int main(){symlink(0, 0);}) +HAVE_SYMLINKAT ?u1 $(include unistd.h compile int main(){symlinkat(0, 0, 0);}) +HAVE_SYNC ?u1 $(include unistd.h compile int main(){sync();}) +HAVE_SYSCONF ?u1 $(include unistd.h compile int main(){sysconf(0);}) +HAVE_SYSTEM ?u1 $(include stdlib.h compile int main(){system(0);}) +HAVE_TCGETPGRP ?u1 $(include unistd.h compile int main(){tcgetpgrp(0);}) +HAVE_TCSETPGRP ?u1 $(include unistd.h compile int main(){tcsetpgrp(0, 0);}) +HAVE_TEMPNAM ?u1 $(include stdio.h compile int main(){tempnam(0, 0);}) +HAVE_TIMEGM ?u1 $(include time.h compile int main(){struct tm tm; timegm(&tm);}) +HAVE_TIMES ?u1 $(include sys/times.h compile int main(){struct tms tms; times(&tms);}) +HAVE_TMPFILE ?u1 $(include stdio.h compile int main(){tmpfile();}) +HAVE_TMPNAM ?u1 $(include stdio.h compile int main(){tmpnam(0);}) +HAVE_TMPNAM_R ?u1 $(include stdio.h compile int main(){tmpnam_r(0);}) +HAVE_TRUNCATE ?u1 $(include unistd.h compile int main(){truncate(0, 0);}) +HAVE_UMASK ?u1 $(include sys/stat.h compile int main(){umask(0);}) +HAVE_UNAME ?u1 $(include sys/utsname.h compile int main(){struct utsname uts; uname(&uts);}) +HAVE_UNLINKAT ?u1 $(include unistd.h compile int main(){unlinkat(0, 0, 0);}) +HAVE_UTIMENSAT ?u1 $(include sys/stat.h compile int main(){utimensat(0, 0, 0, 0);}) +HAVE_UTIMES ?u1 $(include sys/time.h compile int main(){utimes(0, 0);}) +HAVE_VFORK ?u1 $(include unistd.h compile int main(){vfork();}) +HAVE_WAIT ?u1 $(include sys/wait.h compile int main(){wait(0);}) +HAVE_WAIT3 ?u1 $(include sys/wait.h compile int main(){wait3(0, 0, 0);}) +HAVE_WAIT4 ?u1 $(include sys/wait.h compile int main(){wait4(0, 0, 0, 0);}) +HAVE_WAITID ?u1 $(include sys/wait.h compile int main(){waitid(0, 0, 0, 0);}) +HAVE_WAITPID ?u1 $(include sys/wait.h compile int main(){waitpid(0, 0, 0);}) +HAVE_WCSCOLL ?u1 $(include wchar.h compile int main(){wcscoll(0, 0);}) +HAVE_WCSFTIME ?u1 $(include wchar.h compile int main(){wcsftime(0, 0, 0, 0);}) +HAVE_WCSXFRM ?u1 $(include wchar.h compile int main(){wcsxfrm(0, 0, 0);}) +HAVE_WMEMCMP ?u1 $(include wchar.h compile int main(){wmemcmp(0, 0, 0);}) +HAVE_WRITEV ?u1 $(include sys/uio.h compile int main(){writev(0, 0, 0);}) +HAVE_UUID_CREATE ?u1 $(include uuid.h compile int main(){uuid_create(0,0);}) + +# BSD/macOS specific functions +HAVE_CHFLAGS ?u1 $(include sys/stat.h compile int main(){chflags(0, 0);}) +HAVE_LCHFLAGS ?u1 $(include sys/stat.h compile int main(){lchflags(0, 0);}) +HAVE_LCHMOD ?u1 $(include sys/stat.h compile int main(){lchmod(0, 0);}) +HAVE_KQUEUE ?u1 $(include sys/event.h compile int main(){kqueue();}) +HAVE_FDWALK ?u1 $(include fcntl.h compile int main(){fdwalk(0, 0);}) +HAVE_FORK1 ?u1 $(include sys/types.h,sys/proc.h compile int main(){fork1();}) +HAVE_GETPGRP ?u1 $(include unistd.h compile int main(){getpgrp();}) +HAVE_PLOCK ?u1 $(include sys/lock.h compile int main(){plock(0);}) +HAVE_RTPSPAWN ?u1 $(include spawn.h compile int main(){rtpspawn(0, 0, 0, 0, 0, 0);}) +HAVE_STRLCPY ?u1 $(include string.h compile int main(){strlcpy(0, 0, 0);}) +HAVE__GETPTY ?u1 $(include stdlib.h compile int main(){_getpty(0, 0, 0, 0);}) + +# Additional math functions +HAVE_FSEEK64 ?u1 $(include stdio.h compile int main(){fseek64(0, 0, 0);}) +HAVE_FTELL64 ?u1 $(include stdio.h compile int main(){ftell64(0);}) + +# Library functions +HAVE_CRYPT_R ?u1 $(include crypt.h compile int main(){struct crypt_data cd; crypt_r(0, 0, &cd);}) +HAVE_CTERMID_R ?u1 $(include stdio.h compile int main(){ctermid_r((char*)0);}) +HAVE_EXPLICIT_MEMSET ?u1 $(include string.h compile int main(){explicit_memset(0, 0, 0);}) + +# UUID functions +HAVE_UUID_ENC_BE ?u1 $(include uuid.h compile int main(){uuid_enc_be(0, 0);}) +HAVE_UUID_GENERATE_TIME_SAFE ?u1 $(include uuid.h compile int main(){uuid_generate_time_safe(0);}) + +# Various other functions +HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH ?u1 $(include mach-o/dyld.h compile int main(){_dyld_shared_cache_contains_path(0);}) + +# Curses functions +HAVE_CURSES_FILTER ?u1 $(include curses.h compile int main(){filter();}) +HAVE_CURSES_HAS_KEY ?u1 $(include curses.h compile int main(){has_key(0);}) +HAVE_CURSES_IMMEDOK ?u1 $(include curses.h compile int main(){immedok(0, 0);}) +HAVE_CURSES_IS_PAD ?u1 $(include curses.h compile int main(){is_pad(0);}) +HAVE_CURSES_IS_TERM_RESIZED ?u1 $(include curses.h compile int main(){is_term_resized(0, 0);}) +HAVE_CURSES_RESIZETERM ?u1 $(include curses.h compile int main(){resizeterm(0, 0);}) +HAVE_CURSES_RESIZE_TERM ?u1 $(include curses.h compile int main(){resize_term(0, 0);}) +HAVE_CURSES_SYNCOK ?u1 $(include curses.h compile int main(){syncok(0, 0);}) +HAVE_CURSES_TYPEAHEAD ?u1 $(include curses.h compile int main(){typeahead(0);}) +HAVE_CURSES_USE_ENV ?u1 $(include curses.h compile int main(){use_env(0);}) +HAVE_CURSES_WCHGAT ?u1 $(include curses.h compile int main(){wchgat(0, 0, 0, 0, 0);}) +# Readline functions +HAVE_RL_APPEND_HISTORY ?u1 $(include readline/history.h compile int main(){append_history(0, 0);}) +HAVE_RL_CATCH_SIGNAL ?u1 $(include readline/readline.h compile int main(){rl_catch_signals = 0;}) +HAVE_RL_COMPLETION_MATCHES ?u1 $(include readline/readline.h compile int main(){rl_completion_matches(0, 0);}) +HAVE_RL_RESIZE_TERMINAL ?u1 $(include readline/readline.h compile int main(){rl_resize_terminal();}) + +# Pthread functions +HAVE_PTHREAD_CONDATTR_SETCLOCK ?u1 $(include pthread.h compile int main(){pthread_condattr_t attr; pthread_condattr_setclock(&attr, 0);}) +HAVE_PTHREAD_GETCPUCLOCKID ?u1 $(include pthread.h compile int main(){clockid_t clk; pthread_getcpuclockid(0, &clk);}) + +# Header includes and type checks +HAVE_ADDRINFO ?u1 $(include netdb.h compile int main(){struct addrinfo ai; return 0;}) +HAVE_ALIGNED_REQUIRED ?u1 $(include stddef.h compile int main(){char a[1]; return ((size_t)a) & 1;}) +HAVE_ALTZONE ?u1 $(include time.h compile int main(){return altzone;}) + +# Broken function tests (these typically compile but have runtime issues) +# HAVE_BROKEN_MBSTOWCS ?u1 $(include stdlib.h,wchar.h compile int main(){return mbstowcs(NULL, "test", 0) == 0;}) +# HAVE_BROKEN_NICE ?u1 $(include unistd.h compile int main(){nice(1); return 0;}) +# HAVE_BROKEN_PIPE_BUF ?u1 $(include limits.h compile int main(){return PIPE_BUF;}) +# HAVE_BROKEN_POLL ?u1 $(include poll.h compile int main(){struct pollfd pfd; poll(&pfd, 1, 0); return 0;}) +# HAVE_BROKEN_POSIX_SEMAPHORES ?u1 $(include semaphore.h compile int main(){sem_t sem; return 0;}) +# HAVE_BROKEN_PTHREAD_SIGMASK ?u1 $(include pthread.h,signal.h compile int main(){sigset_t set; pthread_sigmask(SIG_BLOCK, &set, NULL); return 0;}) +# HAVE_BROKEN_SEM_GETVALUE ?u1 $(include semaphore.h compile int main(){sem_t sem; int val; sem_getvalue(&sem, &val); return 0;}) +# HAVE_BROKEN_UNSETENV ?u1 $(include stdlib.h compile int main(){unsetenv("TEST"); return 0;}) + +# Compiler builtin features +HAVE_BUILTIN_ATOMIC ?u1 $(compile int main(){int x = 0; __atomic_store_n(&x, 1, __ATOMIC_SEQ_CST); return __atomic_load_n(&x, __ATOMIC_SEQ_CST);}) +HAVE_COMPUTED_GOTOS ?u1 $(compile int main(){void *ptr = &&label; goto *ptr; label: return 0;}) + +# RTLD declarations +HAVE_DECL_RTLD_DEEPBIND ?u1 $(include dlfcn.h compile int main(){return RTLD_DEEPBIND;}) +HAVE_DECL_RTLD_GLOBAL ?u1 $(include dlfcn.h compile int main(){return RTLD_GLOBAL;}) +HAVE_DECL_RTLD_LAZY ?u1 $(include dlfcn.h compile int main(){return RTLD_LAZY;}) +HAVE_DECL_RTLD_LOCAL ?u1 $(include dlfcn.h compile int main(){return RTLD_LOCAL;}) +HAVE_DECL_RTLD_MEMBER ?u1 $(include dlfcn.h compile int main(){return RTLD_MEMBER;}) +HAVE_DECL_RTLD_NODELETE ?u1 $(include dlfcn.h compile int main(){return RTLD_NODELETE;}) +HAVE_DECL_RTLD_NOLOAD ?u1 $(include dlfcn.h compile int main(){return RTLD_NOLOAD;}) +HAVE_DECL_RTLD_NOW ?u1 $(include dlfcn.h compile int main(){return RTLD_NOW;}) +HAVE_DECL_TZNAME ?u1 $(include time.h compile int main(){return tzname[0] != NULL;}) + +# Device and file system features +HAVE_DEVICE_MACROS ?u1 $(include sys/types.h,sys/stat.h compile int main(){dev_t d = 0; return major(d) | minor(d);}) +HAVE_DEV_PTC ?u1 $(include fcntl.h compile int main(){return open("/dev/ptc", O_RDWR);}) +HAVE_DEV_PTMX ?u1 $(include fcntl.h compile int main(){return open("/dev/ptmx", O_RDWR);}) +HAVE_DIRENT_D_TYPE ?u1 $(include dirent.h compile int main(){struct dirent d; return d.d_type;}) +HAVE_DIRFD ?u1 $(include dirent.h compile int main(){DIR *d = NULL; return dirfd(d);}) + +# System features +HAVE_DYNAMIC_LOADING ?u1 $(include dlfcn.h compile int main(){dlopen("test", RTLD_NOW); return 0;}) +HAVE_EPOLL ?u1 $(include sys/epoll.h compile int main(){return epoll_create(1);}) + +# GCC assembly features +HAVE_GCC_UINT128_T ?u1 $(compile int main(){__uint128_t x = 0; return (int)x;}) + +# gethostbyname_r variants +HAVE_GETHOSTBYNAME_R_3_ARG ?u1 $(include netdb.h compile int main(){struct hostent_data hed; gethostbyname_r("localhost", NULL, &hed); return 0;}) +HAVE_GETHOSTBYNAME_R_5_ARG ?u1 $(include netdb.h compile int main(){struct hostent he; char buf[1024]; gethostbyname_r("localhost", &he, buf, sizeof(buf), NULL); return 0;}) +HAVE_GETHOSTBYNAME_R_6_ARG ?u1 $(include netdb.h compile 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 ?u1 $(include sys/syscall.h,unistd.h compile int main(){return syscall(SYS_getrandom, NULL, 0, 0);}) + +# Bug detection +HAVE_GLIBC_MEMMOVE_BUG ?u1 $(include string.h compile int main(){char buf[10]; memmove(buf+1, buf, 5); return 0;}) +HAVE_IPA_PURE_CONST_BUG ?u1 $(compile int main(){return 0;}) + +# File and library support +HAVE_LARGEFILE_SUPPORT ?u1 $(include sys/types.h compile int main(){off_t offset = 0; return sizeof(offset) > 4;}) +HAVE_LIBB2 ?u1 $(include blake2.h compile int main(){blake2b_state state; blake2s_final(&state, 0, 0); return 0;}) +HAVE_LIBDB ?u1 $(include db.h compile int main(){DB *db; return 0;}) +HAVE_LIBDL ?u1 $(include dlfcn.h compile int main(){dlopen("test", RTLD_NOW); return 0;}) +HAVE_LIBDLD ?u1 $(compile int main(){return 0;}) +HAVE_LIBIEEE ?u1 $(compile int main(){return 0;}) +HAVE_LIBRESOLV ?u1 $(include resolv.h compile int main(){res_init(); return 0;}) +HAVE_LIBSENDFILE ?u1 $(include sys/sendfile.h compile int main(){sendfile(0, 0, NULL, 0); return 0;}) +HAVE_LIBSQLITE3 ?u1 $(include sqlite3.h compile int main(){sqlite3 *db; return 0;}) + +# Linux-specific headers +HAVE_LINUX_AUXVEC_H ?u1 $(include linux/auxvec.h compile int main(){return AT_NULL;}) +HAVE_LINUX_CAN_BCM_H ?u1 $(include linux/can/bcm.h compile int main(){struct bcm_msg_head msg; return 0;}) +HAVE_LINUX_CAN_H ?u1 $(include linux/can.h compile int main(){struct can_frame frame; return 0;}) +HAVE_LINUX_CAN_J1939_H ?u1 $(include linux/can/j1939.h compile int main(){return J1939_MAX_UNICAST_ADDR;}) +HAVE_LINUX_CAN_RAW_FD_FRAMES ?u1 $(include linux/can/raw.h compile int main(){return CAN_RAW_FD_FRAMES;}) +HAVE_LINUX_CAN_RAW_H ?u1 $(include linux/can/raw.h compile int main(){return CAN_RAW_FILTER;}) +HAVE_LINUX_CAN_RAW_JOIN_FILTERS ?u1 $(include linux/can/raw.h compile int main(){return CAN_RAW_JOIN_FILTERS;}) +HAVE_LINUX_MEMFD_H ?u1 $(include linux/memfd.h compile int main(){return MFD_CLOEXEC;}) +HAVE_LINUX_NETLINK_H ?u1 $(include linux/netlink.h compile int main(){struct nlmsghdr nlh; return 0;}) +HAVE_LINUX_QRTR_H ?u1 $(include linux/qrtr.h compile int main(){struct sockaddr_qrtr sq; return 0;}) +HAVE_LINUX_RANDOM_H ?u1 $(include linux/random.h compile int main(){return GRND_NONBLOCK;}) +HAVE_LINUX_SOUNDCARD_H ?u1 $(include linux/soundcard.h compile int main(){return SOUND_VERSION;}) +HAVE_LINUX_TIPC_H ?u1 $(include linux/tipc.h compile int main(){struct sockaddr_tipc addr; return 0;}) +HAVE_LINUX_VM_SOCKETS_H ?u1 $(include linux/vm_sockets.h compile int main(){return VMADDR_CID_ANY;}) +HAVE_LINUX_WAIT_H ?u1 $(include linux/wait.h compile int main(){return 0;}) + +# Type support +HAVE_LONG_DOUBLE ?u1 $(compile int main(){long double x = 0.0L; return (int)x;}) +# HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION ?u1 $(include wchar.h compile int main(){wchar_t w = L'\x80'; return (int)w;}) +HAVE_PROTOTYPES ?u1 $(compile int test(int x); int main(){return test(0);} int test(int x){return x;}) + +# pthread features +HAVE_PTHREAD_DESTRUCTOR ?u1 $(compile #include \nvoid destructor(void *); int main(){pthread_key_create(NULL, destructor); return 0;} void destructor(void *p){}) +HAVE_PTHREAD_INIT ?u1 $(include pthread.h compile int main(){pthread_init(); return 0;}) + +# Readline features +HAVE_RL_COMPLETION_APPEND_CHARACTER ?u1 $(include readline/readline.h compile int main(){rl_completion_append_character = ' '; return 0;}) +HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK ?u1 $(include readline/readline.h compile int main(){rl_completion_display_matches_hook = NULL; return 0;}) +HAVE_RL_COMPLETION_SUPPRESS_APPEND ?u1 $(include readline/readline.h compile int main(){rl_completion_suppress_append = 1; return 0;}) +HAVE_RL_PRE_INPUT_HOOK ?u1 $(include readline/readline.h compile int main(){rl_pre_input_hook = NULL; return 0;}) + +# Signal and socket features +HAVE_SIGINFO_T_SI_BAND ?u1 $(include signal.h compile int main(){siginfo_t si; return si.si_band;}) +HAVE_SOCKADDR_ALG ?u1 $(include linux/if_alg.h compile int main(){struct sockaddr_alg sa; return 0;}) +HAVE_SOCKADDR_SA_LEN ?u1 $(include sys/socket.h compile int main(){struct sockaddr sa; return sa.sa_len;}) +HAVE_SOCKADDR_STORAGE ?u1 $(include sys/socket.h compile int main(){struct sockaddr_storage ss; return 0;}) + +# Type definitions +HAVE_SSIZE_T ?u1 $(include sys/types.h compile int main(){ssize_t s = 0; return (int)s;}) +HAVE_STAT_TV_NSEC ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_mtim.tv_nsec;}) +HAVE_STAT_TV_NSEC2 ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_mtimensec;}) +HAVE_STD_ATOMIC ?u1 $(include stdatomic.h compile int main(){atomic_int x; atomic_uintptr_t y; return 0;}) + +# Struct member checks +HAVE_STRUCT_PASSWD_PW_GECOS ?u1 $(include pwd.h compile int main(){struct passwd pw; return pw.pw_gecos != NULL;}) +HAVE_STRUCT_PASSWD_PW_PASSWD ?u1 $(include pwd.h compile int main(){struct passwd pw; return pw.pw_passwd != NULL;}) +HAVE_STRUCT_STAT_ST_BIRTHTIME ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_birthtime;}) +HAVE_STRUCT_STAT_ST_BLKSIZE ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_blksize;}) +HAVE_STRUCT_STAT_ST_BLOCKS ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_blocks;}) +HAVE_STRUCT_STAT_ST_FLAGS ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_flags;}) +HAVE_STRUCT_STAT_ST_GEN ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_gen;}) +HAVE_STRUCT_STAT_ST_RDEV ?u1 $(include sys/stat.h compile int main(){struct stat st; return st.st_rdev;}) +HAVE_STRUCT_TM_TM_ZONE ?u1 $(include time.h compile int main(){struct tm t; return t.tm_zone != NULL;}) + +# Time and timezone features +HAVE_TM_ZONE ?u1 $(include time.h compile int main(){struct tm t; return t.tm_zone != NULL;}) +HAVE_TZNAME ?u1 $(include time.h compile int main(){return tzname[0] != NULL;}) +# HAVE_USABLE_WCHAR_T ?u1 $(include wchar.h compile int main(){wchar_t w = L'A'; return sizeof(w) >= 2;}) +HAVE_WORKING_TZSET ?u1 $(include time.h compile int main(){tzset(); return 0;}) +HAVE_ZLIB_COPY ?u1 $(include zlib.h compile int main(){z_stream strm; inflateCopy(&strm, &strm); return 0;}) diff --git a/configquery.zig b/configquery.zig new file mode 100644 index 0000000..c028600 --- /dev/null +++ b/configquery.zig @@ -0,0 +1,462 @@ +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 zig_exe: ?[]const u8 = null; + var target_triple: ?[]const u8 = null; + var cache_dir: ?[]const u8 = null; + var include_dirs: std.ArrayList([]const u8) = .empty; + + { + var args = try init.minimal.args.iterateAllocator(arena); + _ = args.next(); + while (args.next()) |arg| { + if (std.mem.startsWith(u8, arg, "-I")) { + include_dirs.append(arena, arg[2..]) catch |e| oom(e); + } else 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")) { + zig_exe = args.next() orelse fatal("--zig-exe requires an argument", .{}); + } else if (std.mem.eql(u8, arg, "--target")) { + target_triple = args.next() orelse fatal("--target requires an argument", .{}); + } else if (std.mem.eql(u8, arg, "--cache-dir")) { + 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] ... + \\ + \\Query system configuration for use with zig's ConfigHeader. + \\ + \\Options: + \\ -o Write output to file (default: stdout) + \\ -I Add directory to include search path + \\ --zig-exe Path to zig executable (for compile queries) + \\ --target Target triple (for compile queries) + \\ + \\Template format (one entry per line): + \\ NAME TYPE [VALUE] + \\ + \\TYPE is one of ?u1, bool, int, ident, string, undef, defined. + \\ + \\A VALUE can be a literal value (i.e. foo, false, 0) or, for types + \\?u1, bool and defined, it can be a query expression of the form: + \\ + \\ $([define DEFINE1;DEFINE2...] [include HEADER1;HEADER2...] [compile BODY]) + \\ + \\Examples: + \\ SIZEOF_INT int 4 + \\ RETSIGTYPE ident void + \\ HAVE_ALLOCA_H ?u1 $(include alloca.h) + \\ HAVE_FORK ?u1 $(include unistd.h compile int main(){fork();}) + \\ HAVE_PRLIMIT ?u1 $(include sys/time.h;sys/resource.h compile int main(){prlimit(0,0,0,0);}) + \\ HAVE_CLOSE_RANGE ?u1 $(define _GNU_SOURCE include unistd.h compile int main(){close_range(0,0,0);}) + \\ + \\Output format: NAME TYPE VALUE (queries resolved to literals) + \\ + ; + var stderr = std.Io.File.stderr().writer(io, &.{}); + stderr.interface.writeAll(usage) catch {}; + 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); + const out = &out_writer.interface; + + 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); + 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 TYPE VALUE' but missing TYPE", .{}); + error_count += 1; + continue; + }; + const name = line[0..first_space]; + const after_name = std.mem.trimStart(u8, line[first_space + 1 ..], " "); + + const second_space = std.mem.indexOfScalar(u8, after_name, ' '); + const type_str = if (second_space) |s| after_name[0..s] else after_name; + + { + const valid_types = [_][]const u8{ "?u1", "bool", "int", "ident", "string", "undef", "defined" }; + const is_valid_type = for (valid_types) |t| { + if (std.mem.eql(u8, type_str, t)) break true; + } else false; + if (!is_valid_type) { + reportError(io, template_path, line_num, "unknown type '{s}', expected one of: ?u1, bool, int, ident, string, undef, defined", .{type_str}); + error_count += 1; + continue; + } + } + out.print("{s} {s}", .{ name, type_str }) catch return out_writer.err.?; + + const value_str = if (second_space) |s| after_name[s + 1 ..] else ""; + const resolved = if (std.mem.startsWith(u8, value_str, "$(")) blk: { + if (!std.mem.endsWith(u8, value_str, ")")) { + reportError(io, template_path, line_num, "unclosed $()", .{}); + error_count += 1; + continue; + } + break :blk evalQuery( + arena, + io, + &include_dirs, + zig_exe, + target_triple, + cache_dir, + value_str[2 .. value_str.len - 1], + template_path, + line_num, + &error_count, + ); + } else value_str; + const prefix: []const u8 = if (resolved.len == 0) "" else " "; + out.print("{s}{s}\n", .{ prefix, resolved }) catch return out_writer.err.?; + } + } + + out.flush() catch return out_writer.err.?; + if (error_count > 0) fatal("{} errors", .{error_count}); +} + +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) Error!?[]const u8 { + std.debug.assert(self.state == .compile); + self.state = .done; + const token, const new_pos = lex(self.expr, self.pos) orelse return null; + if (!std.mem.eql(u8, token, "compile")) return error.Unexpected; + self.pos = new_pos; + const body = self.expr[self.pos..]; + self.pos = self.expr.len; + return body; + } + + fn nextCommon(self: *ExprIterator, name: []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.eql(u8, token, name)) return null; + const values, const end = lex(self.expr, new_pos) orelse return null; + common.* = .{ .values = std.mem.splitScalar(u8, values, ',') }; + self.pos = end; + }, + .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 }; + } +}; + +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 { + { + 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(try it.compile() == null); + } + + { + 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(try it.compile() == null); + } + + { + var it = ExprIterator{ + .expr = "define _GNU_SOURCE include unistd.h compile 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);}", (try it.compile()).?); + } + + { + var it = ExprIterator{ .expr = "compile 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;}", (try it.compile()).?); + } + + { + var it = ExprIterator{ .expr = "garbage" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expectError(error.Unexpected, it.compile()); + try std.testing.expect(it.pos == 0); + } + + { + var it = ExprIterator{ .expr = "include foo.h define BAR" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expectEqualStrings("foo.h", (try it.nextInclude()).?); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expectError(error.Unexpected, it.compile()); + } + + // repeated keywords should error (use commas instead) + { + var it = ExprIterator{ .expr = "include foo.h include bar.h" }; + try std.testing.expect(try it.nextDefine() == null); + try std.testing.expectEqualStrings("foo.h", (try it.nextInclude()).?); + try std.testing.expect(try it.nextInclude() == null); + try std.testing.expectError(error.Unexpected, it.compile()); + } +} + +fn evalQuery( + arena: std.mem.Allocator, + io: std.Io, + include_dirs: *const std.ArrayList([]const u8), + zig_exe: ?[]const u8, + target_triple: ?[]const u8, + cache_dir: ?[]const u8, + expr: []const u8, + file_path: []const u8, + line_num: u32, + errors: *u32, +) []const u8 { + var it = ExprIterator{ .expr = expr }; + + while (it.nextDefine() catch |err| return exprErr(io, file_path, line_num, errors, &it, err)) |_| {} + while (it.nextInclude() catch |err| return exprErr(io, file_path, line_num, errors, &it, err)) |header| { + if (!headerExists(io, include_dirs.items, header)) + return " undef"; + } + const has_compile = it.compile() catch |err| + return exprErr(io, file_path, line_num, errors, &it, err); + if (has_compile == null) return " 1"; + + // Full compile check + const found = doCompile( + arena, + io, + zig_exe orelse fatal("compile query requires --zig-exe", .{}), + target_triple orelse fatal("compile query requires --target", .{}), + cache_dir orelse fatal("compile query requires --cache-dir", .{}), + expr, + file_path, + line_num, + errors, + ); + return if (found) " 1" else " undef"; +} + +// 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}); + } + try w.writeAll((it.compile() catch unreachable).?); + try w.flush(); +} + +fn exprErr(io: std.Io, file_path: []const u8, line_num: u32, errors: *u32, it: *const ExprIterator, err: ExprIterator.Error) []const u8 { + reportError(io, file_path, line_num, "bad expression: {f}", .{it.fmtError(err)}); + errors.* += 1; + return " undef"; +} + +fn headerExists(io: std.Io, include_dirs: []const []const u8, header: []const u8) bool { + for (include_dirs) |dir| { + var buf: [std.fs.max_path_bytes]u8 = undefined; + const path = std.fmt.bufPrint(&buf, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ dir, header }) catch continue; + std.Io.Dir.cwd().access(io, path, .{}) catch continue; + return true; + } + return false; +} + +fn doCompile( + arena: std.mem.Allocator, + io: std.Io, + zig_exe: []const u8, + target_triple: []const u8, + cache_dir: []const u8, + expr: []const u8, + file_path: []const u8, + line_num: u32, + errors: *u32, +) bool { + const sep = std.fs.path.sep_str; + const pid = std.os.linux.getpid(); + 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; + }, + } + + // 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) { + // No expected errors at all, this is likely a bad template, not a missing feature + delete_source = false; + reportError(io, file_path, line_num, "compile check failed with unexpected error(s):\n{s}", .{result.stderr}); + 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");