diff --git a/build.zig b/build.zig index 2437dbe92a..5b5f0004cc 100644 --- a/build.zig +++ b/build.zig @@ -128,7 +128,8 @@ pub fn build(b: *std.Build) !void { "llvm-has-xtensa", "Whether LLVM has the experimental target xtensa enabled", ) orelse false; - const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse false; + const enable_ios_sdk = b.option(bool, "enable-ios-sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false; + const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk; const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false; const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h"); @@ -485,11 +486,12 @@ pub fn build(b: *std.Build) !void { b, optimization_modes, enable_macos_sdk, + enable_ios_sdk, false, enable_symlinks_windows, )); test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); - test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, false, enable_symlinks_windows)); + test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, false, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); test_step.dependOn(tests.addCliTests(b)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index f69427ab05..26d3e89552 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -766,6 +766,60 @@ const MachODumper = struct { }); }, + .BUILD_VERSION => { + const blc = lc.cast(macho.build_version_command).?; + try writer.writeByte('\n'); + try writer.print( + \\platform {s} + \\minos {d}.{d}.{d} + \\sdk {d}.{d}.{d} + \\ntools {d} + , .{ + @tagName(blc.platform), + blc.minos >> 16, + @as(u8, @truncate(blc.minos >> 8)), + @as(u8, @truncate(blc.minos)), + blc.sdk >> 16, + @as(u8, @truncate(blc.sdk >> 8)), + @as(u8, @truncate(blc.sdk)), + blc.ntools, + }); + for (lc.getBuildVersionTools()) |tool| { + try writer.writeByte('\n'); + switch (tool.tool) { + .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}), + else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}), + } + try writer.print( + \\version {d}.{d}.{d} + , .{ + tool.version >> 16, + @as(u8, @truncate(tool.version >> 8)), + @as(u8, @truncate(tool.version)), + }); + } + }, + + .VERSION_MIN_MACOSX, + .VERSION_MIN_IPHONEOS, + .VERSION_MIN_WATCHOS, + .VERSION_MIN_TVOS, + => { + const vlc = lc.cast(macho.version_min_command).?; + try writer.writeByte('\n'); + try writer.print( + \\version {d}.{d}.{d} + \\sdk {d}.{d}.{d} + , .{ + vlc.version >> 16, + @as(u8, @truncate(vlc.version >> 8)), + @as(u8, @truncate(vlc.version)), + vlc.sdk >> 16, + @as(u8, @truncate(vlc.sdk >> 8)), + @as(u8, @truncate(vlc.sdk)), + }); + }, + else => {}, } } diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 681343faae..a3a6b61b61 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -42,7 +42,6 @@ unwind_tables: ?bool, compress_debug_sections: enum { none, zlib } = .none, lib_paths: ArrayList(LazyPath), rpaths: ArrayList(LazyPath), -framework_dirs: ArrayList(LazyPath), frameworks: StringHashMap(FrameworkLinkInfo), verbose_link: bool, verbose_cc: bool, @@ -261,6 +260,8 @@ const FrameworkLinkInfo = struct { pub const IncludeDir = union(enum) { path: LazyPath, path_system: LazyPath, + framework_path: LazyPath, + framework_path_system: LazyPath, other_step: *Compile, config_header_step: *Step.ConfigHeader, }; @@ -442,7 +443,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .c_macros = ArrayList([]const u8).init(owner.allocator), .lib_paths = ArrayList(LazyPath).init(owner.allocator), .rpaths = ArrayList(LazyPath).init(owner.allocator), - .framework_dirs = ArrayList(LazyPath).init(owner.allocator), .installed_headers = ArrayList(*Step).init(owner.allocator), .c_std = std.Build.CStd.C99, .zig_lib_dir = null, @@ -1050,9 +1050,15 @@ pub fn addRPath(self: *Compile, directory_source: LazyPath) void { directory_source.addStepDependencies(&self.step); } +pub fn addSystemFrameworkPath(self: *Compile, directory_source: LazyPath) void { + const b = self.step.owner; + self.include_dirs.append(IncludeDir{ .framework_path_system = directory_source.dupe(b) }) catch @panic("OOM"); + directory_source.addStepDependencies(&self.step); +} + pub fn addFrameworkPath(self: *Compile, directory_source: LazyPath) void { const b = self.step.owner; - self.framework_dirs.append(directory_source.dupe(b)) catch @panic("OOM"); + self.include_dirs.append(IncludeDir{ .framework_path = directory_source.dupe(b) }) catch @panic("OOM"); directory_source.addStepDependencies(&self.step); } @@ -1772,27 +1778,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(include_path.getPath(b)); }, .path_system => |include_path| { - if (b.sysroot != null) { - try zig_args.append("-iwithsysroot"); - } else { - try zig_args.append("-isystem"); - } - - const resolved_include_path = include_path.getPath(b); - - const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { - // We need to check for disk designator and strip it out from dir path so - // that zig/clang can concat resolved_include_path with sysroot. - const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); - - if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| { - break :blk resolved_include_path[where + disk_designator.len ..]; - } - - break :blk resolved_include_path; - } else resolved_include_path; - - try zig_args.append(common_include_path); + try zig_args.append("-isystem"); + try zig_args.append(include_path.getPath(b)); + }, + .framework_path => |include_path| { + try zig_args.append("-F"); + try zig_args.append(include_path.getPath2(b, step)); + }, + .framework_path_system => |include_path| { + try zig_args.append("-iframework"); + try zig_args.append(include_path.getPath2(b, step)); }, .other_step => |other| { if (other.generated_h) |header| { @@ -1847,17 +1842,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { zig_args.appendAssumeCapacity(rpath.getPath2(b, step)); } - for (self.framework_dirs.items) |directory_source| { - if (b.sysroot != null) { - try zig_args.append("-iframeworkwithsysroot"); - } else { - try zig_args.append("-iframework"); - } - try zig_args.append(directory_source.getPath2(b, step)); - try zig_args.append("-F"); - try zig_args.append(directory_source.getPath2(b, step)); - } - { var it = self.frameworks.iterator(); while (it.next()) |entry| { diff --git a/lib/std/macho.zig b/lib/std/macho.zig index d70e3448bd..5cd632b3a6 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1898,6 +1898,16 @@ pub const LoadCommandIterator = struct { const data = lc.data[rpath_lc.path..]; return mem.sliceTo(data, 0); } + + /// Asserts LoadCommand is of type build_version_command. + pub fn getBuildVersionTools(lc: LoadCommand) []const build_tool_version { + const build_lc = lc.cast(build_version_command).?; + const ntools = build_lc.ntools; + if (ntools == 0) return &[0]build_tool_version{}; + const data = lc.data[@sizeOf(build_version_command)..]; + const tools = @as([*]const build_tool_version, @ptrCast(@alignCast(&data[0])))[0..ntools]; + return tools; + } }; pub fn next(it: *LoadCommandIterator) ?LoadCommand { diff --git a/src/main.zig b/src/main.zig index 5a0cb6b4b1..fd220bf172 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1156,12 +1156,20 @@ fn buildOutputType( try clang_argv.append(args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-I")) { try cssan.addIncludePath(.I, arg, args_iter.nextOrFatal(), false); - } else if (mem.eql(u8, arg, "-isystem") or mem.eql(u8, arg, "-iwithsysroot")) { + } else if (mem.eql(u8, arg, "-isystem")) { try cssan.addIncludePath(.isystem, arg, args_iter.nextOrFatal(), false); + } else if (mem.eql(u8, arg, "-iwithsysroot")) { + try cssan.addIncludePath(.iwithsysroot, arg, args_iter.nextOrFatal(), false); } else if (mem.eql(u8, arg, "-idirafter")) { try cssan.addIncludePath(.idirafter, arg, args_iter.nextOrFatal(), false); - } else if (mem.eql(u8, arg, "-iframework") or mem.eql(u8, arg, "-iframeworkwithsysroot")) { - try cssan.addIncludePath(.iframework, arg, args_iter.nextOrFatal(), false); + } else if (mem.eql(u8, arg, "-iframework")) { + const path = args_iter.nextOrFatal(); + try cssan.addIncludePath(.iframework, arg, path, false); + try framework_dirs.append(path); // Forward to the backend as -F + } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) { + const path = args_iter.nextOrFatal(); + try cssan.addIncludePath(.iframeworkwithsysroot, arg, path, false); + try framework_dirs.append(path); // Forward to the backend as -F } else if (mem.eql(u8, arg, "--version")) { const next_arg = args_iter.nextOrFatal(); version = std.SemanticVersion.parse(next_arg) catch |err| { @@ -6191,6 +6199,11 @@ const ClangSearchSanitizer = struct { if (m.idirafter) std.log.warn(wtxt, .{ dir, "isystem", "idirafter" }); if (m.iframework) std.log.warn(wtxt, .{ dir, "isystem", "iframework" }); }, + .iwithsysroot => { + if (m.iwithsysroot) return; + m.iwithsysroot = true; + if (m.iframeworkwithsysroot) std.log.warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" }); + }, .idirafter => { if (m.idirafter) return; m.idirafter = true; @@ -6205,18 +6218,25 @@ const ClangSearchSanitizer = struct { if (m.isystem) std.log.warn(wtxt, .{ dir, "iframework", "isystem" }); if (m.idirafter) std.log.warn(wtxt, .{ dir, "iframework", "idirafter" }); }, + .iframeworkwithsysroot => { + if (m.iframeworkwithsysroot) return; + m.iframeworkwithsysroot = true; + if (m.iwithsysroot) std.log.warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" }); + }, } try self.argv.append(arg); if (!joined) try self.argv.append(dir); } - const Group = enum { I, isystem, idirafter, iframework }; + const Group = enum { I, isystem, iwithsysroot, idirafter, iframework, iframeworkwithsysroot }; const Membership = packed struct { I: bool = false, isystem: bool = false, + iwithsysroot: bool = false, idirafter: bool = false, iframework: bool = false, + iframeworkwithsysroot: bool = false, }; }; diff --git a/test/standalone.zig b/test/standalone.zig index 3725456fa1..6618986292 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -245,6 +245,10 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/compiler_rt_panic", .import = @import("standalone/compiler_rt_panic/build.zig"), }, + .{ + .build_root = "test/standalone/ios", + .import = @import("standalone/ios/build.zig"), + }, }; const std = @import("std"); diff --git a/test/standalone/ios/build.zig b/test/standalone/ios/build.zig new file mode 100644 index 0000000000..4e409cb5a3 --- /dev/null +++ b/test/standalone/ios/build.zig @@ -0,0 +1,37 @@ +const std = @import("std"); + +pub const requires_symlinks = true; +pub const requires_ios_sdk = true; + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{ + .cpu_arch = .aarch64, + .os_tag = .ios, + }; + const target_info = std.zig.system.NativeTargetInfo.detect(target) catch @panic("couldn't detect native target"); + const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse @panic("no iOS SDK found"); + b.sysroot = sdk.path; + + const exe = b.addExecutable(.{ + .name = "main", + .optimize = optimize, + .target = target, + }); + exe.addCSourceFile(.{ .file = .{ .path = "main.m" }, .flags = &.{} }); + exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/include" }) }); + exe.addSystemFrameworkPath(.{ .path = b.pathJoin(&.{ sdk.path, "/System/Library/Frameworks" }) }); + exe.addLibraryPath(.{ .path = b.pathJoin(&.{ sdk.path, "/usr/lib" }) }); + exe.linkFramework("Foundation"); + exe.linkFramework("UIKit"); + exe.linkLibC(); + + const check = exe.checkObject(); + check.checkStart(); + check.checkExact("cmd BUILD_VERSION"); + check.checkExact("platform IOS"); + test_step.dependOn(&check.step); +} diff --git a/test/standalone/ios/main.m b/test/standalone/ios/main.m new file mode 100644 index 0000000000..35465210e3 --- /dev/null +++ b/test/standalone/ios/main.m @@ -0,0 +1,34 @@ +#import + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@end + +int main() { + @autoreleasepool { + return UIApplicationMain(0, nil, nil, NSStringFromClass([AppDelegate class])); + } +} + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(id)options { + CGRect mainScreenBounds = [[UIScreen mainScreen] bounds]; + self.window = [[UIWindow alloc] initWithFrame:mainScreenBounds]; + UIViewController *viewController = [[UIViewController alloc] init]; + viewController.view.frame = mainScreenBounds; + + NSString* msg = @"Hello world"; + + UILabel *label = [[UILabel alloc] initWithFrame:mainScreenBounds]; + [label setText:msg]; + [viewController.view addSubview: label]; + + self.window.rootViewController = viewController; + + [self.window makeKeyAndVisible]; + + return YES; +} + +@end diff --git a/test/tests.zig b/test/tests.zig index 37bf50021c..45fffc7dc2 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -566,6 +566,7 @@ pub fn addStandaloneTests( b: *std.Build, optimize_modes: []const OptimizeMode, enable_macos_sdk: bool, + enable_ios_sdk: bool, omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { @@ -615,10 +616,13 @@ pub fn addStandaloneTests( case.import.requires_symlinks; const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and case.import.requires_macos_sdk; + const requires_ios_sdk = @hasDecl(case.import, "requires_ios_sdk") and + case.import.requires_ios_sdk; const bad = (requires_stage2 and omit_stage2) or (requires_symlinks and omit_symlinks) or - (requires_macos_sdk and !enable_macos_sdk); + (requires_macos_sdk and !enable_macos_sdk) or + (requires_ios_sdk and !enable_ios_sdk); if (!bad) { const dep = b.anonymousDependency(case.build_root, case.import, .{}); const dep_step = dep.builder.default_step; @@ -635,6 +639,7 @@ pub fn addStandaloneTests( pub fn addLinkTests( b: *std.Build, enable_macos_sdk: bool, + enable_ios_sdk: bool, omit_stage2: bool, enable_symlinks_windows: bool, ) *Step { @@ -648,10 +653,13 @@ pub fn addLinkTests( case.import.requires_symlinks; const requires_macos_sdk = @hasDecl(case.import, "requires_macos_sdk") and case.import.requires_macos_sdk; + const requires_ios_sdk = @hasDecl(case.import, "requires_ios_sdk") and + case.import.requires_ios_sdk; const bad = (requires_stage2 and omit_stage2) or (requires_symlinks and omit_symlinks) or - (requires_macos_sdk and !enable_macos_sdk); + (requires_macos_sdk and !enable_macos_sdk) or + (requires_ios_sdk and !enable_ios_sdk); if (!bad) { const dep = b.anonymousDependency(case.build_root, case.import, .{}); const dep_step = dep.builder.default_step;