| 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,9 +81,9 @@ pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths { |
| 81 | if (comptime builtin.target.isDarwin()) { | 81 | if (comptime builtin.target.isDarwin()) { |
| 82 | if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: { | 82 | if (std.zig.system.darwin.isSdkInstalled(arena)) sdk: { |
| 83 | const sdk = std.zig.system.darwin.getSdk(arena, native_target) orelse break :sdk; | 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" })); | 84 | try self.addLibDir(try std.fs.path.join(arena, &.{ sdk, "usr/lib" })); |
| 85 | try self.addFrameworkDir(try std.fs.path.join(arena, &.{ sdk.path, "System/Library/Frameworks" })); | 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.path, "usr/include" })); | 86 | try self.addIncludeDir(try std.fs.path.join(arena, &.{ sdk, "usr/include" })); |
| 87 | return self; | 87 | return self; |
| 88 | } | 88 | } |
| 89 | return self; | 89 | return self; |
lib/std/zig/system/darwin.zig+14-87| ... | @@ -30,12 +30,11 @@ pub fn isSdkInstalled(allocator: Allocator) bool { | ... | @@ -30,12 +30,11 @@ pub fn isSdkInstalled(allocator: Allocator) bool { |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | /// Detect SDK on Darwin. | 32 | /// Detect SDK on Darwin. |
| 33 | /// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK sysroot (if any). | 33 | /// Calls `xcrun --sdk <target_sdk> --show-sdk-path` which fetches the path to the SDK. |
| 34 | /// Subsequently calls `xcrun --sdk <target_sdk> --show-sdk-version` which fetches version of the SDK. | 34 | /// Caller owns the memory. |
| 35 | /// The caller needs to deinit the resulting struct. | ||
| 36 | /// stderr from xcrun is ignored. | 35 | /// stderr from xcrun is ignored. |
| 37 | /// If error.OutOfMemory occurs in Allocator, this function returns null. | 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 | const is_simulator_abi = target.abi == .simulator; | 38 | const is_simulator_abi = target.abi == .simulator; |
| 40 | const sdk = switch (target.os.tag) { | 39 | const sdk = switch (target.os.tag) { |
| 41 | .macos => "macosx", | 40 | .macos => "macosx", |
| ... | @@ -44,91 +43,19 @@ pub fn getSdk(allocator: Allocator, target: Target) ?Sdk { | ... | @@ -44,91 +43,19 @@ pub fn getSdk(allocator: Allocator, target: Target) ?Sdk { |
| 44 | .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", | 43 | .tvos => if (is_simulator_abi) "appletvsimulator" else "appletvos", |
| 45 | else => return null, | 44 | else => return null, |
| 46 | }; | 45 | }; |
| 47 | const path = path: { | 46 | const argv = &[_][]const u8{ "/usr/bin/xcrun", "--sdk", sdk, "--show-sdk-path" }; |
| 48 | 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; |
| 49 | const result = std.process.Child.exec(.{ .allocator = allocator, .argv = argv }) catch return null; | 48 | defer { |
| 50 | defer { | 49 | allocator.free(result.stderr); |
| 51 | allocator.free(result.stderr); | 50 | allocator.free(result.stdout); |
| 52 | allocator.free(result.stdout); | 51 | } |
| 53 | } | 52 | switch (result.term) { |
| 54 | switch (result.term) { | 53 | .Exited => |code| if (code != 0) return null, |
| 55 | .Exited => |code| if (code != 0) return null, | 54 | else => 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); | ||
| 109 | } | 55 | } |
| 110 | }; | 56 | return allocator.dupe(u8, mem.trimRight(u8, result.stdout, "\r\n")) catch null; |
| 57 | } | ||
| 111 | 58 | ||
| 112 | test { | 59 | test { |
| 113 | _ = macos; | 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,13 +187,13 @@ pub const LibCInstallation = struct { |
| 187 | return error.DarwinSdkNotFound; | 187 | return error.DarwinSdkNotFound; |
| 188 | const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse | 188 | const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse |
| 189 | return error.DarwinSdkNotFound; | 189 | return error.DarwinSdkNotFound; |
| 190 | defer args.allocator.free(sdk.path); | 190 | defer args.allocator.free(sdk); |
| 191 | 191 | ||
| 192 | self.include_dir = try fs.path.join(args.allocator, &.{ | 192 | self.include_dir = try fs.path.join(args.allocator, &.{ |
| 193 | sdk.path, "usr/include", | 193 | sdk, "usr/include", |
| 194 | }); | 194 | }); |
| 195 | self.sys_include_dir = try fs.path.join(args.allocator, &.{ | 195 | self.sys_include_dir = try fs.path.join(args.allocator, &.{ |
| 196 | sdk.path, "usr/include", | 196 | sdk, "usr/include", |
| 197 | }); | 197 | }); |
| 198 | return self; | 198 | return self; |
| 199 | } else if (is_windows) { | 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,7 +278,11 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo |
| 278 | const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8)); | 278 | const platform_version = @as(u32, @intCast(ver.major << 16 | ver.minor << 8)); |
| 279 | break :blk platform_version; | 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 | @intCast(ver.major << 16 | ver.minor << 8) | 286 | @intCast(ver.major << 16 | ver.minor << 8) |
| 283 | else | 287 | else |
| 284 | platform_version; | 288 | platform_version; |
| ... | @@ -293,7 +297,7 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo | ... | @@ -293,7 +297,7 @@ pub fn writeBuildVersionLC(options: *const link.Options, lc_writer: anytype) !vo |
| 293 | else => unreachable, | 297 | else => unreachable, |
| 294 | }, | 298 | }, |
| 295 | .minos = platform_version, | 299 | .minos = platform_version, |
| 296 | .sdk = sdk_version, | 300 | .sdk = sdk_version_value, |
| 297 | .ntools = 1, | 301 | .ntools = 1, |
| 298 | }); | 302 | }); |
| 299 | try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{ | 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,3 +319,58 @@ pub fn writeLoadDylibLCs(dylibs: []const Dylib, referenced: []u16, lc_writer: an |
| 315 | }, lc_writer); | 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,13 +23,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 23 | .name = "test", | 23 | .name = "test", |
| 24 | .optimize = optimize, | 24 | .optimize = optimize, |
| 25 | }); | 25 | }); |
| 26 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include" }) }); | 26 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); |
| 27 | exe.addIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include/c++/v1" }) }); | 27 | exe.addIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) }); |
| 28 | exe.addCSourceFile(.{ .file = .{ .path = "test.cpp" }, .flags = &.{ | 28 | exe.addCSourceFile(.{ .file = .{ .path = "test.cpp" }, .flags = &.{ |
| 29 | "-nostdlib++", | 29 | "-nostdlib++", |
| 30 | "-nostdinc++", | 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 | const run_cmd = b.addRunArtifact(exe); | 34 | const run_cmd = b.addRunArtifact(exe); |
| 35 | run_cmd.expectStdErrEqual("x: 5\n"); | 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,7 +14,7 @@ pub fn build(b: *std.Build) void { |
| 14 | }; | 14 | }; |
| 15 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch @panic("couldn't detect native target"); | 15 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch @panic("couldn't detect native target"); |
| 16 | const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse @panic("no iOS SDK found"); | 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 | const exe = b.addExecutable(.{ | 19 | const exe = b.addExecutable(.{ |
| 20 | .name = "main", | 20 | .name = "main", |
| ... | @@ -22,9 +22,9 @@ pub fn build(b: *std.Build) void { | ... | @@ -22,9 +22,9 @@ pub fn build(b: *std.Build) void { |
| 22 | .target = target, | 22 | .target = target, |
| 23 | }); | 23 | }); |
| 24 | exe.addCSourceFile(.{ .file = .{ .path = "main.m" }, .flags = &.{} }); | 24 | exe.addCSourceFile(.{ .file = .{ .path = "main.m" }, .flags = &.{} }); |
| 25 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include" }) }); | 25 | exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); |
| 26 | exe.addSystemFrameworkPath(.{ .path = b.pathJoin(&.{ sdk.path, "/System/Library/Frameworks" }) }); | 26 | exe.addSystemFrameworkPath(.{ .path = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) }); |
| 27 | exe.addLibraryPath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/lib" }) }); | 27 | exe.addLibraryPath(.{ .path = b.pathJoin(&.{ sdk, "/usr/lib" }) }); |
| 28 | exe.linkFramework("Foundation"); | 28 | exe.linkFramework("Foundation"); |
| 29 | exe.linkFramework("UIKit"); | 29 | exe.linkFramework("UIKit"); |
| 30 | exe.linkLibC(); | 30 | exe.linkLibC(); |