| author | |
| committer | |
| log | 3b2b9fcbc5e162063febf989883f29e55cc64c65 |
| tree | c85bbce50c24a18df10e08889b1cb3cc3f69e335 |
| parent | c429bb5d2feb36f83e33ef95a25dd5bb7455f16c |
`std.zig.system.darwin.getSdk` now pulls only the SDK path
so we execute a child process only once and not twice as it was
until now since we parse the SDK version directly from the pulled path.
This is actually how `ld64` does it too.6 files changed, 88 insertions(+), 102 deletions(-)
lib/std/zig/system/NativePaths.zig+3-3| ... | ... | @@ -81,9 +81,9 @@ pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths { |
| 81 | 81 | if (comptime builtin.target.isDarwin()) { |
| 82 | 82 | if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: { |
| 83 | 83 | const sdk = std.zig.system.darwin.getSdk(arena, native_target) orelse break :sdk; |
| 84 | try self.addLibDir(try std.fs.path.join(arena, &.{ sdk.path, "usr/lib" })); | |
| 85 | try self.addFrameworkDir(try std.fs.path.join(arena, &.{ sdk.path, "System/Library/Frameworks" })); | |
| 86 | try self.addIncludeDir(try std.fs.path.join(arena, &.{ sdk.path, "usr/include" })); | |
| 84 | try self.addLibDir(try std.fs.path.join(arena, &.{ sdk, "usr/lib" })); | |
| 85 | try self.addFrameworkDir(try std.fs.path.join(arena, &.{ sdk, "System/Library/Frameworks" })); | |
| 86 | try self.addIncludeDir(try std.fs.path.join(arena, &.{ sdk, "usr/include" })); | |
| 87 | 87 | return self; |
| 88 | 88 | } |
| 89 | 89 | return self; |
lib/std/zig/system/darwin.zig+14-87| ... | ... | @@ -30,12 +30,11 @@ pub fn isSdkInstalled(allocator: Allocator) bool { |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | /// Detect SDK on Darwin. |
| 33 | /// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK sysroot (if any). | |
| 34 | /// Subsequently calls `xcrun --sdk <target_sdk> --show-sdk-version` which fetches version of the SDK. | |
| 35 | /// The caller needs to deinit the resulting struct. | |
| 33 | /// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK. | |
| 34 | /// Caller owns the memory. | |
| 36 | 35 | /// stderr from xcrun is ignored. |
| 37 | 36 | /// If error.OutOfMemory occurs in Allocator, this function returns null. |
| 38 | pub fn getSdk(allocator: Allocator, target: Target) ?Sdk { | |
| 37 | pub fn getSdk(allocator: Allocator, target: Target) ?[]const u8 { | |
| 39 | 38 | const is_simulator_abi = target.abi == .simulator; |
| 40 | 39 | const sdk = switch (target.os.tag) { |
| 41 | 40 | .macos => "macosx", |
| ... | ... | @@ -44,91 +43,19 @@ pub fn getSdk(allocator: Allocator, target: Target) ?Sdk { |
| 44 | 43 | .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", |
| 45 | 44 | else => return null, |
| 46 | 45 | }; |
| 47 | const path = path: { | |
| 48 | const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-path" }; | |
| 49 | const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null; | |
| 50 | defer { | |
| 51 | allocator.free(result.stderr); | |
| 52 | allocator.free(result.stdout); | |
| 53 | } | |
| 54 | switch (result.term) { | |
| 55 | .Exited => |code| if (code != 0) return null, | |
| 56 | else => return null, | |
| 57 | } | |
| 58 | const path = allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")) catch return null; | |
| 59 | break :path path; | |
| 60 | }; | |
| 61 | const version = version: { | |
| 62 | const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-version" }; | |
| 63 | const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null; | |
| 64 | defer { | |
| 65 | allocator.free(result.stderr); | |
| 66 | allocator.free(result.stdout); | |
| 67 | } | |
| 68 | switch (result.term) { | |
| 69 | .Exited => |code| if (code != 0) return null, | |
| 70 | else => return null, | |
| 71 | } | |
| 72 | const raw_version = mem.trimRight(u8, result.stdout, "\r\n"); | |
| 73 | const version = parseSdkVersion(raw_version) orelse Version{ | |
| 74 | .major = 0, | |
| 75 | .minor = 0, | |
| 76 | .patch = 0, | |
| 77 | }; | |
| 78 | break :version version; | |
| 79 | }; | |
| 80 | return Sdk{ | |
| 81 | .path = path, | |
| 82 | .version = version, | |
| 83 | }; | |
| 84 | } | |
| 85 | ||
| 86 | // Versions reported by Apple aren't exactly semantically valid as they usually omit | |
| 87 | // the patch component. Hence, we do a simple check for the number of components and | |
| 88 | // add the missing patch value if needed. | |
| 89 | fn parseSdkVersion(raw: []const u8) ?Version { | |
| 90 | var buffer: [128]u8 = undefined; | |
| 91 | if (raw.len > buffer.len) return null; | |
| 92 | @memcpy(buffer[0..raw.len], raw); | |
| 93 | const dots_count = mem.count(u8, raw, "."); | |
| 94 | if (dots_count < 1) return null; | |
| 95 | const len = if (dots_count < 2) blk: { | |
| 96 | const patch_suffix = ".0"; | |
| 97 | buffer[raw.len..][0..patch_suffix.len].* = patch_suffix.*; | |
| 98 | break :blk raw.len + patch_suffix.len; | |
| 99 | } else raw.len; | |
| 100 | return Version.parse(buffer[0..len]) catch null; | |
| 101 | } | |
| 102 | ||
| 103 | pub const Sdk = struct { | |
| 104 | path: []const u8, | |
| 105 | version: Version, | |
| 106 | ||
| 107 | pub fn deinit(self: Sdk, allocator: Allocator) void { | |
| 108 | allocator.free(self.path); | |
| 46 | const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-path" }; | |
| 47 | const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null; | |
| 48 | defer { | |
| 49 | allocator.free(result.stderr); | |
| 50 | allocator.free(result.stdout); | |
| 51 | } | |
| 52 | switch (result.term) { | |
| 53 | .Exited => |code| if (code != 0) return null, | |
| 54 | else => return null, | |
| 109 | 55 | } |
| 110 | }; | |
| 56 | return allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")) catch null; | |
| 57 | } | |
| 111 | 58 | |
| 112 | 59 | test { |
| 113 | 60 | _ = macos; |
| 114 | 61 | } |
| 115 | ||
| 116 | const expect = std.testing.expect; | |
| 117 | const expectEqual = std.testing.expectEqual; | |
| 118 | ||
| 119 | fn testParseSdkVersionSuccess(exp: Version, raw: []const u8) !void { | |
| 120 | const maybe_ver = parseSdkVersion(raw); | |
| 121 | try expect(maybe_ver != null); | |
| 122 | const ver = maybe_ver.?; | |
| 123 | try expectEqual(exp.major, ver.major); | |
| 124 | try expectEqual(exp.minor, ver.minor); | |
| 125 | try expectEqual(exp.patch, ver.patch); | |
| 126 | } | |
| 127 | ||
| 128 | test "parseSdkVersion" { | |
| 129 | try testParseSdkVersionSuccess(.{ .major = 13, .minor = 4, .patch = 0 }, "13.4"); | |
| 130 | try testParseSdkVersionSuccess(.{ .major = 13, .minor = 4, .patch = 1 }, "13.4.1"); | |
| 131 | try testParseSdkVersionSuccess(.{ .major = 11, .minor = 15, .patch = 0 }, "11.15"); | |
| 132 | ||
| 133 | try expect(parseSdkVersion("11") == null); | |
| 134 | } |
src/libc_installation.zig+3-3| ... | ... | @@ -187,13 +187,13 @@ pub const LibCInstallation = struct { |
| 187 | 187 | return error.DarwinSdkNotFound; |
| 188 | 188 | const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse |
| 189 | 189 | return error.DarwinSdkNotFound; |
| 190 | defer args.allocator.free(sdk.path); | |
| 190 | defer args.allocator.free(sdk); | |
| 191 | 191 | |
| 192 | 192 | self.include_dir = try fs.path.join(args.allocator, &.{ |
| 193 | sdk.path, "usr/include", | |
| 193 | sdk, "usr/include", | |
| 194 | 194 | }); |
| 195 | 195 | self.sys_include_dir = try fs.path.join(args.allocator, &.{ |
| 196 | sdk.path, "usr/include", | |
| 196 | sdk, "usr/include", | |
| 197 | 197 | }); |
| 198 | 198 | return self; |
| 199 | 199 | } else if (is_windows) { |
src/link/MachO/load_commands.zig+61-2| ... | ... | @@ -278,7 +278,11 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo |
| 278 | 278 | const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8)); |
| 279 | 279 | break :blk platform_version; |
| 280 | 280 | }; |
| 281 | const sdk_version: u32 = if (options.darwin_sdk_version) |ver| | |
| 281 | const sdk_version: ?std.SemanticVersion = options.darwin_sdk_version orelse blk: { | |
| 282 | if (options.sysroot) |path| break :blk inferSdkVersionFromSdkPath(path); | |
| 283 | break :blk null; | |
| 284 | }; | |
| 285 | const sdk_version_value: u32 = if (sdk_version) |ver| | |
| 282 | 286 | @intCast(ver.major << 16 | ver.minor << 8) |
| 283 | 287 | else |
| 284 | 288 | platform_version; |
| ... | ... | @@ -293,7 +297,7 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo |
| 293 | 297 | else => unreachable, |
| 294 | 298 | }, |
| 295 | 299 | .minos = platform_version, |
| 296 | .sdk = sdk_version, | |
| 300 | .sdk = sdk_version_value, | |
| 297 | 301 | .ntools = 1, |
| 298 | 302 | }); |
| 299 | 303 | try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{ |
| ... | ... | @@ -315,3 +319,58 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: an |
| 315 | 319 | }, lc_writer); |
| 316 | 320 | } |
| 317 | 321 | } |
| 322 | ||
| 323 | fn inferSdkVersionFromSdkPath(path: []const u8) ?std.SemanticVersion { | |
| 324 | const stem = std.fs.path.stem(path); | |
| 325 | const start = for (stem, 0..) |c, i| { | |
| 326 | if (std.ascii.isDigit(c)) break i; | |
| 327 | } else stem.len; | |
| 328 | const end = for (stem[start..], start..) |c, i| { | |
| 329 | if (std.ascii.isDigit(c) or c == '.') continue; | |
| 330 | break i; | |
| 331 | } else stem.len; | |
| 332 | return parseSdkVersion(stem[start..end]); | |
| 333 | } | |
| 334 | ||
| 335 | // Versions reported by Apple aren't exactly semantically valid as they usually omit | |
| 336 | // the patch component, so we parse SDK value by hand. | |
| 337 | fn parseSdkVersion(raw: []const u8) ?std.SemanticVersion { | |
| 338 | var parsed: std.SemanticVersion = .{ | |
| 339 | .major = 0, | |
| 340 | .minor = 0, | |
| 341 | .patch = 0, | |
| 342 | }; | |
| 343 | ||
| 344 | const parseNext = struct { | |
| 345 | fn parseNext(it: anytype) ?u16 { | |
| 346 | const nn = it.next() orelse return null; | |
| 347 | return std.fmt.parseInt(u16, nn, 10) catch null; | |
| 348 | } | |
| 349 | }.parseNext; | |
| 350 | ||
| 351 | var it = std.mem.splitAny(u8, raw, "."); | |
| 352 | parsed.major = parseNext(&it) orelse return null; | |
| 353 | parsed.minor = parseNext(&it) orelse return null; | |
| 354 | parsed.patch = parseNext(&it) orelse 0; | |
| 355 | return parsed; | |
| 356 | } | |
| 357 | ||
| 358 | const expect = std.testing.expect; | |
| 359 | const expectEqual = std.testing.expectEqual; | |
| 360 | ||
| 361 | fn testParseSdkVersionSuccess(exp: std.SemanticVersion, raw: []const u8) !void { | |
| 362 | const maybe_ver = parseSdkVersion(raw); | |
| 363 | try expect(maybe_ver != null); | |
| 364 | const ver = maybe_ver.?; | |
| 365 | try expectEqual(exp.major, ver.major); | |
| 366 | try expectEqual(exp.minor, ver.minor); | |
| 367 | try expectEqual(exp.patch, ver.patch); | |
| 368 | } | |
| 369 | ||
| 370 | test "parseSdkVersion" { | |
| 371 | try testParseSdkVersionSuccess(.{ .major = 13, .minor = 4, .patch = 0 }, "13.4"); | |
| 372 | try testParseSdkVersionSuccess(.{ .major = 13, .minor = 4, .patch = 1 }, "13.4.1"); | |
| 373 | try testParseSdkVersionSuccess(.{ .major = 11, .minor = 15, .patch = 0 }, "11.15"); | |
| 374 | ||
| 375 | try expect(parseSdkVersion("11") == null); | |
| 376 | } |
test/link/macho/bugs/13056/build.zig+3-3| ... | ... | @@ -23,13 +23,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 23 | 23 | .name = "test", |
| 24 | 24 | .optimize = optimize, |
| 25 | 25 | }); |
| 26 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include" }) }); | |
| 27 | exe.addIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include/c++/v1" }) }); | |
| 26 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); | |
| 27 | exe.addIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) }); | |
| 28 | 28 | exe.addCSourceFile(.{ .file = .{ .path = "test.cpp" }, .flags = &.{ |
| 29 | 29 | "-nostdlib++", |
| 30 | 30 | "-nostdinc++", |
| 31 | 31 | } }); |
| 32 | exe.addObjectFile(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/lib/libc++.tbd" }) }); | |
| 32 | exe.addObjectFile(.{ .path = b.pathJoin(&.{ sdk, "/usr/lib/libc++.tbd" }) }); | |
| 33 | 33 | |
| 34 | 34 | const run_cmd = b.addRunArtifact(exe); |
| 35 | 35 | run_cmd.expectStdErrEqual("x: 5\n"); |
test/standalone/ios/build.zig+4-4| ... | ... | @@ -14,7 +14,7 @@ pub fn build(b: *std.Build) void { |
| 14 | 14 | }; |
| 15 | 15 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch @panic("couldn't detect native target"); |
| 16 | 16 | const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse @panic("no iOS SDK found"); |
| 17 | b.sysroot = sdk.path; | |
| 17 | b.sysroot = sdk; | |
| 18 | 18 | |
| 19 | 19 | const exe = b.addExecutable(.{ |
| 20 | 20 | .name = "main", |
| ... | ... | @@ -22,9 +22,9 @@ pub fn build(b: *std.Build) void { |
| 22 | 22 | .target = target, |
| 23 | 23 | }); |
| 24 | 24 | exe.addCSourceFile(.{ .file = .{ .path = "main.m" }, .flags = &.{} }); |
| 25 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include" }) }); | |
| 26 | exe.addSystemFrameworkPath(.{ .path = b.pathJoin(&.{ sdk.path, "/System/Library/Frameworks" }) }); | |
| 27 | exe.addLibraryPath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/lib" }) }); | |
| 25 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); | |
| 26 | exe.addSystemFrameworkPath(.{ .path = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) }); | |
| 27 | exe.addLibraryPath(.{ .path = b.pathJoin(&.{ sdk, "/usr/lib" }) }); | |
| 28 | 28 | exe.linkFramework("Foundation"); |
| 29 | 29 | exe.linkFramework("UIKit"); |
| 30 | 30 | exe.linkLibC(); |