| author | |
| committer | |
| log | 291c08f7b0ea4e333c37a0ac378176891f255fa0 |
| tree | ee9571bf196c1fce5ec9d298dffc103c66b4d3ab |
| parent | 87d8cb19e4eed905b93d39554ea9a2a1012f6668 |
| parent | 03ddb42b8bb96815c1bb4b857ffdfb94191ab861 |
| signature |
82 files changed, 1142 insertions(+), 465 deletions(-)
build.zig+1| ... | ... | @@ -489,6 +489,7 @@ pub fn build(b: *Builder) !void { |
| 489 | 489 | |
| 490 | 490 | toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); |
| 491 | 491 | toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target)); |
| 492 | toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk)); | |
| 492 | 493 | toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); |
| 493 | 494 | toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes)); |
| 494 | 495 | toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); |
ci/azure/macos_script+1| ... | ... | @@ -76,6 +76,7 @@ release/bin/zig build test-run-translated-c -Denable-macos-sdk |
| 76 | 76 | release/bin/zig build docs -Denable-macos-sdk |
| 77 | 77 | release/bin/zig build test-fmt -Denable-macos-sdk |
| 78 | 78 | release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded |
| 79 | release/bin/zig build test-link -Denable-macos-sdk | |
| 79 | 80 | |
| 80 | 81 | if [ "${BUILD_REASON}" != "PullRequest" ]; then |
| 81 | 82 | mv ../LICENSE release/ |
lib/std/build.zig+13| ... | ... | @@ -24,6 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 24 | 24 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 25 | 25 | pub const RunStep = @import("build/RunStep.zig"); |
| 26 | 26 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 27 | pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); | |
| 27 | 28 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 28 | 29 | pub const OptionsStep = @import("build/OptionsStep.zig"); |
| 29 | 30 | |
| ... | ... | @@ -1582,6 +1583,9 @@ pub const LibExeObjStep = struct { |
| 1582 | 1583 | /// (Darwin) Path to entitlements file |
| 1583 | 1584 | entitlements: ?[]const u8 = null, |
| 1584 | 1585 | |
| 1586 | /// (Darwin) Size of the pagezero segment. | |
| 1587 | pagezero_size: ?u64 = null, | |
| 1588 | ||
| 1585 | 1589 | /// Position Independent Code |
| 1586 | 1590 | force_pic: ?bool = null, |
| 1587 | 1591 | |
| ... | ... | @@ -1861,6 +1865,10 @@ pub const LibExeObjStep = struct { |
| 1861 | 1865 | return run_step; |
| 1862 | 1866 | } |
| 1863 | 1867 | |
| 1868 | pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 1869 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | |
| 1870 | } | |
| 1871 | ||
| 1864 | 1872 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { |
| 1865 | 1873 | self.linker_script = source.dupe(self.builder); |
| 1866 | 1874 | source.addStepDependencies(&self.step); |
| ... | ... | @@ -2638,6 +2646,10 @@ pub const LibExeObjStep = struct { |
| 2638 | 2646 | if (self.entitlements) |entitlements| { |
| 2639 | 2647 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); |
| 2640 | 2648 | } |
| 2649 | if (self.pagezero_size) |pagezero_size| { | |
| 2650 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); | |
| 2651 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); | |
| 2652 | } | |
| 2641 | 2653 | |
| 2642 | 2654 | if (self.bundle_compiler_rt) |x| { |
| 2643 | 2655 | if (x) { |
| ... | ... | @@ -3443,6 +3455,7 @@ pub const Step = struct { |
| 3443 | 3455 | write_file, |
| 3444 | 3456 | run, |
| 3445 | 3457 | check_file, |
| 3458 | check_object, | |
| 3446 | 3459 | install_raw, |
| 3447 | 3460 | options, |
| 3448 | 3461 | custom, |
lib/std/build/CheckObjectStep.zig created+392| ... | ... | @@ -0,0 +1,392 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const build = std.build; | |
| 4 | const fs = std.fs; | |
| 5 | const macho = std.macho; | |
| 6 | const mem = std.mem; | |
| 7 | const testing = std.testing; | |
| 8 | ||
| 9 | const CheckObjectStep = @This(); | |
| 10 | ||
| 11 | const Allocator = mem.Allocator; | |
| 12 | const Builder = build.Builder; | |
| 13 | const Step = build.Step; | |
| 14 | ||
| 15 | pub const base_id = .check_obj; | |
| 16 | ||
| 17 | step: Step, | |
| 18 | builder: *Builder, | |
| 19 | source: build.FileSource, | |
| 20 | max_bytes: usize = 20 * 1024 * 1024, | |
| 21 | checks: std.ArrayList(Check), | |
| 22 | dump_symtab: bool = false, | |
| 23 | obj_format: std.Target.ObjectFormat, | |
| 24 | ||
| 25 | pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 26 | const gpa = builder.allocator; | |
| 27 | const self = gpa.create(CheckObjectStep) catch unreachable; | |
| 28 | self.* = .{ | |
| 29 | .builder = builder, | |
| 30 | .step = Step.init(.check_file, "CheckObject", gpa, make), | |
| 31 | .source = source.dupe(builder), | |
| 32 | .checks = std.ArrayList(Check).init(gpa), | |
| 33 | .obj_format = obj_format, | |
| 34 | }; | |
| 35 | self.source.addStepDependencies(&self.step); | |
| 36 | return self; | |
| 37 | } | |
| 38 | ||
| 39 | const Action = union(enum) { | |
| 40 | match: MatchAction, | |
| 41 | compute_eq: ComputeEqAction, | |
| 42 | }; | |
| 43 | ||
| 44 | /// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` | |
| 45 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature | |
| 46 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use | |
| 47 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. | |
| 48 | /// it should be plenty useful in its current form. | |
| 49 | const MatchAction = struct { | |
| 50 | needle: []const u8, | |
| 51 | ||
| 52 | /// Will return true if the `needle` was found in the `haystack`. | |
| 53 | /// Some examples include: | |
| 54 | /// | |
| 55 | /// LC 0 => will match in its entirety | |
| 56 | /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64 | |
| 57 | /// and save under `vmaddr` global name (see `global_vars` param) | |
| 58 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` | |
| 59 | /// in that order with other letters in between | |
| 60 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { | |
| 61 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); | |
| 62 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); | |
| 63 | ||
| 64 | while (needle_it.next()) |needle_tok| { | |
| 65 | const hay_tok = hay_it.next() orelse return false; | |
| 66 | ||
| 67 | if (mem.indexOf(u8, needle_tok, "{*}")) |index| { | |
| 68 | // We have fuzzy matchers within the search pattern, so we match substrings. | |
| 69 | var start = index; | |
| 70 | var n_tok = needle_tok; | |
| 71 | var h_tok = hay_tok; | |
| 72 | while (true) { | |
| 73 | n_tok = n_tok[start + 3 ..]; | |
| 74 | const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end| | |
| 75 | n_tok[0..sub_end] | |
| 76 | else | |
| 77 | n_tok; | |
| 78 | if (mem.indexOf(u8, h_tok, inner) == null) return false; | |
| 79 | start = mem.indexOf(u8, n_tok, "{*}") orelse break; | |
| 80 | } | |
| 81 | } else if (mem.startsWith(u8, needle_tok, "{")) { | |
| 82 | const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace; | |
| 83 | if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; | |
| 84 | ||
| 85 | const name = needle_tok[1..closing_brace]; | |
| 86 | if (name.len == 0) return error.MissingBraceValue; | |
| 87 | const value = try std.fmt.parseInt(u64, hay_tok, 16); | |
| 88 | try global_vars.putNoClobber(name, value); | |
| 89 | } else { | |
| 90 | if (!mem.eql(u8, hay_tok, needle_tok)) return false; | |
| 91 | } | |
| 92 | } | |
| 93 | ||
| 94 | return true; | |
| 95 | } | |
| 96 | }; | |
| 97 | ||
| 98 | /// ComputeEqAction can be used to perform an operation on the extracted global variables | |
| 99 | /// using the MatchAction. It currently only supports an addition. The operation is required | |
| 100 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, | |
| 101 | /// to avoid any parsing really). | |
| 102 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively | |
| 103 | /// they could then be added with this simple program `vmaddr entryoff +`. | |
| 104 | const ComputeEqAction = struct { | |
| 105 | expected: []const u8, | |
| 106 | var_stack: std.ArrayList([]const u8), | |
| 107 | op_stack: std.ArrayList(Op), | |
| 108 | ||
| 109 | const Op = enum { | |
| 110 | add, | |
| 111 | }; | |
| 112 | }; | |
| 113 | ||
| 114 | const Check = struct { | |
| 115 | builder: *Builder, | |
| 116 | actions: std.ArrayList(Action), | |
| 117 | ||
| 118 | fn create(b: *Builder) Check { | |
| 119 | return .{ | |
| 120 | .builder = b, | |
| 121 | .actions = std.ArrayList(Action).init(b.allocator), | |
| 122 | }; | |
| 123 | } | |
| 124 | ||
| 125 | fn match(self: *Check, needle: []const u8) void { | |
| 126 | self.actions.append(.{ | |
| 127 | .match = .{ .needle = self.builder.dupe(needle) }, | |
| 128 | }) catch unreachable; | |
| 129 | } | |
| 130 | ||
| 131 | fn computeEq(self: *Check, act: ComputeEqAction) void { | |
| 132 | self.actions.append(.{ | |
| 133 | .compute_eq = act, | |
| 134 | }) catch unreachable; | |
| 135 | } | |
| 136 | }; | |
| 137 | ||
| 138 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. | |
| 139 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { | |
| 140 | var new_check = Check.create(self.builder); | |
| 141 | new_check.match(phrase); | |
| 142 | self.checks.append(new_check) catch unreachable; | |
| 143 | } | |
| 144 | ||
| 145 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. | |
| 146 | /// Asserts at least one check already exists. | |
| 147 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | |
| 148 | assert(self.checks.items.len > 0); | |
| 149 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 150 | last.match(phrase); | |
| 151 | } | |
| 152 | ||
| 153 | /// Creates a new check checking specifically symbol table parsed and dumped from the object | |
| 154 | /// file. | |
| 155 | /// Issuing this check will force parsing and dumping of the symbol table. | |
| 156 | pub fn checkInSymtab(self: *CheckObjectStep) void { | |
| 157 | self.dump_symtab = true; | |
| 158 | const symtab_label = switch (self.obj_format) { | |
| 159 | .macho => MachODumper.symtab_label, | |
| 160 | else => @panic("TODO other parsers"), | |
| 161 | }; | |
| 162 | self.checkStart(symtab_label); | |
| 163 | } | |
| 164 | ||
| 165 | /// Creates a new standalone, singular check which allows running simple binary operations | |
| 166 | /// on the extracted variables. It will then compare the reduced program with the value of | |
| 167 | /// the expected variable. | |
| 168 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { | |
| 169 | const gpa = self.builder.allocator; | |
| 170 | var ca = ComputeEqAction{ | |
| 171 | .expected = expected, | |
| 172 | .var_stack = std.ArrayList([]const u8).init(gpa), | |
| 173 | .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), | |
| 174 | }; | |
| 175 | ||
| 176 | var it = mem.tokenize(u8, program, " "); | |
| 177 | while (it.next()) |next| { | |
| 178 | if (mem.eql(u8, next, "+")) { | |
| 179 | ca.op_stack.append(.add) catch unreachable; | |
| 180 | } else { | |
| 181 | ca.var_stack.append(self.builder.dupe(next)) catch unreachable; | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | var new_check = Check.create(self.builder); | |
| 186 | new_check.computeEq(ca); | |
| 187 | self.checks.append(new_check) catch unreachable; | |
| 188 | } | |
| 189 | ||
| 190 | fn make(step: *Step) !void { | |
| 191 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | |
| 192 | ||
| 193 | const gpa = self.builder.allocator; | |
| 194 | const src_path = self.source.getPath(self.builder); | |
| 195 | const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | |
| 196 | ||
| 197 | const output = switch (self.obj_format) { | |
| 198 | .macho => try MachODumper.parseAndDump(contents, .{ | |
| 199 | .gpa = gpa, | |
| 200 | .dump_symtab = self.dump_symtab, | |
| 201 | }), | |
| 202 | .elf => @panic("TODO elf parser"), | |
| 203 | .coff => @panic("TODO coff parser"), | |
| 204 | .wasm => @panic("TODO wasm parser"), | |
| 205 | else => unreachable, | |
| 206 | }; | |
| 207 | ||
| 208 | var vars = std.StringHashMap(u64).init(gpa); | |
| 209 | ||
| 210 | for (self.checks.items) |chk| { | |
| 211 | var it = mem.tokenize(u8, output, "\r\n"); | |
| 212 | for (chk.actions.items) |act| { | |
| 213 | switch (act) { | |
| 214 | .match => |match_act| { | |
| 215 | while (it.next()) |line| { | |
| 216 | if (try match_act.match(line, &vars)) break; | |
| 217 | } else { | |
| 218 | std.debug.print( | |
| 219 | \\ | |
| 220 | \\========= Expected to find: ========================== | |
| 221 | \\{s} | |
| 222 | \\========= But parsed file does not contain it: ======= | |
| 223 | \\{s} | |
| 224 | \\ | |
| 225 | , .{ match_act.needle, output }); | |
| 226 | return error.TestFailed; | |
| 227 | } | |
| 228 | }, | |
| 229 | .compute_eq => |c_eq| { | |
| 230 | var values = std.ArrayList(u64).init(gpa); | |
| 231 | try values.ensureTotalCapacity(c_eq.var_stack.items.len); | |
| 232 | for (c_eq.var_stack.items) |vv| { | |
| 233 | const val = vars.get(vv) orelse { | |
| 234 | std.debug.print( | |
| 235 | \\ | |
| 236 | \\========= Variable was not extracted: =========== | |
| 237 | \\{s} | |
| 238 | \\========= From parsed file: ===================== | |
| 239 | \\{s} | |
| 240 | \\ | |
| 241 | , .{ vv, output }); | |
| 242 | return error.TestFailed; | |
| 243 | }; | |
| 244 | values.appendAssumeCapacity(val); | |
| 245 | } | |
| 246 | ||
| 247 | var op_i: usize = 1; | |
| 248 | var reduced: u64 = values.items[0]; | |
| 249 | for (c_eq.op_stack.items) |op| { | |
| 250 | const other = values.items[op_i]; | |
| 251 | switch (op) { | |
| 252 | .add => { | |
| 253 | reduced += other; | |
| 254 | }, | |
| 255 | } | |
| 256 | } | |
| 257 | ||
| 258 | const expected = vars.get(c_eq.expected) orelse { | |
| 259 | std.debug.print( | |
| 260 | \\ | |
| 261 | \\========= Variable was not extracted: =========== | |
| 262 | \\{s} | |
| 263 | \\========= From parsed file: ===================== | |
| 264 | \\{s} | |
| 265 | \\ | |
| 266 | , .{ c_eq.expected, output }); | |
| 267 | return error.TestFailed; | |
| 268 | }; | |
| 269 | try testing.expectEqual(reduced, expected); | |
| 270 | }, | |
| 271 | } | |
| 272 | } | |
| 273 | } | |
| 274 | } | |
| 275 | ||
| 276 | const Opts = struct { | |
| 277 | gpa: ?Allocator = null, | |
| 278 | dump_symtab: bool = false, | |
| 279 | }; | |
| 280 | ||
| 281 | const MachODumper = struct { | |
| 282 | const symtab_label = "symtab"; | |
| 283 | ||
| 284 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | |
| 285 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | |
| 286 | var stream = std.io.fixedBufferStream(bytes); | |
| 287 | const reader = stream.reader(); | |
| 288 | ||
| 289 | const hdr = try reader.readStruct(macho.mach_header_64); | |
| 290 | if (hdr.magic != macho.MH_MAGIC_64) { | |
| 291 | return error.InvalidMagicNumber; | |
| 292 | } | |
| 293 | ||
| 294 | var output = std.ArrayList(u8).init(gpa); | |
| 295 | const writer = output.writer(); | |
| 296 | ||
| 297 | var symtab_cmd: ?macho.symtab_command = null; | |
| 298 | var i: u16 = 0; | |
| 299 | while (i < hdr.ncmds) : (i += 1) { | |
| 300 | var cmd = try macho.LoadCommand.read(gpa, reader); | |
| 301 | ||
| 302 | if (opts.dump_symtab and cmd.cmd() == .SYMTAB) { | |
| 303 | symtab_cmd = cmd.symtab; | |
| 304 | } | |
| 305 | ||
| 306 | try dumpLoadCommand(cmd, i, writer); | |
| 307 | try writer.writeByte('\n'); | |
| 308 | } | |
| 309 | ||
| 310 | if (symtab_cmd) |cmd| { | |
| 311 | try writer.writeAll(symtab_label ++ "\n"); | |
| 312 | const strtab = bytes[cmd.stroff..][0..cmd.strsize]; | |
| 313 | const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)]; | |
| 314 | const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab); | |
| 315 | ||
| 316 | for (symtab) |sym| { | |
| 317 | if (sym.stab()) continue; | |
| 318 | const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); | |
| 319 | try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); | |
| 320 | } | |
| 321 | } | |
| 322 | ||
| 323 | return output.toOwnedSlice(); | |
| 324 | } | |
| 325 | ||
| 326 | fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { | |
| 327 | // print header first | |
| 328 | try writer.print( | |
| 329 | \\LC {d} | |
| 330 | \\cmd {s} | |
| 331 | \\cmdsize {d} | |
| 332 | , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); | |
| 333 | ||
| 334 | switch (lc.cmd()) { | |
| 335 | .SEGMENT_64 => { | |
| 336 | // TODO dump section headers | |
| 337 | const seg = lc.segment.inner; | |
| 338 | try writer.writeByte('\n'); | |
| 339 | try writer.print( | |
| 340 | \\segname {s} | |
| 341 | \\vmaddr {x} | |
| 342 | \\vmsize {x} | |
| 343 | \\fileoff {x} | |
| 344 | \\filesz {x} | |
| 345 | , .{ | |
| 346 | seg.segName(), | |
| 347 | seg.vmaddr, | |
| 348 | seg.vmsize, | |
| 349 | seg.fileoff, | |
| 350 | seg.filesize, | |
| 351 | }); | |
| 352 | }, | |
| 353 | ||
| 354 | .ID_DYLIB, | |
| 355 | .LOAD_DYLIB, | |
| 356 | => { | |
| 357 | const dylib = lc.dylib.inner.dylib; | |
| 358 | try writer.writeByte('\n'); | |
| 359 | try writer.print( | |
| 360 | \\name {s} | |
| 361 | \\timestamp {d} | |
| 362 | \\current version {x} | |
| 363 | \\compatibility version {x} | |
| 364 | , .{ | |
| 365 | mem.sliceTo(lc.dylib.data, 0), | |
| 366 | dylib.timestamp, | |
| 367 | dylib.current_version, | |
| 368 | dylib.compatibility_version, | |
| 369 | }); | |
| 370 | }, | |
| 371 | ||
| 372 | .MAIN => { | |
| 373 | try writer.writeByte('\n'); | |
| 374 | try writer.print( | |
| 375 | \\entryoff {x} | |
| 376 | \\stacksize {x} | |
| 377 | , .{ lc.main.entryoff, lc.main.stacksize }); | |
| 378 | }, | |
| 379 | ||
| 380 | .RPATH => { | |
| 381 | try writer.writeByte('\n'); | |
| 382 | try writer.print( | |
| 383 | \\path {s} | |
| 384 | , .{ | |
| 385 | mem.sliceTo(lc.rpath.data, 0), | |
| 386 | }); | |
| 387 | }, | |
| 388 | ||
| 389 | else => {}, | |
| 390 | } | |
| 391 | } | |
| 392 | }; |
lib/std/build/RunStep.zig+1| ... | ... | @@ -149,6 +149,7 @@ fn make(step: *Step) !void { |
| 149 | 149 | const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; |
| 150 | 150 | |
| 151 | 151 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); |
| 152 | ||
| 152 | 153 | for (self.argv.items) |arg| { |
| 153 | 154 | switch (arg) { |
| 154 | 155 | .bytes => |bytes| try argv_list.append(bytes), |
src/link/MachO.zig+92-72| ... | ... | @@ -934,6 +934,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 934 | 934 | try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); |
| 935 | 935 | } |
| 936 | 936 | |
| 937 | if (self.base.options.entry) |entry| { | |
| 938 | try argv.append("-e"); | |
| 939 | try argv.append(entry); | |
| 940 | } | |
| 941 | ||
| 937 | 942 | try argv.appendSlice(positionals.items); |
| 938 | 943 | |
| 939 | 944 | try argv.append("-o"); |
| ... | ... | @@ -3371,13 +3376,12 @@ fn addCodeSignatureLC(self: *MachO) !void { |
| 3371 | 3376 | fn setEntryPoint(self: *MachO) !void { |
| 3372 | 3377 | if (self.base.options.output_mode != .Exe) return; |
| 3373 | 3378 | |
| 3374 | // TODO we should respect the -entry flag passed in by the user to set a custom | |
| 3375 | // entrypoint. For now, assume default of `_main`. | |
| 3376 | 3379 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 3377 | const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{ | |
| 3380 | const entry_name = self.base.options.entry orelse "_main"; | |
| 3381 | const n_strx = self.strtab_dir.getKeyAdapted(entry_name, StringIndexAdapter{ | |
| 3378 | 3382 | .bytes = &self.strtab, |
| 3379 | 3383 | }) orelse { |
| 3380 | log.err("'_main' export not found", .{}); | |
| 3384 | log.err("entrypoint '{s}' not found", .{entry_name}); | |
| 3381 | 3385 | return error.MissingMainEntrypoint; |
| 3382 | 3386 | }; |
| 3383 | 3387 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; |
| ... | ... | @@ -5711,28 +5715,46 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5711 | 5715 | |
| 5712 | 5716 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 5713 | 5717 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only; |
| 5718 | ||
| 5719 | const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64)); | |
| 5714 | 5720 | const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); |
| 5715 | const bind_size = try bind.bindInfoSize(bind_pointers.items); | |
| 5716 | const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); | |
| 5717 | const export_size = trie.size; | |
| 5721 | dyld_info.rebase_off = @intCast(u32, rebase_off); | |
| 5722 | dyld_info.rebase_size = @intCast(u32, rebase_size); | |
| 5723 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ | |
| 5724 | dyld_info.rebase_off, | |
| 5725 | dyld_info.rebase_off + dyld_info.rebase_size, | |
| 5726 | }); | |
| 5718 | 5727 | |
| 5719 | dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); | |
| 5720 | dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64))); | |
| 5721 | seg.inner.filesize += dyld_info.rebase_size; | |
| 5728 | const bind_off = mem.alignForwardGeneric(u64, dyld_info.rebase_off + dyld_info.rebase_size, @alignOf(u64)); | |
| 5729 | const bind_size = try bind.bindInfoSize(bind_pointers.items); | |
| 5730 | dyld_info.bind_off = @intCast(u32, bind_off); | |
| 5731 | dyld_info.bind_size = @intCast(u32, bind_size); | |
| 5732 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ | |
| 5733 | dyld_info.bind_off, | |
| 5734 | dyld_info.bind_off + dyld_info.bind_size, | |
| 5735 | }); | |
| 5722 | 5736 | |
| 5723 | dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size; | |
| 5724 | dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, bind_size, @alignOf(u64))); | |
| 5725 | seg.inner.filesize += dyld_info.bind_size; | |
| 5737 | const lazy_bind_off = mem.alignForwardGeneric(u64, dyld_info.bind_off + dyld_info.bind_size, @alignOf(u64)); | |
| 5738 | const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); | |
| 5739 | dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off); | |
| 5740 | dyld_info.lazy_bind_size = @intCast(u32, lazy_bind_size); | |
| 5741 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ | |
| 5742 | dyld_info.lazy_bind_off, | |
| 5743 | dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, | |
| 5744 | }); | |
| 5726 | 5745 | |
| 5727 | dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size; | |
| 5728 | dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64))); | |
| 5729 | seg.inner.filesize += dyld_info.lazy_bind_size; | |
| 5746 | const export_off = mem.alignForwardGeneric(u64, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, @alignOf(u64)); | |
| 5747 | const export_size = trie.size; | |
| 5748 | dyld_info.export_off = @intCast(u32, export_off); | |
| 5749 | dyld_info.export_size = @intCast(u32, export_size); | |
| 5750 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ | |
| 5751 | dyld_info.export_off, | |
| 5752 | dyld_info.export_off + dyld_info.export_size, | |
| 5753 | }); | |
| 5730 | 5754 | |
| 5731 | dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size; | |
| 5732 | dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, export_size, @alignOf(u64))); | |
| 5733 | seg.inner.filesize += dyld_info.export_size; | |
| 5755 | seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff; | |
| 5734 | 5756 | |
| 5735 | const needed_size = dyld_info.rebase_size + dyld_info.bind_size + dyld_info.lazy_bind_size + dyld_info.export_size; | |
| 5757 | const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off; | |
| 5736 | 5758 | var buffer = try self.base.allocator.alloc(u8, needed_size); |
| 5737 | 5759 | defer self.base.allocator.free(buffer); |
| 5738 | 5760 | mem.set(u8, buffer, 0); |
| ... | ... | @@ -5740,14 +5762,15 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5740 | 5762 | var stream = std.io.fixedBufferStream(buffer); |
| 5741 | 5763 | const writer = stream.writer(); |
| 5742 | 5764 | |
| 5765 | const base_off = dyld_info.rebase_off; | |
| 5743 | 5766 | try bind.writeRebaseInfo(rebase_pointers.items, writer); |
| 5744 | try stream.seekBy(@intCast(i64, dyld_info.rebase_size) - @intCast(i64, rebase_size)); | |
| 5767 | try stream.seekTo(dyld_info.bind_off - base_off); | |
| 5745 | 5768 | |
| 5746 | 5769 | try bind.writeBindInfo(bind_pointers.items, writer); |
| 5747 | try stream.seekBy(@intCast(i64, dyld_info.bind_size) - @intCast(i64, bind_size)); | |
| 5770 | try stream.seekTo(dyld_info.lazy_bind_off - base_off); | |
| 5748 | 5771 | |
| 5749 | 5772 | try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); |
| 5750 | try stream.seekBy(@intCast(i64, dyld_info.lazy_bind_size) - @intCast(i64, lazy_bind_size)); | |
| 5773 | try stream.seekTo(dyld_info.export_off - base_off); | |
| 5751 | 5774 | |
| 5752 | 5775 | _ = try trie.write(writer); |
| 5753 | 5776 | |
| ... | ... | @@ -5758,7 +5781,7 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5758 | 5781 | |
| 5759 | 5782 | try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); |
| 5760 | 5783 | try self.populateLazyBindOffsetsInStubHelper( |
| 5761 | buffer[dyld_info.rebase_size + dyld_info.bind_size ..][0..dyld_info.lazy_bind_size], | |
| 5784 | buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size], | |
| 5762 | 5785 | ); |
| 5763 | 5786 | self.load_commands_dirty = true; |
| 5764 | 5787 | } |
| ... | ... | @@ -5928,32 +5951,31 @@ fn writeFunctionStarts(self: *MachO) !void { |
| 5928 | 5951 | } else break; |
| 5929 | 5952 | } |
| 5930 | 5953 | |
| 5931 | const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); | |
| 5932 | var buffer = try self.base.allocator.alloc(u8, max_size); | |
| 5933 | defer self.base.allocator.free(buffer); | |
| 5934 | mem.set(u8, buffer, 0); | |
| 5954 | var buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 5955 | defer buffer.deinit(); | |
| 5935 | 5956 | |
| 5936 | var stream = std.io.fixedBufferStream(buffer); | |
| 5937 | const writer = stream.writer(); | |
| 5957 | const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); | |
| 5958 | try buffer.ensureTotalCapacity(max_size); | |
| 5938 | 5959 | |
| 5939 | 5960 | for (offsets.items) |offset| { |
| 5940 | try std.leb.writeULEB128(writer, offset); | |
| 5961 | try std.leb.writeULEB128(buffer.writer(), offset); | |
| 5941 | 5962 | } |
| 5942 | 5963 | |
| 5943 | const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64))); | |
| 5944 | 5964 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 5945 | 5965 | const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data; |
| 5946 | 5966 | |
| 5947 | fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 5948 | fn_cmd.datasize = needed_size; | |
| 5949 | seg.inner.filesize += needed_size; | |
| 5967 | const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); | |
| 5968 | const datasize = buffer.items.len; | |
| 5969 | fn_cmd.dataoff = @intCast(u32, dataoff); | |
| 5970 | fn_cmd.datasize = @intCast(u32, datasize); | |
| 5971 | seg.inner.filesize = fn_cmd.dataoff + fn_cmd.datasize - seg.inner.fileoff; | |
| 5950 | 5972 | |
| 5951 | 5973 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ |
| 5952 | 5974 | fn_cmd.dataoff, |
| 5953 | 5975 | fn_cmd.dataoff + fn_cmd.datasize, |
| 5954 | 5976 | }); |
| 5955 | 5977 | |
| 5956 | try self.base.file.?.pwriteAll(buffer[0..needed_size], fn_cmd.dataoff); | |
| 5978 | try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff); | |
| 5957 | 5979 | self.load_commands_dirty = true; |
| 5958 | 5980 | } |
| 5959 | 5981 | |
| ... | ... | @@ -6001,11 +6023,12 @@ fn writeDices(self: *MachO) !void { |
| 6001 | 6023 | |
| 6002 | 6024 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6003 | 6025 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data; |
| 6004 | const needed_size = @intCast(u32, buf.items.len); | |
| 6005 | 6026 | |
| 6006 | dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 6007 | dice_cmd.datasize = needed_size; | |
| 6008 | seg.inner.filesize += needed_size; | |
| 6027 | const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); | |
| 6028 | const datasize = buf.items.len; | |
| 6029 | dice_cmd.dataoff = @intCast(u32, dataoff); | |
| 6030 | dice_cmd.datasize = @intCast(u32, datasize); | |
| 6031 | seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff; | |
| 6009 | 6032 | |
| 6010 | 6033 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ |
| 6011 | 6034 | dice_cmd.dataoff, |
| ... | ... | @@ -6022,7 +6045,8 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6022 | 6045 | |
| 6023 | 6046 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6024 | 6047 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 6025 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 6048 | const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64)); | |
| 6049 | symtab.symoff = @intCast(u32, symoff); | |
| 6026 | 6050 | |
| 6027 | 6051 | var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 6028 | 6052 | defer locals.deinit(); |
| ... | ... | @@ -6122,7 +6146,7 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6122 | 6146 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); |
| 6123 | 6147 | |
| 6124 | 6148 | symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs); |
| 6125 | seg.inner.filesize += locals_size + exports_size + undefs_size; | |
| 6149 | seg.inner.filesize = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64) - seg.inner.fileoff; | |
| 6126 | 6150 | |
| 6127 | 6151 | // Update dynamic symbol table. |
| 6128 | 6152 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab; |
| ... | ... | @@ -6142,22 +6166,21 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6142 | 6166 | const nstubs = @intCast(u32, self.stubs_table.keys().len); |
| 6143 | 6167 | const ngot_entries = @intCast(u32, self.got_entries_table.keys().len); |
| 6144 | 6168 | |
| 6145 | dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 6169 | const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); | |
| 6170 | dysymtab.indirectsymoff = @intCast(u32, indirectsymoff); | |
| 6146 | 6171 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; |
| 6147 | 6172 | |
| 6148 | const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); | |
| 6149 | seg.inner.filesize += needed_size; | |
| 6173 | seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff; | |
| 6150 | 6174 | |
| 6151 | 6175 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ |
| 6152 | 6176 | dysymtab.indirectsymoff, |
| 6153 | dysymtab.indirectsymoff + needed_size, | |
| 6177 | dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32), | |
| 6154 | 6178 | }); |
| 6155 | 6179 | |
| 6156 | var buf = try self.base.allocator.alloc(u8, needed_size); | |
| 6157 | defer self.base.allocator.free(buf); | |
| 6158 | ||
| 6159 | var stream = std.io.fixedBufferStream(buf); | |
| 6160 | var writer = stream.writer(); | |
| 6180 | var buf = std.ArrayList(u8).init(self.base.allocator); | |
| 6181 | defer buf.deinit(); | |
| 6182 | try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32)); | |
| 6183 | const writer = buf.writer(); | |
| 6161 | 6184 | |
| 6162 | 6185 | stubs.reserved1 = 0; |
| 6163 | 6186 | for (self.stubs_table.keys()) |key| { |
| ... | ... | @@ -6191,7 +6214,9 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6191 | 6214 | } |
| 6192 | 6215 | } |
| 6193 | 6216 | |
| 6194 | try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); | |
| 6217 | assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32)); | |
| 6218 | ||
| 6219 | try self.base.file.?.pwriteAll(buf.items, dysymtab.indirectsymoff); | |
| 6195 | 6220 | self.load_commands_dirty = true; |
| 6196 | 6221 | } |
| 6197 | 6222 | |
| ... | ... | @@ -6201,18 +6226,16 @@ fn writeStringTable(self: *MachO) !void { |
| 6201 | 6226 | |
| 6202 | 6227 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6203 | 6228 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 6204 | symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 6205 | symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); | |
| 6206 | seg.inner.filesize += symtab.strsize; | |
| 6229 | const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); | |
| 6230 | const strsize = self.strtab.items.len; | |
| 6231 | symtab.stroff = @intCast(u32, stroff); | |
| 6232 | symtab.strsize = @intCast(u32, strsize); | |
| 6233 | seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff; | |
| 6207 | 6234 | |
| 6208 | 6235 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 6209 | 6236 | |
| 6210 | 6237 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); |
| 6211 | 6238 | |
| 6212 | if (symtab.strsize > self.strtab.items.len) { | |
| 6213 | // This is potentially the last section, so we need to pad it out. | |
| 6214 | try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); | |
| 6215 | } | |
| 6216 | 6239 | self.load_commands_dirty = true; |
| 6217 | 6240 | } |
| 6218 | 6241 | |
| ... | ... | @@ -6236,25 +6259,22 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 6236 | 6259 | const tracy = trace(@src()); |
| 6237 | 6260 | defer tracy.end(); |
| 6238 | 6261 | |
| 6239 | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | |
| 6240 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; | |
| 6262 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | |
| 6263 | const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; | |
| 6241 | 6264 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file |
| 6242 | 6265 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 |
| 6243 | const fileoff = mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, 16); | |
| 6244 | const padding = fileoff - (linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize); | |
| 6245 | const needed_size = code_sig.estimateSize(fileoff); | |
| 6246 | code_sig_cmd.dataoff = @intCast(u32, fileoff); | |
| 6247 | code_sig_cmd.datasize = needed_size; | |
| 6266 | const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, 16); | |
| 6267 | const datasize = code_sig.estimateSize(dataoff); | |
| 6268 | cs_cmd.dataoff = @intCast(u32, dataoff); | |
| 6269 | cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff)); | |
| 6248 | 6270 | |
| 6249 | 6271 | // Advance size of __LINKEDIT segment |
| 6250 | linkedit_segment.inner.filesize += needed_size + padding; | |
| 6251 | if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) { | |
| 6252 | linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size); | |
| 6253 | } | |
| 6254 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); | |
| 6272 | seg.inner.filesize = cs_cmd.dataoff + cs_cmd.datasize - seg.inner.fileoff; | |
| 6273 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); | |
| 6274 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ dataoff, dataoff + datasize }); | |
| 6255 | 6275 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| 6256 | 6276 | // except for code signature data. |
| 6257 | try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); | |
| 6277 | try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1); | |
| 6258 | 6278 | self.load_commands_dirty = true; |
| 6259 | 6279 | } |
| 6260 | 6280 |
test/link.zig created+64| ... | ... | @@ -0,0 +1,64 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const tests = @import("tests.zig"); | |
| 4 | ||
| 5 | pub fn addCases(cases: *tests.StandaloneContext) void { | |
| 6 | cases.addBuildFile("test/link/bss/build.zig", .{ | |
| 7 | .build_modes = false, // we only guarantee zerofill for undefined in Debug | |
| 8 | }); | |
| 9 | ||
| 10 | cases.addBuildFile("test/link/common_symbols/build.zig", .{ | |
| 11 | .build_modes = true, | |
| 12 | }); | |
| 13 | ||
| 14 | cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{ | |
| 15 | .build_modes = true, | |
| 16 | }); | |
| 17 | ||
| 18 | cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{ | |
| 19 | .build_modes = true, | |
| 20 | }); | |
| 21 | ||
| 22 | cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{ | |
| 23 | .build_modes = true, | |
| 24 | }); | |
| 25 | ||
| 26 | cases.addBuildFile("test/link/tls/build.zig", .{ | |
| 27 | .build_modes = true, | |
| 28 | }); | |
| 29 | ||
| 30 | if (builtin.os.tag == .macos) { | |
| 31 | cases.addBuildFile("test/link/macho/entry/build.zig", .{ | |
| 32 | .build_modes = true, | |
| 33 | }); | |
| 34 | ||
| 35 | cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ | |
| 36 | .build_modes = false, | |
| 37 | }); | |
| 38 | ||
| 39 | cases.addBuildFile("test/link/macho/dylib/build.zig", .{ | |
| 40 | .build_modes = true, | |
| 41 | }); | |
| 42 | ||
| 43 | cases.addBuildFile("test/link/macho/frameworks/build.zig", .{ | |
| 44 | .build_modes = true, | |
| 45 | .requires_macos_sdk = true, | |
| 46 | }); | |
| 47 | ||
| 48 | // Try to build and run an Objective-C executable. | |
| 49 | cases.addBuildFile("test/link/macho/objc/build.zig", .{ | |
| 50 | .build_modes = true, | |
| 51 | .requires_macos_sdk = true, | |
| 52 | }); | |
| 53 | ||
| 54 | // Try to build and run an Objective-C++ executable. | |
| 55 | cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ | |
| 56 | .build_modes = true, | |
| 57 | .requires_macos_sdk = true, | |
| 58 | }); | |
| 59 | ||
| 60 | cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ | |
| 61 | .build_modes = true, | |
| 62 | }); | |
| 63 | } | |
| 64 | } |
test/link/bss/build.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | const test_step = b.step("test", "Test"); | |
| 6 | ||
| 7 | const exe = b.addExecutable("bss", "main.zig"); | |
| 8 | b.default_step.dependOn(&exe.step); | |
| 9 | exe.setBuildMode(mode); | |
| 10 | ||
| 11 | const run = exe.run(); | |
| 12 | run.expectStdOutEqual("0, 1, 0\n"); | |
| 13 | test_step.dependOn(&run.step); | |
| 14 | } |
test/link/bss/main.zig created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | // Stress test zerofill layout | |
| 4 | var buffer: [0x1000000]u64 = undefined; | |
| 5 | ||
| 6 | pub fn main() anyerror!void { | |
| 7 | buffer[0x10] = 1; | |
| 8 | try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{ | |
| 9 | buffer[0], | |
| 10 | buffer[0x10], | |
| 11 | buffer[0x1000000 - 1], | |
| 12 | }); | |
| 13 | } |
test/link/common_symbols/a.c created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | int i; | |
| 2 | int j; | |
| 3 | ||
| 4 | int add_to_i_and_j(int x) { | |
| 5 | return x + i + j; | |
| 6 | } |
test/link/common_symbols/b.c created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | long i; | |
| 2 | int j = 2; | |
| 3 | int k; | |
| 4 | ||
| 5 | void incr_i() { | |
| 6 | i++; | |
| 7 | } |
test/link/common_symbols/build.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"}); | |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | ||
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 12 | test_exe.linkLibrary(lib_a); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test it"); | |
| 15 | test_step.dependOn(&test_exe.step); | |
| 16 | } |
test/link/common_symbols/c.c created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | extern int k; | |
| 2 | ||
| 3 | int common_defined_externally() { | |
| 4 | return k; | |
| 5 | } |
test/link/common_symbols/main.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | extern fn common_defined_externally() c_int; | |
| 5 | extern fn incr_i() void; | |
| 6 | extern fn add_to_i_and_j(x: c_int) c_int; | |
| 7 | ||
| 8 | test "undef shadows common symbol: issue #9937" { | |
| 9 | try expect(common_defined_externally() == 0); | |
| 10 | } | |
| 11 | ||
| 12 | test "import C common symbols" { | |
| 13 | incr_i(); | |
| 14 | const res = add_to_i_and_j(2); | |
| 15 | try expect(res == 5); | |
| 16 | } |
test/link/common_symbols_alignment/a.c created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | int foo; | |
| 2 | __attribute__((aligned(4096))) int bar; |
test/link/common_symbols_alignment/build.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | ||
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 12 | test_exe.linkLibrary(lib_a); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test it"); | |
| 15 | test_step.dependOn(&test_exe.step); | |
| 16 | } |
test/link/common_symbols_alignment/main.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | extern var foo: i32; | |
| 4 | extern var bar: i32; | |
| 5 | ||
| 6 | test { | |
| 7 | try std.testing.expect(@ptrToInt(&foo) % 4 == 0); | |
| 8 | try std.testing.expect(@ptrToInt(&bar) % 4096 == 0); | |
| 9 | } |
test/link/interdependent_static_c_libs/a.c created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | #include "a.h" | |
| 2 | int32_t add(int32_t a, int32_t b) { | |
| 3 | return a + b; | |
| 4 | } |
test/link/interdependent_static_c_libs/a.h created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | #include <stdint.h> | |
| 2 | int32_t add(int32_t a, int32_t b); |
test/link/interdependent_static_c_libs/b.c created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | #include "a.h" | |
| 2 | #include "b.h" | |
| 3 | ||
| 4 | int32_t sub(int32_t a, int32_t b) { | |
| 5 | return add(a, -1 * b); | |
| 6 | } |
test/link/interdependent_static_c_libs/b.h created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | #include <stdint.h> | |
| 2 | int32_t sub(int32_t a, int32_t b); |
test/link/interdependent_static_c_libs/build.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | lib_a.addIncludePath("."); | |
| 10 | ||
| 11 | const lib_b = b.addStaticLibrary("b", null); | |
| 12 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | |
| 13 | lib_b.setBuildMode(mode); | |
| 14 | lib_b.addIncludePath("."); | |
| 15 | ||
| 16 | const test_exe = b.addTest("main.zig"); | |
| 17 | test_exe.setBuildMode(mode); | |
| 18 | test_exe.linkLibrary(lib_a); | |
| 19 | test_exe.linkLibrary(lib_b); | |
| 20 | test_exe.addIncludePath("."); | |
| 21 | ||
| 22 | const test_step = b.step("test", "Test it"); | |
| 23 | test_step.dependOn(&test_exe.step); | |
| 24 | } |
test/link/interdependent_static_c_libs/main.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const c = @cImport(@cInclude("b.h")); | |
| 4 | ||
| 5 | test "import C sub" { | |
| 6 | const result = c.sub(2, 1); | |
| 7 | try expect(result == 1); | |
| 8 | } |
test/link/macho/dylib/a.c created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | #include <stdio.h> | |
| 2 | ||
| 3 | char world[] = "world"; | |
| 4 | ||
| 5 | char* hello() { | |
| 6 | return "Hello"; | |
| 7 | } |
test/link/macho/dylib/build.zig created+49| ... | ... | @@ -0,0 +1,49 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test"); | |
| 8 | test_step.dependOn(b.getInstallStep()); | |
| 9 | ||
| 10 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 11 | dylib.setBuildMode(mode); | |
| 12 | dylib.addCSourceFile("a.c", &.{}); | |
| 13 | dylib.linkLibC(); | |
| 14 | dylib.install(); | |
| 15 | ||
| 16 | const check_dylib = dylib.checkObject(.macho); | |
| 17 | check_dylib.checkStart("cmd ID_DYLIB"); | |
| 18 | check_dylib.checkNext("name @rpath/liba.dylib"); | |
| 19 | check_dylib.checkNext("timestamp 2"); | |
| 20 | check_dylib.checkNext("current version 10000"); | |
| 21 | check_dylib.checkNext("compatibility version 10000"); | |
| 22 | ||
| 23 | test_step.dependOn(&check_dylib.step); | |
| 24 | ||
| 25 | const exe = b.addExecutable("main", null); | |
| 26 | exe.setBuildMode(mode); | |
| 27 | exe.addCSourceFile("main.c", &.{}); | |
| 28 | exe.linkSystemLibrary("a"); | |
| 29 | exe.linkLibC(); | |
| 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); | |
| 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | |
| 32 | ||
| 33 | const check_exe = exe.checkObject(.macho); | |
| 34 | check_exe.checkStart("cmd LOAD_DYLIB"); | |
| 35 | check_exe.checkNext("name @rpath/liba.dylib"); | |
| 36 | check_exe.checkNext("timestamp 2"); | |
| 37 | check_exe.checkNext("current version 10000"); | |
| 38 | check_exe.checkNext("compatibility version 10000"); | |
| 39 | ||
| 40 | check_exe.checkStart("cmd RPATH"); | |
| 41 | check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); | |
| 42 | ||
| 43 | test_step.dependOn(&check_exe.step); | |
| 44 | ||
| 45 | const run = exe.run(); | |
| 46 | run.cwd = b.pathFromRoot("."); | |
| 47 | run.expectStdOutEqual("Hello world"); | |
| 48 | test_step.dependOn(&run.step); | |
| 49 | } |
test/link/macho/dylib/main.c created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | #include <stdio.h> | |
| 2 | ||
| 3 | char* hello(); | |
| 4 | extern char world[]; | |
| 5 | ||
| 6 | int main() { | |
| 7 | printf("%s %s", hello(), world); | |
| 8 | return 0; | |
| 9 | } |
test/link/macho/entry/build.zig created+34| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test"); | |
| 8 | test_step.dependOn(b.getInstallStep()); | |
| 9 | ||
| 10 | const exe = b.addExecutable("main", null); | |
| 11 | exe.setBuildMode(mode); | |
| 12 | exe.addCSourceFile("main.c", &.{}); | |
| 13 | exe.linkLibC(); | |
| 14 | exe.entry_symbol_name = "_non_main"; | |
| 15 | ||
| 16 | const check_exe = exe.checkObject(.macho); | |
| 17 | ||
| 18 | check_exe.checkStart("segname __TEXT"); | |
| 19 | check_exe.checkNext("vmaddr {vmaddr}"); | |
| 20 | ||
| 21 | check_exe.checkStart("cmd MAIN"); | |
| 22 | check_exe.checkNext("entryoff {entryoff}"); | |
| 23 | ||
| 24 | check_exe.checkInSymtab(); | |
| 25 | check_exe.checkNext("_non_main {n_value}"); | |
| 26 | ||
| 27 | check_exe.checkComputeEq("vmaddr entryoff +", "n_value"); | |
| 28 | ||
| 29 | test_step.dependOn(&check_exe.step); | |
| 30 | ||
| 31 | const run = exe.run(); | |
| 32 | run.expectStdOutEqual("42"); | |
| 33 | test_step.dependOn(&run.step); | |
| 34 | } |
test/link/macho/entry/main.c created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | #include <stdio.h> | |
| 2 | ||
| 3 | int non_main() { | |
| 4 | printf("%d", 42); | |
| 5 | return 0; | |
| 6 | } |
test/link/macho/frameworks/build.zig created+32| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test the program"); | |
| 8 | ||
| 9 | const exe = b.addExecutable("test", null); | |
| 10 | b.default_step.dependOn(&exe.step); | |
| 11 | exe.addCSourceFile("main.c", &[0][]const u8{}); | |
| 12 | exe.setBuildMode(mode); | |
| 13 | exe.linkLibC(); | |
| 14 | exe.linkFramework("Cocoa"); | |
| 15 | ||
| 16 | const check = exe.checkObject(.macho); | |
| 17 | check.checkStart("cmd LOAD_DYLIB"); | |
| 18 | check.checkNext("name {*}Cocoa"); | |
| 19 | ||
| 20 | switch (mode) { | |
| 21 | .Debug, .ReleaseSafe => { | |
| 22 | check.checkStart("cmd LOAD_DYLIB"); | |
| 23 | check.checkNext("name {*}libobjc{*}.dylib"); | |
| 24 | }, | |
| 25 | else => {}, | |
| 26 | } | |
| 27 | ||
| 28 | test_step.dependOn(&check.step); | |
| 29 | ||
| 30 | const run_cmd = exe.run(); | |
| 31 | test_step.dependOn(&run_cmd.step); | |
| 32 | } |
test/link/macho/frameworks/main.c created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <objc/runtime.h> | |
| 3 | ||
| 4 | int main() { | |
| 5 | assert(objc_getClass("NSObject") > 0); | |
| 6 | assert(objc_getClass("NSApplication") > 0); | |
| 7 | } |
test/link/macho/objc/Foo.h created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | #import <Foundation/Foundation.h> | |
| 2 | ||
| 3 | @interface Foo : NSObject | |
| 4 | ||
| 5 | - (NSString *)name; | |
| 6 | ||
| 7 | @end |
test/link/macho/objc/Foo.m created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | #import "Foo.h" | |
| 2 | ||
| 3 | @implementation Foo | |
| 4 | ||
| 5 | - (NSString *)name | |
| 6 | { | |
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | |
| 8 | return str; | |
| 9 | } | |
| 10 | ||
| 11 | @end |
test/link/macho/objc/build.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test the program"); | |
| 8 | ||
| 9 | const exe = b.addExecutable("test", null); | |
| 10 | b.default_step.dependOn(&exe.step); | |
| 11 | exe.addIncludePath("."); | |
| 12 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); | |
| 13 | exe.addCSourceFile("test.m", &[0][]const u8{}); | |
| 14 | exe.setBuildMode(mode); | |
| 15 | exe.linkLibC(); | |
| 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, | |
| 17 | // populate paths to the sysroot here. | |
| 18 | exe.linkFramework("Foundation"); | |
| 19 | ||
| 20 | const run_cmd = exe.run(); | |
| 21 | test_step.dependOn(&run_cmd.step); | |
| 22 | } |
test/link/macho/objc/test.m created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | #import "Foo.h" | |
| 2 | #import <assert.h> | |
| 3 | ||
| 4 | int main(int argc, char *argv[]) | |
| 5 | { | |
| 6 | @autoreleasepool { | |
| 7 | Foo *foo = [[Foo alloc] init]; | |
| 8 | NSString *result = [foo name]; | |
| 9 | assert([result isEqualToString:@"Zig"]); | |
| 10 | return 0; | |
| 11 | } | |
| 12 | } |
test/link/macho/objcpp/Foo.h created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | #import <Foundation/Foundation.h> | |
| 2 | ||
| 3 | @interface Foo : NSObject | |
| 4 | ||
| 5 | - (NSString *)name; | |
| 6 | ||
| 7 | @end |
test/link/macho/objcpp/Foo.mm created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | #import "Foo.h" | |
| 2 | ||
| 3 | @implementation Foo | |
| 4 | ||
| 5 | - (NSString *)name | |
| 6 | { | |
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | |
| 8 | return str; | |
| 9 | } | |
| 10 | ||
| 11 | @end |
test/link/macho/objcpp/build.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test the program"); | |
| 8 | ||
| 9 | const exe = b.addExecutable("test", null); | |
| 10 | b.default_step.dependOn(&exe.step); | |
| 11 | exe.addIncludePath("."); | |
| 12 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); | |
| 13 | exe.addCSourceFile("test.mm", &[0][]const u8{}); | |
| 14 | exe.setBuildMode(mode); | |
| 15 | exe.linkLibCpp(); | |
| 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, | |
| 17 | // populate paths to the sysroot here. | |
| 18 | exe.linkFramework("Foundation"); | |
| 19 | ||
| 20 | const run_cmd = exe.run(); | |
| 21 | run_cmd.expectStdOutEqual("Hello from C++ and Zig"); | |
| 22 | ||
| 23 | test_step.dependOn(&run_cmd.step); | |
| 24 | } |
test/link/macho/objcpp/test.mm created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | #import "Foo.h" | |
| 2 | #import <assert.h> | |
| 3 | #include <iostream> | |
| 4 | ||
| 5 | int main(int argc, char *argv[]) | |
| 6 | { | |
| 7 | @autoreleasepool { | |
| 8 | Foo *foo = [[Foo alloc] init]; | |
| 9 | NSString *result = [foo name]; | |
| 10 | std::cout << "Hello from C++ and " << [result UTF8String]; | |
| 11 | assert([result isEqualToString:@"Zig"]); | |
| 12 | return 0; | |
| 13 | } | |
| 14 | } |
test/link/macho/pagezero/build.zig created+43| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test"); | |
| 8 | test_step.dependOn(b.getInstallStep()); | |
| 9 | ||
| 10 | { | |
| 11 | const exe = b.addExecutable("pagezero", null); | |
| 12 | exe.setBuildMode(mode); | |
| 13 | exe.addCSourceFile("main.c", &.{}); | |
| 14 | exe.linkLibC(); | |
| 15 | exe.pagezero_size = 0x4000; | |
| 16 | ||
| 17 | const check = exe.checkObject(.macho); | |
| 18 | check.checkStart("LC 0"); | |
| 19 | check.checkNext("segname __PAGEZERO"); | |
| 20 | check.checkNext("vmaddr 0"); | |
| 21 | check.checkNext("vmsize 4000"); | |
| 22 | ||
| 23 | check.checkStart("segname __TEXT"); | |
| 24 | check.checkNext("vmaddr 4000"); | |
| 25 | ||
| 26 | test_step.dependOn(&check.step); | |
| 27 | } | |
| 28 | ||
| 29 | { | |
| 30 | const exe = b.addExecutable("no_pagezero", null); | |
| 31 | exe.setBuildMode(mode); | |
| 32 | exe.addCSourceFile("main.c", &.{}); | |
| 33 | exe.linkLibC(); | |
| 34 | exe.pagezero_size = 0; | |
| 35 | ||
| 36 | const check = exe.checkObject(.macho); | |
| 37 | check.checkStart("LC 0"); | |
| 38 | check.checkNext("segname __TEXT"); | |
| 39 | check.checkNext("vmaddr 0"); | |
| 40 | ||
| 41 | test_step.dependOn(&check.step); | |
| 42 | } | |
| 43 | } |
test/link/macho/pagezero/main.c created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | int main(int argc, char* argv[]) { | |
| 2 | return 0; | |
| 3 | } |
test/link/macho/stack_size/build.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test"); | |
| 8 | test_step.dependOn(b.getInstallStep()); | |
| 9 | ||
| 10 | const exe = b.addExecutable("main", null); | |
| 11 | exe.setBuildMode(mode); | |
| 12 | exe.addCSourceFile("main.c", &.{}); | |
| 13 | exe.linkLibC(); | |
| 14 | exe.stack_size = 0x100000000; | |
| 15 | ||
| 16 | const check_exe = exe.checkObject(.macho); | |
| 17 | check_exe.checkStart("cmd MAIN"); | |
| 18 | check_exe.checkNext("stacksize 100000000"); | |
| 19 | ||
| 20 | test_step.dependOn(&check_exe.step); | |
| 21 | ||
| 22 | const run = exe.run(); | |
| 23 | test_step.dependOn(&run.step); | |
| 24 | } |
test/link/macho/stack_size/main.c created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | int main(int argc, char* argv[]) { | |
| 2 | return 0; | |
| 3 | } |
test/link/static_lib_as_system_lib/a.c created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | #include "a.h" | |
| 2 | int32_t add(int32_t a, int32_t b) { | |
| 3 | return a + b; | |
| 4 | } |
test/link/static_lib_as_system_lib/a.h created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | #include <stdint.h> | |
| 2 | int32_t add(int32_t a, int32_t b); |
test/link/static_lib_as_system_lib/build.zig created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const lib_a = b.addStaticLibrary("a", null); | |
| 8 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | |
| 9 | lib_a.setBuildMode(mode); | |
| 10 | lib_a.addIncludePath("."); | |
| 11 | lib_a.install(); | |
| 12 | ||
| 13 | const test_exe = b.addTest("main.zig"); | |
| 14 | test_exe.setBuildMode(mode); | |
| 15 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la | |
| 16 | test_exe.addSystemIncludePath("."); | |
| 17 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; | |
| 18 | test_exe.addLibraryPath(search_path); | |
| 19 | ||
| 20 | const test_step = b.step("test", "Test it"); | |
| 21 | test_step.dependOn(b.getInstallStep()); | |
| 22 | test_step.dependOn(&test_exe.step); | |
| 23 | } |
test/link/static_lib_as_system_lib/main.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const c = @cImport(@cInclude("a.h")); | |
| 4 | ||
| 5 | test "import C add" { | |
| 6 | const result = c.add(2, 1); | |
| 7 | try expect(result == 3); | |
| 8 | } |
test/link/tls/a.c created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | _Thread_local int a; | |
| 2 | ||
| 3 | int getA() { | |
| 4 | return a; | |
| 5 | } |
test/link/tls/build.zig created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 7 | lib.setBuildMode(mode); | |
| 8 | lib.addCSourceFile("a.c", &.{}); | |
| 9 | lib.linkLibC(); | |
| 10 | ||
| 11 | const test_exe = b.addTest("main.zig"); | |
| 12 | test_exe.setBuildMode(mode); | |
| 13 | test_exe.linkLibrary(lib); | |
| 14 | test_exe.linkLibC(); | |
| 15 | ||
| 16 | const test_step = b.step("test", "Test it"); | |
| 17 | test_step.dependOn(&test_exe.step); | |
| 18 | } |
test/link/tls/main.zig created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | extern threadlocal var a: i32; | |
| 4 | extern fn getA() i32; | |
| 5 | ||
| 6 | fn getA2() i32 { | |
| 7 | return a; | |
| 8 | } | |
| 9 | ||
| 10 | test { | |
| 11 | a = 2; | |
| 12 | try std.testing.expect(getA() == 2); | |
| 13 | try std.testing.expect(2 == getA2()); | |
| 14 | try std.testing.expect(getA() == getA2()); | |
| 15 | } |
test/standalone.zig+4-33| ... | ... | @@ -13,31 +13,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 13 | 13 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); |
| 14 | 14 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); |
| 15 | 15 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); |
| 16 | if (builtin.os.tag == .macos) { | |
| 17 | // Zig's macOS linker does not yet support LTO for LLVM IR files: | |
| 18 | // https://github.com/ziglang/zig/issues/8680 | |
| 19 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | |
| 20 | .build_modes = false, | |
| 21 | .cross_targets = true, | |
| 22 | }); | |
| 23 | } else { | |
| 24 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | |
| 25 | .build_modes = true, | |
| 26 | .cross_targets = true, | |
| 27 | }); | |
| 28 | } | |
| 16 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | |
| 17 | .build_modes = true, | |
| 18 | .cross_targets = true, | |
| 19 | }); | |
| 29 | 20 | cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); |
| 30 | 21 | cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); |
| 31 | cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig", .{}); | |
| 32 | cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig", .{}); | |
| 33 | cases.addBuildFile("test/standalone/link_common_symbols/build.zig", .{}); | |
| 34 | cases.addBuildFile("test/standalone/link_frameworks/build.zig", .{ | |
| 35 | .requires_macos_sdk = true, | |
| 36 | }); | |
| 37 | cases.addBuildFile("test/standalone/link_common_symbols_alignment/build.zig", .{}); | |
| 38 | if (builtin.os.tag == .macos) { | |
| 39 | cases.addBuildFile("test/standalone/link_import_tls_dylib/build.zig", .{}); | |
| 40 | } | |
| 41 | 22 | cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); |
| 42 | 23 | cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); |
| 43 | 24 | cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); |
| ... | ... | @@ -69,16 +50,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 69 | 50 | if (builtin.os.tag == .linux) { |
| 70 | 51 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); |
| 71 | 52 | } |
| 72 | // Try to build and run an Objective-C executable. | |
| 73 | cases.addBuildFile("test/standalone/objc/build.zig", .{ | |
| 74 | .build_modes = true, | |
| 75 | .requires_macos_sdk = true, | |
| 76 | }); | |
| 77 | // Try to build and run an Objective-C++ executable. | |
| 78 | cases.addBuildFile("test/standalone/objcpp/build.zig", .{ | |
| 79 | .build_modes = true, | |
| 80 | .requires_macos_sdk = true, | |
| 81 | }); | |
| 82 | 53 | |
| 83 | 54 | // Ensure the development tools are buildable. |
| 84 | 55 | cases.add("tools/gen_spirv_spec.zig"); |
test/standalone/link_common_symbols/a.c deleted-6| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | int i; | |
| 2 | int j; | |
| 3 | ||
| 4 | int add_to_i_and_j(int x) { | |
| 5 | return x + i + j; | |
| 6 | } |
test/standalone/link_common_symbols/b.c deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | long i; | |
| 2 | int j = 2; | |
| 3 | int k; | |
| 4 | ||
| 5 | void incr_i() { | |
| 6 | i++; | |
| 7 | } |
test/standalone/link_common_symbols/build.zig deleted-16| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"}); | |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | ||
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 12 | test_exe.linkLibrary(lib_a); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test it"); | |
| 15 | test_step.dependOn(&test_exe.step); | |
| 16 | } |
test/standalone/link_common_symbols/c.c deleted-5| ... | ... | @@ -1,5 +0,0 @@ |
| 1 | extern int k; | |
| 2 | ||
| 3 | int common_defined_externally() { | |
| 4 | return k; | |
| 5 | } |
test/standalone/link_common_symbols/main.zig deleted-16| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | extern fn common_defined_externally() c_int; | |
| 5 | extern fn incr_i() void; | |
| 6 | extern fn add_to_i_and_j(x: c_int) c_int; | |
| 7 | ||
| 8 | test "undef shadows common symbol: issue #9937" { | |
| 9 | try expect(common_defined_externally() == 0); | |
| 10 | } | |
| 11 | ||
| 12 | test "import C common symbols" { | |
| 13 | incr_i(); | |
| 14 | const res = add_to_i_and_j(2); | |
| 15 | try expect(res == 5); | |
| 16 | } |
test/standalone/link_common_symbols_alignment/a.c deleted-2| ... | ... | @@ -1,2 +0,0 @@ |
| 1 | int foo; | |
| 2 | __attribute__((aligned(4096))) int bar; |
test/standalone/link_common_symbols_alignment/build.zig deleted-16| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | ||
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 12 | test_exe.linkLibrary(lib_a); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test it"); | |
| 15 | test_step.dependOn(&test_exe.step); | |
| 16 | } |
test/standalone/link_common_symbols_alignment/main.zig deleted-9| ... | ... | @@ -1,9 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | extern var foo: i32; | |
| 4 | extern var bar: i32; | |
| 5 | ||
| 6 | test { | |
| 7 | try std.testing.expect(@ptrToInt(&foo) % 4 == 0); | |
| 8 | try std.testing.expect(@ptrToInt(&bar) % 4096 == 0); | |
| 9 | } |
test/standalone/link_frameworks/build.zig deleted-34| ... | ... | @@ -1,34 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | const CrossTarget = std.zig.CrossTarget; | |
| 4 | ||
| 5 | fn isRunnableTarget(t: CrossTarget) bool { | |
| 6 | // TODO I think we might be able to run this on Linux via Darling. | |
| 7 | // Add a check for that here, and return true if Darling is available. | |
| 8 | if (t.isNative() and t.getOsTag() == .macos) | |
| 9 | return true | |
| 10 | else | |
| 11 | return false; | |
| 12 | } | |
| 13 | ||
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 16 | const target = b.standardTargetOptions(.{}); | |
| 17 | ||
| 18 | const test_step = b.step("test", "Test the program"); | |
| 19 | ||
| 20 | const exe = b.addExecutable("test", null); | |
| 21 | b.default_step.dependOn(&exe.step); | |
| 22 | exe.addCSourceFile("main.c", &[0][]const u8{}); | |
| 23 | exe.setBuildMode(mode); | |
| 24 | exe.setTarget(target); | |
| 25 | exe.linkLibC(); | |
| 26 | // TODO when we figure out how to ship framework stubs for cross-compilation, | |
| 27 | // populate paths to the sysroot here. | |
| 28 | exe.linkFramework("Cocoa"); | |
| 29 | ||
| 30 | if (isRunnableTarget(target)) { | |
| 31 | const run_cmd = exe.run(); | |
| 32 | test_step.dependOn(&run_cmd.step); | |
| 33 | } | |
| 34 | } |
test/standalone/link_frameworks/main.c deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <objc/runtime.h> | |
| 3 | ||
| 4 | int main() { | |
| 5 | assert(objc_getClass("NSObject") > 0); | |
| 6 | assert(objc_getClass("NSApplication") > 0); | |
| 7 | } |
test/standalone/link_import_tls_dylib/a.c deleted-1| ... | ... | @@ -1 +0,0 @@ |
| 1 | _Thread_local int a; |
test/standalone/link_import_tls_dylib/build.zig deleted-16| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 7 | lib.setBuildMode(mode); | |
| 8 | lib.addCSourceFile("a.c", &.{}); | |
| 9 | ||
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 12 | test_exe.linkLibrary(lib); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test it"); | |
| 15 | test_step.dependOn(&test_exe.step); | |
| 16 | } |
test/standalone/link_import_tls_dylib/main.zig deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | extern threadlocal var a: i32; | |
| 4 | ||
| 5 | test { | |
| 6 | try std.testing.expect(a == 0); | |
| 7 | } |
test/standalone/link_interdependent_static_c_libs/a.c deleted-4| ... | ... | @@ -1,4 +0,0 @@ |
| 1 | #include "a.h" | |
| 2 | int32_t add(int32_t a, int32_t b) { | |
| 3 | return a + b; | |
| 4 | } |
test/standalone/link_interdependent_static_c_libs/a.h deleted-2| ... | ... | @@ -1,2 +0,0 @@ |
| 1 | #include <stdint.h> | |
| 2 | int32_t add(int32_t a, int32_t b); |
test/standalone/link_interdependent_static_c_libs/b.c deleted-6| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | #include "a.h" | |
| 2 | #include "b.h" | |
| 3 | ||
| 4 | int32_t sub(int32_t a, int32_t b) { | |
| 5 | return add(a, -1 * b); | |
| 6 | } |
test/standalone/link_interdependent_static_c_libs/b.h deleted-2| ... | ... | @@ -1,2 +0,0 @@ |
| 1 | #include <stdint.h> | |
| 2 | int32_t sub(int32_t a, int32_t b); |
test/standalone/link_interdependent_static_c_libs/build.zig deleted-24| ... | ... | @@ -1,24 +0,0 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | lib_a.addIncludePath("."); | |
| 10 | ||
| 11 | const lib_b = b.addStaticLibrary("b", null); | |
| 12 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | |
| 13 | lib_b.setBuildMode(mode); | |
| 14 | lib_b.addIncludePath("."); | |
| 15 | ||
| 16 | const test_exe = b.addTest("main.zig"); | |
| 17 | test_exe.setBuildMode(mode); | |
| 18 | test_exe.linkLibrary(lib_a); | |
| 19 | test_exe.linkLibrary(lib_b); | |
| 20 | test_exe.addIncludePath("."); | |
| 21 | ||
| 22 | const test_step = b.step("test", "Test it"); | |
| 23 | test_step.dependOn(&test_exe.step); | |
| 24 | } |
test/standalone/link_interdependent_static_c_libs/main.zig deleted-8| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const c = @cImport(@cInclude("b.h")); | |
| 4 | ||
| 5 | test "import C sub" { | |
| 6 | const result = c.sub(2, 1); | |
| 7 | try expect(result == 1); | |
| 8 | } |
test/standalone/link_static_lib_as_system_lib/a.c deleted-4| ... | ... | @@ -1,4 +0,0 @@ |
| 1 | #include "a.h" | |
| 2 | int32_t add(int32_t a, int32_t b) { | |
| 3 | return a + b; | |
| 4 | } |
test/standalone/link_static_lib_as_system_lib/a.h deleted-2| ... | ... | @@ -1,2 +0,0 @@ |
| 1 | #include <stdint.h> | |
| 2 | int32_t add(int32_t a, int32_t b); |
test/standalone/link_static_lib_as_system_lib/build.zig deleted-23| ... | ... | @@ -1,23 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const lib_a = b.addStaticLibrary("a", null); | |
| 8 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | |
| 9 | lib_a.setBuildMode(mode); | |
| 10 | lib_a.addIncludePath("."); | |
| 11 | lib_a.install(); | |
| 12 | ||
| 13 | const test_exe = b.addTest("main.zig"); | |
| 14 | test_exe.setBuildMode(mode); | |
| 15 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la | |
| 16 | test_exe.addSystemIncludePath("."); | |
| 17 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; | |
| 18 | test_exe.addLibraryPath(search_path); | |
| 19 | ||
| 20 | const test_step = b.step("test", "Test it"); | |
| 21 | test_step.dependOn(b.getInstallStep()); | |
| 22 | test_step.dependOn(&test_exe.step); | |
| 23 | } |
test/standalone/link_static_lib_as_system_lib/main.zig deleted-8| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const c = @cImport(@cInclude("a.h")); | |
| 4 | ||
| 5 | test "import C add" { | |
| 6 | const result = c.add(2, 1); | |
| 7 | try expect(result == 3); | |
| 8 | } |
test/standalone/objc/Foo.h deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | #import <Foundation/Foundation.h> | |
| 2 | ||
| 3 | @interface Foo : NSObject | |
| 4 | ||
| 5 | - (NSString *)name; | |
| 6 | ||
| 7 | @end |
test/standalone/objc/Foo.m deleted-11| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | #import "Foo.h" | |
| 2 | ||
| 3 | @implementation Foo | |
| 4 | ||
| 5 | - (NSString *)name | |
| 6 | { | |
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | |
| 8 | return str; | |
| 9 | } | |
| 10 | ||
| 11 | @end |
test/standalone/objc/build.zig deleted-36| ... | ... | @@ -1,36 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | const CrossTarget = std.zig.CrossTarget; | |
| 4 | ||
| 5 | fn isRunnableTarget(t: CrossTarget) bool { | |
| 6 | // TODO I think we might be able to run this on Linux via Darling. | |
| 7 | // Add a check for that here, and return true if Darling is available. | |
| 8 | if (t.isNative() and t.getOsTag() == .macos) | |
| 9 | return true | |
| 10 | else | |
| 11 | return false; | |
| 12 | } | |
| 13 | ||
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 16 | const target = b.standardTargetOptions(.{}); | |
| 17 | ||
| 18 | const test_step = b.step("test", "Test the program"); | |
| 19 | ||
| 20 | const exe = b.addExecutable("test", null); | |
| 21 | b.default_step.dependOn(&exe.step); | |
| 22 | exe.addIncludePath("."); | |
| 23 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); | |
| 24 | exe.addCSourceFile("test.m", &[0][]const u8{}); | |
| 25 | exe.setBuildMode(mode); | |
| 26 | exe.setTarget(target); | |
| 27 | exe.linkLibC(); | |
| 28 | // TODO when we figure out how to ship framework stubs for cross-compilation, | |
| 29 | // populate paths to the sysroot here. | |
| 30 | exe.linkFramework("Foundation"); | |
| 31 | ||
| 32 | if (isRunnableTarget(target)) { | |
| 33 | const run_cmd = exe.run(); | |
| 34 | test_step.dependOn(&run_cmd.step); | |
| 35 | } | |
| 36 | } |
test/standalone/objc/test.m deleted-12| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | #import "Foo.h" | |
| 2 | #import <assert.h> | |
| 3 | ||
| 4 | int main(int argc, char *argv[]) | |
| 5 | { | |
| 6 | @autoreleasepool { | |
| 7 | Foo *foo = [[Foo alloc] init]; | |
| 8 | NSString *result = [foo name]; | |
| 9 | assert([result isEqualToString:@"Zig"]); | |
| 10 | return 0; | |
| 11 | } | |
| 12 | } |
test/standalone/objcpp/Foo.h deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | #import <Foundation/Foundation.h> | |
| 2 | ||
| 3 | @interface Foo : NSObject | |
| 4 | ||
| 5 | - (NSString *)name; | |
| 6 | ||
| 7 | @end |
test/standalone/objcpp/Foo.mm deleted-11| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | #import "Foo.h" | |
| 2 | ||
| 3 | @implementation Foo | |
| 4 | ||
| 5 | - (NSString *)name | |
| 6 | { | |
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | |
| 8 | return str; | |
| 9 | } | |
| 10 | ||
| 11 | @end |
test/standalone/objcpp/build.zig deleted-36| ... | ... | @@ -1,36 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | const CrossTarget = std.zig.CrossTarget; | |
| 4 | ||
| 5 | fn isRunnableTarget(t: CrossTarget) bool { | |
| 6 | // TODO I think we might be able to run this on Linux via Darling. | |
| 7 | // Add a check for that here, and return true if Darling is available. | |
| 8 | if (t.isNative() and t.getOsTag() == .macos) | |
| 9 | return true | |
| 10 | else | |
| 11 | return false; | |
| 12 | } | |
| 13 | ||
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 16 | const target = b.standardTargetOptions(.{}); | |
| 17 | ||
| 18 | const test_step = b.step("test", "Test the program"); | |
| 19 | ||
| 20 | const exe = b.addExecutable("test", null); | |
| 21 | b.default_step.dependOn(&exe.step); | |
| 22 | exe.addIncludePath("."); | |
| 23 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); | |
| 24 | exe.addCSourceFile("test.mm", &[0][]const u8{}); | |
| 25 | exe.setBuildMode(mode); | |
| 26 | exe.setTarget(target); | |
| 27 | exe.linkLibCpp(); | |
| 28 | // TODO when we figure out how to ship framework stubs for cross-compilation, | |
| 29 | // populate paths to the sysroot here. | |
| 30 | exe.linkFramework("Foundation"); | |
| 31 | ||
| 32 | if (isRunnableTarget(target)) { | |
| 33 | const run_cmd = exe.run(); | |
| 34 | test_step.dependOn(&run_cmd.step); | |
| 35 | } | |
| 36 | } |
test/standalone/objcpp/test.mm deleted-14| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | #import "Foo.h" | |
| 2 | #import <assert.h> | |
| 3 | #include <iostream> | |
| 4 | ||
| 5 | int main(int argc, char *argv[]) | |
| 6 | { | |
| 7 | @autoreleasepool { | |
| 8 | Foo *foo = [[Foo alloc] init]; | |
| 9 | NSString *result = [foo name]; | |
| 10 | std::cout << "Hello from C++ and " << [result UTF8String]; | |
| 11 | assert([result isEqualToString:@"Zig"]); | |
| 12 | return 0; | |
| 13 | } | |
| 14 | } |
test/tests.zig+24-1| ... | ... | @@ -21,6 +21,7 @@ const assemble_and_link = @import("assemble_and_link.zig"); |
| 21 | 21 | const translate_c = @import("translate_c.zig"); |
| 22 | 22 | const run_translated_c = @import("run_translated_c.zig"); |
| 23 | 23 | const gen_h = @import("gen_h.zig"); |
| 24 | const link = @import("link.zig"); | |
| 24 | 25 | |
| 25 | 26 | // Implementations |
| 26 | 27 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; |
| ... | ... | @@ -479,6 +480,27 @@ pub fn addStandaloneTests( |
| 479 | 480 | return cases.step; |
| 480 | 481 | } |
| 481 | 482 | |
| 483 | pub fn addLinkTests( | |
| 484 | b: *build.Builder, | |
| 485 | test_filter: ?[]const u8, | |
| 486 | modes: []const Mode, | |
| 487 | enable_macos_sdk: bool, | |
| 488 | ) *build.Step { | |
| 489 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | |
| 490 | cases.* = StandaloneContext{ | |
| 491 | .b = b, | |
| 492 | .step = b.step("test-link", "Run the linker tests"), | |
| 493 | .test_index = 0, | |
| 494 | .test_filter = test_filter, | |
| 495 | .modes = modes, | |
| 496 | .skip_non_native = true, | |
| 497 | .enable_macos_sdk = enable_macos_sdk, | |
| 498 | .target = .{}, | |
| 499 | }; | |
| 500 | link.addCases(cases); | |
| 501 | return cases.step; | |
| 502 | } | |
| 503 | ||
| 482 | 504 | pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { |
| 483 | 505 | _ = test_filter; |
| 484 | 506 | _ = modes; |
| ... | ... | @@ -973,7 +995,8 @@ pub const StandaloneContext = struct { |
| 973 | 995 | } |
| 974 | 996 | |
| 975 | 997 | if (features.cross_targets and !self.target.isNative()) { |
| 976 | const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{self.target.zigTriple(b.allocator) catch unreachable}) catch unreachable; | |
| 998 | const target_triple = self.target.zigTriple(b.allocator) catch unreachable; | |
| 999 | const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; | |
| 977 | 1000 | zig_args.append(target_arg) catch unreachable; |
| 978 | 1001 | } |
| 979 | 1002 |