| ... | ... | @@ -14,10 +14,14 @@ pub fn main() !void { |
| 14 | 14 | // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, |
| 15 | 15 | // one shot program. We don't need to waste time freeing memory and finding places to squish |
| 16 | 16 | // bytes into. So we free everything all at once at the very end. |
| 17 | | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 18 | | defer arena.deinit(); |
| 17 | var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 18 | defer single_threaded_arena.deinit(); |
| 19 | |
| 20 | var thread_safe_arena: std.heap.ThreadSafeAllocator = .{ |
| 21 | .child_allocator = single_threaded_arena.allocator(), |
| 22 | }; |
| 23 | const allocator = thread_safe_arena.allocator(); |
| 19 | 24 | |
| 20 | | const allocator = arena.allocator(); |
| 21 | 25 | var args = try process.argsAlloc(allocator); |
| 22 | 26 | defer process.argsFree(allocator, args); |
| 23 | 27 | |
| ... | ... | @@ -245,71 +249,127 @@ pub fn main() !void { |
| 245 | 249 | if (builder.validateUserInputDidItFail()) |
| 246 | 250 | usageAndErr(builder, true, stderr_stream); |
| 247 | 251 | |
| 248 | | make(builder, targets.items) catch |err| { |
| 252 | runStepNames(builder, targets.items) catch |err| { |
| 249 | 253 | switch (err) { |
| 250 | 254 | error.UncleanExit => process.exit(1), |
| 251 | | // This error is intended to indicate that the step has already |
| 252 | | // logged an error message and so printing the error return trace |
| 253 | | // here would be unwanted extra information, unless the user opts |
| 254 | | // into it with a debug flag. |
| 255 | | error.StepFailed => process.exit(1), |
| 256 | 255 | else => return err, |
| 257 | 256 | } |
| 258 | 257 | }; |
| 259 | 258 | } |
| 260 | 259 | |
| 261 | | fn make(b: *std.Build, step_names: []const []const u8) !void { |
| 262 | | var wanted_steps = ArrayList(*std.Build.Step).init(b.allocator); |
| 263 | | defer wanted_steps.deinit(); |
| 260 | fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { |
| 261 | var step_stack = ArrayList(*std.Build.Step).init(b.allocator); |
| 262 | defer step_stack.deinit(); |
| 264 | 263 | |
| 265 | 264 | if (step_names.len == 0) { |
| 266 | | try wanted_steps.append(b.default_step); |
| 265 | try step_stack.append(b.default_step); |
| 267 | 266 | } else { |
| 268 | | for (step_names) |step_name| { |
| 267 | try step_stack.resize(step_names.len); |
| 268 | |
| 269 | for (step_names) |step_name, i| { |
| 269 | 270 | const s = b.top_level_steps.get(step_name) orelse { |
| 270 | 271 | std.debug.print("no step named '{s}'. Access the help menu with 'zig build -h'\n", .{step_name}); |
| 271 | 272 | process.exit(1); |
| 272 | 273 | }; |
| 273 | | try wanted_steps.append(&s.step); |
| 274 | step_stack.items[step_names.len - i - 1] = &s.step; |
| 274 | 275 | } |
| 275 | 276 | } |
| 276 | 277 | |
| 277 | | for (wanted_steps.items) |s| { |
| 278 | | checkForDependencyLoop(b, s) catch |err| switch (err) { |
| 278 | const starting_steps = step_stack.items; |
| 279 | for (starting_steps) |s| { |
| 280 | checkForDependencyLoop(b, s, &step_stack) catch |err| switch (err) { |
| 279 | 281 | error.DependencyLoopDetected => return error.UncleanExit, |
| 280 | 282 | else => |e| return e, |
| 281 | 283 | }; |
| 282 | 284 | } |
| 283 | 285 | |
| 284 | | for (wanted_steps.items) |s| { |
| 285 | | try makeOneStep(b, s); |
| 286 | var thread_pool: std.Thread.Pool = undefined; |
| 287 | try thread_pool.init(b.allocator); |
| 288 | defer thread_pool.deinit(); |
| 289 | |
| 290 | { |
| 291 | var wait_group: std.Thread.WaitGroup = .{}; |
| 292 | defer wait_group.wait(); |
| 293 | var i = step_stack.items.len; |
| 294 | |
| 295 | while (i > 0) { |
| 296 | i -= 1; |
| 297 | const step = step_stack.items[i]; |
| 298 | |
| 299 | wait_group.start(); |
| 300 | thread_pool.spawn(workerMakeOneStep, .{ &wait_group, b, step }) catch |
| 301 | @panic("unhandled error"); |
| 302 | } |
| 286 | 303 | } |
| 287 | | } |
| 288 | 304 | |
| 289 | | fn checkForDependencyLoop(b: *std.Build, s: *std.Build.Step) !void { |
| 290 | | if (s.loop_tag == .started) { |
| 291 | | std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); |
| 292 | | return error.DependencyLoopDetected; |
| 305 | var any_failed = false; |
| 306 | |
| 307 | for (step_stack.items) |s| { |
| 308 | switch (s.result) { |
| 309 | .not_done => unreachable, |
| 310 | .success => continue, |
| 311 | .failure => |f| { |
| 312 | any_failed = true; |
| 313 | std.debug.print("{s}: {s}\n", .{ |
| 314 | s.name, @errorName(f.err_code), |
| 315 | }); |
| 316 | }, |
| 317 | } |
| 293 | 318 | } |
| 294 | | s.loop_tag = .started; |
| 295 | 319 | |
| 296 | | for (s.dependencies.items) |dep| { |
| 297 | | checkForDependencyLoop(b, dep) catch |err| { |
| 298 | | if (err == error.DependencyLoopDetected) { |
| 299 | | std.debug.print(" {s}\n", .{s.name}); |
| 320 | if (any_failed) { |
| 321 | process.exit(1); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | fn checkForDependencyLoop( |
| 326 | b: *std.Build, |
| 327 | s: *std.Build.Step, |
| 328 | step_stack: *ArrayList(*std.Build.Step), |
| 329 | ) !void { |
| 330 | switch (s.loop_tag) { |
| 331 | .started => { |
| 332 | std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); |
| 333 | return error.DependencyLoopDetected; |
| 334 | }, |
| 335 | .unstarted => { |
| 336 | s.loop_tag = .started; |
| 337 | |
| 338 | try step_stack.append(s); |
| 339 | |
| 340 | for (s.dependencies.items) |dep| { |
| 341 | checkForDependencyLoop(b, dep, step_stack) catch |err| { |
| 342 | if (err == error.DependencyLoopDetected) { |
| 343 | std.debug.print(" {s}\n", .{s.name}); |
| 344 | } |
| 345 | return err; |
| 346 | }; |
| 300 | 347 | } |
| 301 | | return err; |
| 302 | | }; |
| 348 | |
| 349 | s.loop_tag = .done; |
| 350 | }, |
| 351 | .done => {}, |
| 303 | 352 | } |
| 353 | } |
| 354 | |
| 355 | fn workerMakeOneStep(wg: *std.Thread.WaitGroup, b: *std.Build, s: *std.Build.Step) void { |
| 356 | defer wg.finish(); |
| 357 | |
| 358 | _ = b; |
| 304 | 359 | |
| 305 | | s.loop_tag = .done; |
| 360 | if (s.make()) |_| { |
| 361 | s.result = .success; |
| 362 | } else |err| { |
| 363 | s.result = .{ .failure = .{ |
| 364 | .err_code = err, |
| 365 | } }; |
| 366 | } |
| 306 | 367 | } |
| 307 | 368 | |
| 308 | 369 | fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { |
| 309 | 370 | for (s.dependencies.items) |dep| { |
| 310 | 371 | try makeOneStep(b, dep); |
| 311 | 372 | } |
| 312 | | |
| 313 | 373 | try s.make(); |
| 314 | 374 | } |
| 315 | 375 | |