authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-24 00:02:12+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-24 00:02:12+02:00
log291c08f7b0ea4e333c37a0ac378176891f255fa0
treeee9571bf196c1fce5ec9d298dffc103c66b4d3ab
parent87d8cb19e4eed905b93d39554ea9a2a1012f6668
parent03ddb42b8bb96815c1bb4b857ffdfb94191ab861
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11910 from ziglang/linker-tests


82 files changed, 1142 insertions(+), 465 deletions(-)

build.zig+1
......@@ -489,6 +489,7 @@ pub fn build(b: *Builder) !void {
489489
490490 toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
491491 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));
492493 toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
493494 toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes));
494495 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
7676release/bin/zig build docs -Denable-macos-sdk
7777release/bin/zig build test-fmt -Denable-macos-sdk
7878release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded
79release/bin/zig build test-link -Denable-macos-sdk
7980
8081if [ "${BUILD_REASON}" != "PullRequest" ]; then
8182 mv ../LICENSE release/
lib/std/build.zig+13
......@@ -24,6 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig");
2424pub const WriteFileStep = @import("build/WriteFileStep.zig");
2525pub const RunStep = @import("build/RunStep.zig");
2626pub const CheckFileStep = @import("build/CheckFileStep.zig");
27pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
2728pub const InstallRawStep = @import("build/InstallRawStep.zig");
2829pub const OptionsStep = @import("build/OptionsStep.zig");
2930
......@@ -1582,6 +1583,9 @@ pub const LibExeObjStep = struct {
15821583 /// (Darwin) Path to entitlements file
15831584 entitlements: ?[]const u8 = null,
15841585
1586 /// (Darwin) Size of the pagezero segment.
1587 pagezero_size: ?u64 = null,
1588
15851589 /// Position Independent Code
15861590 force_pic: ?bool = null,
15871591
......@@ -1861,6 +1865,10 @@ pub const LibExeObjStep = struct {
18611865 return run_step;
18621866 }
18631867
1868 pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
1869 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
1870 }
1871
18641872 pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
18651873 self.linker_script = source.dupe(self.builder);
18661874 source.addStepDependencies(&self.step);
......@@ -2638,6 +2646,10 @@ pub const LibExeObjStep = struct {
26382646 if (self.entitlements) |entitlements| {
26392647 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
26402648 }
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 }
26412653
26422654 if (self.bundle_compiler_rt) |x| {
26432655 if (x) {
......@@ -3443,6 +3455,7 @@ pub const Step = struct {
34433455 write_file,
34443456 run,
34453457 check_file,
3458 check_object,
34463459 install_raw,
34473460 options,
34483461 custom,
lib/std/build/CheckObjectStep.zig created+392
......@@ -0,0 +1,392 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const build = std.build;
4const fs = std.fs;
5const macho = std.macho;
6const mem = std.mem;
7const testing = std.testing;
8
9const CheckObjectStep = @This();
10
11const Allocator = mem.Allocator;
12const Builder = build.Builder;
13const Step = build.Step;
14
15pub const base_id = .check_obj;
16
17step: Step,
18builder: *Builder,
19source: build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),
22dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,
24
25pub 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
39const 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.
49const 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 +`.
104const 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
114const 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.
139pub 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.
147pub 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.
156pub 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.
168pub 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
190fn 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
276const Opts = struct {
277 gpa: ?Allocator = null,
278 dump_symtab: bool = false,
279};
280
281const 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 {
149149 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
150150
151151 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
152
152153 for (self.argv.items) |arg| {
153154 switch (arg) {
154155 .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
934934 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
935935 }
936936
937 if (self.base.options.entry) |entry| {
938 try argv.append("-e");
939 try argv.append(entry);
940 }
941
937942 try argv.appendSlice(positionals.items);
938943
939944 try argv.append("-o");
......@@ -3371,13 +3376,12 @@ fn addCodeSignatureLC(self: *MachO) !void {
33713376fn setEntryPoint(self: *MachO) !void {
33723377 if (self.base.options.output_mode != .Exe) return;
33733378
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`.
33763379 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{
33783382 .bytes = &self.strtab,
33793383 }) orelse {
3380 log.err("'_main' export not found", .{});
3384 log.err("entrypoint '{s}' not found", .{entry_name});
33813385 return error.MissingMainEntrypoint;
33823386 };
33833387 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
......@@ -5711,28 +5715,46 @@ fn writeDyldInfoData(self: *MachO) !void {
57115715
57125716 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
57135717 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));
57145720 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 });
57185727
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 });
57225736
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 });
57265745
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 });
57305754
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;
57345756
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;
57365758 var buffer = try self.base.allocator.alloc(u8, needed_size);
57375759 defer self.base.allocator.free(buffer);
57385760 mem.set(u8, buffer, 0);
......@@ -5740,14 +5762,15 @@ fn writeDyldInfoData(self: *MachO) !void {
57405762 var stream = std.io.fixedBufferStream(buffer);
57415763 const writer = stream.writer();
57425764
5765 const base_off = dyld_info.rebase_off;
57435766 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);
57455768
57465769 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);
57485771
57495772 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);
57515774
57525775 _ = try trie.write(writer);
57535776
......@@ -5758,7 +5781,7 @@ fn writeDyldInfoData(self: *MachO) !void {
57585781
57595782 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
57605783 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],
57625785 );
57635786 self.load_commands_dirty = true;
57645787}
......@@ -5928,32 +5951,31 @@ fn writeFunctionStarts(self: *MachO) !void {
59285951 } else break;
59295952 }
59305953
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();
59355956
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);
59385959
59395960 for (offsets.items) |offset| {
5940 try std.leb.writeULEB128(writer, offset);
5961 try std.leb.writeULEB128(buffer.writer(), offset);
59415962 }
59425963
5943 const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64)));
59445964 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
59455965 const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data;
59465966
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;
59505972
59515973 log.debug("writing function starts info from 0x{x} to 0x{x}", .{
59525974 fn_cmd.dataoff,
59535975 fn_cmd.dataoff + fn_cmd.datasize,
59545976 });
59555977
5956 try self.base.file.?.pwriteAll(buffer[0..needed_size], fn_cmd.dataoff);
5978 try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff);
59575979 self.load_commands_dirty = true;
59585980}
59595981
......@@ -6001,11 +6023,12 @@ fn writeDices(self: *MachO) !void {
60016023
60026024 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
60036025 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
6004 const needed_size = @intCast(u32, buf.items.len);
60056026
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;
60096032
60106033 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
60116034 dice_cmd.dataoff,
......@@ -6022,7 +6045,8 @@ fn writeSymbolTable(self: *MachO) !void {
60226045
60236046 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
60246047 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);
60266050
60276051 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
60286052 defer locals.deinit();
......@@ -6122,7 +6146,7 @@ fn writeSymbolTable(self: *MachO) !void {
61226146 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
61236147
61246148 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;
61266150
61276151 // Update dynamic symbol table.
61286152 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
......@@ -6142,22 +6166,21 @@ fn writeSymbolTable(self: *MachO) !void {
61426166 const nstubs = @intCast(u32, self.stubs_table.keys().len);
61436167 const ngot_entries = @intCast(u32, self.got_entries_table.keys().len);
61446168
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);
61466171 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
61476172
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;
61506174
61516175 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
61526176 dysymtab.indirectsymoff,
6153 dysymtab.indirectsymoff + needed_size,
6177 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
61546178 });
61556179
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();
61616184
61626185 stubs.reserved1 = 0;
61636186 for (self.stubs_table.keys()) |key| {
......@@ -6191,7 +6214,9 @@ fn writeSymbolTable(self: *MachO) !void {
61916214 }
61926215 }
61936216
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);
61956220 self.load_commands_dirty = true;
61966221}
61976222
......@@ -6201,18 +6226,16 @@ fn writeStringTable(self: *MachO) !void {
62016226
62026227 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
62036228 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;
62076234
62086235 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
62096236
62106237 try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);
62116238
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 }
62166239 self.load_commands_dirty = true;
62176240}
62186241
......@@ -6236,25 +6259,22 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
62366259 const tracy = trace(@src());
62376260 defer tracy.end();
62386261
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;
62416264 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
62426265 // 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));
62486270
62496271 // 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 });
62556275 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
62566276 // 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);
62586278 self.load_commands_dirty = true;
62596279}
62606280
test/link.zig created+64
......@@ -0,0 +1,64 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const tests = @import("tests.zig");
4
5pub 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 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2
3// Stress test zerofill layout
4var buffer: [0x1000000]u64 = undefined;
5
6pub 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 @@
1int i;
2int j;
3
4int 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 @@
1long i;
2int j = 2;
3int k;
4
5void incr_i() {
6 i++;
7}
test/link/common_symbols/build.zig created+16
......@@ -0,0 +1,16 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1extern int k;
2
3int common_defined_externally() {
4 return k;
5}
test/link/common_symbols/main.zig created+16
......@@ -0,0 +1,16 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4extern fn common_defined_externally() c_int;
5extern fn incr_i() void;
6extern fn add_to_i_and_j(x: c_int) c_int;
7
8test "undef shadows common symbol: issue #9937" {
9 try expect(common_defined_externally() == 0);
10}
11
12test "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 @@
1int foo;
2__attribute__((aligned(4096))) int bar;
test/link/common_symbols_alignment/build.zig created+16
......@@ -0,0 +1,16 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2
3extern var foo: i32;
4extern var bar: i32;
5
6test {
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"
2int32_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>
2int32_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
4int32_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>
2int32_t sub(int32_t a, int32_t b);
test/link/interdependent_static_c_libs/build.zig created+24
......@@ -0,0 +1,24 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const c = @cImport(@cInclude("b.h"));
4
5test "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
3char world[] = "world";
4
5char* hello() {
6 return "Hello";
7}
test/link/macho/dylib/build.zig created+49
......@@ -0,0 +1,49 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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
3char* hello();
4extern char world[];
5
6int main() {
7 printf("%s %s", hello(), world);
8 return 0;
9}
test/link/macho/entry/build.zig created+34
......@@ -0,0 +1,34 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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
3int non_main() {
4 printf("%d", 42);
5 return 0;
6}
test/link/macho/frameworks/build.zig created+32
......@@ -0,0 +1,32 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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
4int 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 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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
4int 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 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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
5int 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 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/stack_size/build.zig created+24
......@@ -0,0 +1,24 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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 @@
1int 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"
2int32_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>
2int32_t add(int32_t a, int32_t b);
test/link/static_lib_as_system_lib/build.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const c = @cImport(@cInclude("a.h"));
4
5test "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
3int getA() {
4 return a;
5}
test/link/tls/build.zig created+18
......@@ -0,0 +1,18 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2
3extern threadlocal var a: i32;
4extern fn getA() i32;
5
6fn getA2() i32 {
7 return a;
8}
9
10test {
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 {
1313 cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});
1414 cases.addBuildFile("test/standalone/shared_library/build.zig", .{});
1515 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 });
2920 cases.addBuildFile("test/standalone/global_linkage/build.zig", .{});
3021 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 }
4122 cases.addBuildFile("test/standalone/issue_339/build.zig", .{});
4223 cases.addBuildFile("test/standalone/issue_8550/build.zig", .{});
4324 cases.addBuildFile("test/standalone/issue_794/build.zig", .{});
......@@ -69,16 +50,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
6950 if (builtin.os.tag == .linux) {
7051 cases.addBuildFile("test/standalone/pie/build.zig", .{});
7152 }
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 });
8253
8354 // Ensure the development tools are buildable.
8455 cases.add("tools/gen_spirv_spec.zig");
test/standalone/link_common_symbols/a.c deleted-6
......@@ -1,6 +0,0 @@
1int i;
2int j;
3
4int 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 @@
1long i;
2int j = 2;
3int k;
4
5void incr_i() {
6 i++;
7}
test/standalone/link_common_symbols/build.zig deleted-16
......@@ -1,16 +0,0 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1extern int k;
2
3int common_defined_externally() {
4 return k;
5}
test/standalone/link_common_symbols/main.zig deleted-16
......@@ -1,16 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4extern fn common_defined_externally() c_int;
5extern fn incr_i() void;
6extern fn add_to_i_and_j(x: c_int) c_int;
7
8test "undef shadows common symbol: issue #9937" {
9 try expect(common_defined_externally() == 0);
10}
11
12test "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 @@
1int foo;
2__attribute__((aligned(4096))) int bar;
test/standalone/link_common_symbols_alignment/build.zig deleted-16
......@@ -1,16 +0,0 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2
3extern var foo: i32;
4extern var bar: i32;
5
6test {
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 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const CrossTarget = std.zig.CrossTarget;
4
5fn 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
14pub 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
4int 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 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2
3extern threadlocal var a: i32;
4
5test {
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"
2int32_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>
2int32_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
4int32_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>
2int32_t sub(int32_t a, int32_t b);
test/standalone/link_interdependent_static_c_libs/build.zig deleted-24
......@@ -1,24 +0,0 @@
1const Builder = @import("std").build.Builder;
2
3pub 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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const c = @cImport(@cInclude("b.h"));
4
5test "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"
2int32_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>
2int32_t add(int32_t a, int32_t b);
test/standalone/link_static_lib_as_system_lib/build.zig deleted-23
......@@ -1,23 +0,0 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub 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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const c = @cImport(@cInclude("a.h"));
4
5test "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 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const CrossTarget = std.zig.CrossTarget;
4
5fn 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
14pub 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
4int 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 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const CrossTarget = std.zig.CrossTarget;
4
5fn 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
14pub 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
5int 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");
2121const translate_c = @import("translate_c.zig");
2222const run_translated_c = @import("run_translated_c.zig");
2323const gen_h = @import("gen_h.zig");
24const link = @import("link.zig");
2425
2526// Implementations
2627pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext;
......@@ -479,6 +480,27 @@ pub fn addStandaloneTests(
479480 return cases.step;
480481}
481482
483pub 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
482504pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
483505 _ = test_filter;
484506 _ = modes;
......@@ -973,7 +995,8 @@ pub const StandaloneContext = struct {
973995 }
974996
975997 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;
9771000 zig_args.append(target_arg) catch unreachable;
9781001 }
9791002