authorgravatar for xq@random-projects.netFelix "xq" Queißner <xq@random-projects.net> 2021-02-26 19:26:06+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-11 10:43:38+03:00
log98941cf27c523cd02e5c1a5fb95418a6f220e396
tree91e7ebc71e24daf243cc6b9fc7a63bcfe84052bf
parent27bd0971bb725fd3166bcee9364d3af868a24b5d

Makes output path stuff more sane.


4 files changed, 45 insertions(+), 353 deletions(-)

lib/std/build.zig+41-22
...@@ -1591,6 +1591,13 @@ pub const LibExeObjStep = struct {...@@ -1591,6 +1591,13 @@ pub const LibExeObjStep = struct {
1591 self.out_lib_filename = self.out_filename;1591 self.out_lib_filename = self.out_filename;
1592 }1592 }
1593 }1593 }
1594 if (self.output_dir != null) {
1595 self.output_lib_path_source.path =
1596 fs.path.join(
1597 self.builder.allocator,
1598 &[_][]const u8{ self.output_dir.?, self.out_lib_filename },
1599 ) catch unreachable;
1600 }
1594 }1601 }
1595 }1602 }
15961603
...@@ -2132,6 +2139,12 @@ pub const LibExeObjStep = struct {...@@ -2132,6 +2139,12 @@ pub const LibExeObjStep = struct {
2132 self.link_objects.append(LinkObject{ .other_step = other }) catch unreachable;2139 self.link_objects.append(LinkObject{ .other_step = other }) catch unreachable;
2133 self.include_dirs.append(IncludeDir{ .other_step = other }) catch unreachable;2140 self.include_dirs.append(IncludeDir{ .other_step = other }) catch unreachable;
21342141
2142 // BUG: The following code introduces a order-of-call dependency:
2143 // var lib = addSharedLibrary(...);
2144 // var exe = addExecutable(...);
2145 // exe.linkLibrary(lib);
2146 // lib.linkSystemLibrary("foobar"); // this will be ignored for exe!
2147
2135 // Inherit dependency on system libraries2148 // Inherit dependency on system libraries
2136 for (other.link_objects.items) |link_object| {2149 for (other.link_objects.items) |link_object| {
2137 switch (link_object) {2150 switch (link_object) {
...@@ -2174,28 +2187,6 @@ pub const LibExeObjStep = struct {...@@ -2174,28 +2187,6 @@ pub const LibExeObjStep = struct {
2174 return error.NeedAnObject;2187 return error.NeedAnObject;
2175 }2188 }
21762189
2177 // Update generated files
2178 self.output_path_source.path =
2179 fs.path.join(
2180 self.builder.allocator,
2181 &[_][]const u8{ self.output_dir.?, self.out_filename },
2182 ) catch unreachable;
2183 self.output_lib_path_source.path =
2184 fs.path.join(
2185 self.builder.allocator,
2186 &[_][]const u8{ self.output_dir.?, self.out_lib_filename },
2187 ) catch unreachable;
2188 self.output_h_path_source.path =
2189 fs.path.join(
2190 self.builder.allocator,
2191 &[_][]const u8{ self.output_dir.?, self.out_h_filename },
2192 ) catch unreachable;
2193 self.output_pdb_path_source.path =
2194 fs.path.join(
2195 self.builder.allocator,
2196 &[_][]const u8{ self.output_dir.?, self.out_pdb_filename },
2197 ) catch unreachable;
2198
2199 var zig_args = ArrayList([]const u8).init(builder.allocator);2190 var zig_args = ArrayList([]const u8).init(builder.allocator);
2200 defer zig_args.deinit();2191 defer zig_args.deinit();
22012192
...@@ -2711,6 +2702,34 @@ pub const LibExeObjStep = struct {...@@ -2711,6 +2702,34 @@ pub const LibExeObjStep = struct {
2711 }2702 }
2712 }2703 }
27132704
2705 // This will ensure all output filenames will now have the output_dir available!
2706 self.computeOutFileNames();
2707
2708 // Update generated files
2709 if (self.output_dir != null) {
2710 self.output_path_source.path =
2711 fs.path.join(
2712 self.builder.allocator,
2713 &[_][]const u8{ self.output_dir.?, self.out_filename },
2714 ) catch unreachable;
2715
2716 if (self.emit_h) {
2717 self.output_h_path_source.path =
2718 fs.path.join(
2719 self.builder.allocator,
2720 &[_][]const u8{ self.output_dir.?, self.out_h_filename },
2721 ) catch unreachable;
2722 }
2723
2724 if (self.target.isWindows() or self.target.isUefi()) {
2725 self.output_pdb_path_source.path =
2726 fs.path.join(
2727 self.builder.allocator,
2728 &[_][]const u8{ self.output_dir.?, self.out_pdb_filename },
2729 ) catch unreachable;
2730 }
2731 }
2732
2714 if (self.kind == .Lib and self.linkage == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {2733 if (self.kind == .Lib and self.linkage == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
2715 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);2734 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
2716 }2735 }
lib/std/build/RunStep.zig+3-3
...@@ -112,9 +112,9 @@ pub fn addPathDir(self: *RunStep, search_path: []const u8) void {...@@ -112,9 +112,9 @@ pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
112112
113 if (prev_path) |pp| {113 if (prev_path) |pp| {
114 const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });114 const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
115 env_map.set(key, new_path) catch unreachable;115 env_map.put(key, new_path) catch unreachable;
116 } else {116 } else {
117 env_map.set(key, self.builder.dupePath(search_path)) catch unreachable;117 env_map.put(key, self.builder.dupePath(search_path)) catch unreachable;
118 }118 }
119}119}
120120
...@@ -129,7 +129,7 @@ pub fn getEnvMap(self: *RunStep) *BufMap {...@@ -129,7 +129,7 @@ pub fn getEnvMap(self: *RunStep) *BufMap {
129129
130pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {130pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
131 const env_map = self.getEnvMap();131 const env_map = self.getEnvMap();
132 env_map.set(132 env_map.put(
133 self.builder.dupe(key),133 self.builder.dupe(key),
134 self.builder.dupe(value),134 self.builder.dupe(value),
135 ) catch unreachable;135 ) catch unreachable;
lib/std/build/TranslateCStep.zig+1-1
...@@ -89,7 +89,7 @@ fn make(step: *Step) !void {...@@ -89,7 +89,7 @@ fn make(step: *Step) !void {
89 self.output_dir = fs.path.dirname(output_path).?;89 self.output_dir = fs.path.dirname(output_path).?;
90 }90 }
9191
92 self.source.path = fs.path.join(92 self.output_file.path = fs.path.join(
93 self.builder.allocator,93 self.builder.allocator,
94 &[_][]const u8{ self.output_dir.?, self.out_basename },94 &[_][]const u8{ self.output_dir.?, self.out_basename },
95 ) catch unreachable;95 ) catch unreachable;
lib/std/build/run.zig deleted-327
...@@ -1,327 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2021 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("../std.zig");
7const builtin = std.builtin;
8const build = std.build;
9const Step = build.Step;
10const Builder = build.Builder;
11const LibExeObjStep = build.LibExeObjStep;
12const WriteFileStep = build.WriteFileStep;
13const fs = std.fs;
14const mem = std.mem;
15const process = std.process;
16const ArrayList = std.ArrayList;
17const BufMap = std.BufMap;
18const warn = std.debug.warn;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22pub const RunStep = struct {
23 step: Step,
24 builder: *Builder,
25
26 /// See also addArg and addArgs to modifying this directly
27 argv: ArrayList(Arg),
28
29 /// Set this to modify the current working directory
30 cwd: ?[]const u8,
31
32 /// Override this field to modify the environment, or use setEnvironmentVariable
33 env_map: ?*BufMap,
34
35 stdout_action: StdIoAction = .inherit,
36 stderr_action: StdIoAction = .inherit,
37
38 stdin_behavior: std.ChildProcess.StdIo = .Inherit,
39
40 expected_exit_code: u8 = 0,
41
42 pub const StdIoAction = union(enum) {
43 inherit,
44 ignore,
45 expect_exact: []const u8,
46 expect_matches: []const []const u8,
47 };
48
49 pub const Arg = union(enum) {
50 artifact: *LibExeObjStep,
51 file_source: build.FileSource,
52 bytes: []u8,
53 };
54
55 pub fn create(builder: *Builder, name: []const u8) *RunStep {
56 const self = builder.allocator.create(RunStep) catch unreachable;
57 self.* = RunStep{
58 .builder = builder,
59 .step = Step.init(.Run, name, builder.allocator, make),
60 .argv = ArrayList(Arg).init(builder.allocator),
61 .cwd = null,
62 .env_map = null,
63 };
64 return self;
65 }
66
67 pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
68 self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
69 self.step.dependOn(&artifact.step);
70 }
71
72 pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void {
73 self.argv.append(Arg{
74 .file_source = file_source.dupe(self.builder),
75 }) catch unreachable;
76 file_source.addStepDependencies(&self.step);
77 }
78
79 pub fn addArg(self: *RunStep, arg: []const u8) void {
80 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
81 }
82
83 pub fn addArgs(self: *RunStep, args: []const []const u8) void {
84 for (args) |arg| {
85 self.addArg(arg);
86 }
87 }
88
89 pub fn clearEnvironment(self: *RunStep) void {
90 const new_env_map = self.builder.allocator.create(BufMap) catch unreachable;
91 new_env_map.* = BufMap.init(self.builder.allocator);
92 self.env_map = new_env_map;
93 }
94
95 pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
96 const env_map = self.getEnvMap();
97
98 var key: []const u8 = undefined;
99 var prev_path: ?[]const u8 = undefined;
100 if (builtin.os.tag == .windows) {
101 key = "Path";
102 prev_path = env_map.get(key);
103 if (prev_path == null) {
104 key = "PATH";
105 prev_path = env_map.get(key);
106 }
107 } else {
108 key = "PATH";
109 prev_path = env_map.get(key);
110 }
111
112 if (prev_path) |pp| {
113 const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
114 env_map.put(key, new_path) catch unreachable;
115 } else {
116 env_map.put(key, self.builder.dupePath(search_path)) catch unreachable;
117 }
118 }
119
120 pub fn getEnvMap(self: *RunStep) *BufMap {
121 return self.env_map orelse {
122 const env_map = self.builder.allocator.create(BufMap) catch unreachable;
123 env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable;
124 self.env_map = env_map;
125 return env_map;
126 };
127 }
128
129 pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
130 const env_map = self.getEnvMap();
131 // Note: no need to dupe these strings because BufMap does it internally.
132 env_map.put(key, value) catch unreachable;
133 }
134
135 pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
136 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
137 }
138
139 pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
140 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
141 }
142
143 fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
144 return switch (action) {
145 .ignore => .Ignore,
146 .inherit => .Inherit,
147 .expect_exact, .expect_matches => .Pipe,
148 };
149 }
150
151 fn make(step: *Step) !void {
152 const self = @fieldParentPtr(RunStep, "step", step);
153
154 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
155
156 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
157 for (self.argv.items) |arg| {
158 switch (arg) {
159 .bytes => |bytes| try argv_list.append(bytes),
160 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
161 .artifact => |artifact| {
162 if (artifact.target.isWindows()) {
163 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
164 self.addPathForDynLibs(artifact);
165 }
166 const executable_path = artifact.installed_path orelse artifact.getOutputPath();
167 try argv_list.append(executable_path);
168 },
169 }
170 }
171
172 const argv = argv_list.items;
173
174 const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable;
175 defer child.deinit();
176
177 child.cwd = cwd;
178 child.env_map = self.env_map orelse self.builder.env_map;
179
180 child.stdin_behavior = self.stdin_behavior;
181 child.stdout_behavior = stdIoActionToBehavior(self.stdout_action);
182 child.stderr_behavior = stdIoActionToBehavior(self.stderr_action);
183
184 if (self.builder.verbose) {
185 for (argv) |arg| {
186 warn("{s} ", .{arg});
187 }
188 warn("\n", .{});
189 }
190
191 child.spawn() catch |err| {
192 warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
193 return err;
194 };
195
196 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
197
198 var stdout: ?[]const u8 = null;
199 defer if (stdout) |s| self.builder.allocator.free(s);
200
201 switch (self.stdout_action) {
202 .expect_exact, .expect_matches => {
203 stdout = child.stdout.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
204 },
205 .inherit, .ignore => {},
206 }
207
208 var stderr: ?[]const u8 = null;
209 defer if (stderr) |s| self.builder.allocator.free(s);
210
211 switch (self.stderr_action) {
212 .expect_exact, .expect_matches => {
213 stderr = child.stderr.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
214 },
215 .inherit, .ignore => {},
216 }
217
218 const term = child.wait() catch |err| {
219 warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
220 return err;
221 };
222
223 switch (term) {
224 .Exited => |code| {
225 if (code != self.expected_exit_code) {
226 warn("The following command exited with error code {} (expected {}):\n", .{
227 code,
228 self.expected_exit_code,
229 });
230 printCmd(cwd, argv);
231 return error.UncleanExit;
232 }
233 },
234 else => {
235 warn("The following command terminated unexpectedly:\n", .{});
236 printCmd(cwd, argv);
237 return error.UncleanExit;
238 },
239 }
240
241 switch (self.stderr_action) {
242 .inherit, .ignore => {},
243 .expect_exact => |expected_bytes| {
244 if (!mem.eql(u8, expected_bytes, stderr.?)) {
245 warn(
246 \\
247 \\========= Expected this stderr: =========
248 \\{s}
249 \\========= But found: ====================
250 \\{s}
251 \\
252 , .{ expected_bytes, stderr.? });
253 printCmd(cwd, argv);
254 return error.TestFailed;
255 }
256 },
257 .expect_matches => |matches| for (matches) |match| {
258 if (mem.indexOf(u8, stderr.?, match) == null) {
259 warn(
260 \\
261 \\========= Expected to find in stderr: =========
262 \\{s}
263 \\========= But stderr does not contain it: =====
264 \\{s}
265 \\
266 , .{ match, stderr.? });
267 printCmd(cwd, argv);
268 return error.TestFailed;
269 }
270 },
271 }
272
273 switch (self.stdout_action) {
274 .inherit, .ignore => {},
275 .expect_exact => |expected_bytes| {
276 if (!mem.eql(u8, expected_bytes, stdout.?)) {
277 warn(
278 \\
279 \\========= Expected this stdout: =========
280 \\{s}
281 \\========= But found: ====================
282 \\{s}
283 \\
284 , .{ expected_bytes, stdout.? });
285 printCmd(cwd, argv);
286 return error.TestFailed;
287 }
288 },
289 .expect_matches => |matches| for (matches) |match| {
290 if (mem.indexOf(u8, stdout.?, match) == null) {
291 warn(
292 \\
293 \\========= Expected to find in stdout: =========
294 \\{s}
295 \\========= But stdout does not contain it: =====
296 \\{s}
297 \\
298 , .{ match, stdout.? });
299 printCmd(cwd, argv);
300 return error.TestFailed;
301 }
302 },
303 }
304 }
305
306 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
307 if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd});
308 for (argv) |arg| {
309 warn("{s} ", .{arg});
310 }
311 warn("\n", .{});
312 }
313
314 fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
315 for (artifact.link_objects.items) |link_object| {
316 switch (link_object) {
317 .other_step => |other| {
318 if (other.target.isWindows() and other.isDynamicLibrary()) {
319 self.addPathDir(fs.path.dirname(other.getOutputPath()).?);
320 self.addPathForDynLibs(other);
321 }
322 },
323 else => {},
324 }
325 }
326 }
327};