| ... | @@ -7,6 +7,7 @@ const mem = std.mem; | ... | @@ -7,6 +7,7 @@ const mem = std.mem; |
| 7 | const process = std.process; | 7 | const process = std.process; |
| 8 | const ArrayList = std.ArrayList; | 8 | const ArrayList = std.ArrayList; |
| 9 | const File = std.fs.File; | 9 | const File = std.fs.File; |
| | 10 | const Step = std.Build.Step; |
| 10 | | 11 | |
| 11 | pub const dependencies = @import("@dependencies"); | 12 | pub const dependencies = @import("@dependencies"); |
| 12 | | 13 | |
| ... | @@ -258,7 +259,7 @@ pub fn main() !void { | ... | @@ -258,7 +259,7 @@ pub fn main() !void { |
| 258 | } | 259 | } |
| 259 | | 260 | |
| 260 | fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { | 261 | fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { |
| 261 | var step_stack = ArrayList(*std.Build.Step).init(b.allocator); | 262 | var step_stack = ArrayList(*Step).init(b.allocator); |
| 262 | defer step_stack.deinit(); | 263 | defer step_stack.deinit(); |
| 263 | | 264 | |
| 264 | if (step_names.len == 0) { | 265 | if (step_names.len == 0) { |
| ... | @@ -290,28 +291,35 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { | ... | @@ -290,28 +291,35 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { |
| 290 | { | 291 | { |
| 291 | var wait_group: std.Thread.WaitGroup = .{}; | 292 | var wait_group: std.Thread.WaitGroup = .{}; |
| 292 | defer wait_group.wait(); | 293 | defer wait_group.wait(); |
| 293 | var i = step_stack.items.len; | | |
| 294 | | 294 | |
| | 295 | // Here we spawn the initial set of tasks with a nice heuristic - |
| | 296 | // dependency order. Each worker when it finishes a step will then |
| | 297 | // check whether it should run any dependants. |
| | 298 | var i = step_stack.items.len; |
| 295 | while (i > 0) { | 299 | while (i > 0) { |
| 296 | i -= 1; | 300 | i -= 1; |
| 297 | const step = step_stack.items[i]; | 301 | const step = step_stack.items[i]; |
| 298 | | 302 | |
| 299 | wait_group.start(); | 303 | wait_group.start(); |
| 300 | thread_pool.spawn(workerMakeOneStep, .{ &wait_group, b, step }) catch | 304 | thread_pool.spawn(workerMakeOneStep, .{ &wait_group, &thread_pool, b, step }) catch |
| 301 | @panic("unhandled error"); | 305 | @panic("OOM"); |
| 302 | } | 306 | } |
| 303 | } | 307 | } |
| 304 | | 308 | |
| 305 | var any_failed = false; | 309 | var any_failed = false; |
| 306 | | 310 | |
| 307 | for (step_stack.items) |s| { | 311 | for (step_stack.items) |s| { |
| 308 | switch (s.result) { | 312 | switch (s.state) { |
| 309 | .not_done => unreachable, | 313 | .precheck_unstarted => unreachable, |
| | 314 | .precheck_started => unreachable, |
| | 315 | .precheck_done => unreachable, |
| | 316 | .running => unreachable, |
| | 317 | .dependency_failure => continue, |
| 310 | .success => continue, | 318 | .success => continue, |
| 311 | .failure => |f| { | 319 | .failure => { |
| 312 | any_failed = true; | 320 | any_failed = true; |
| 313 | std.debug.print("{s}: {s}\n", .{ | 321 | std.debug.print("{s}: {s}\n", .{ |
| 314 | s.name, @errorName(f.err_code), | 322 | s.name, @errorName(s.result.err_code), |
| 315 | }); | 323 | }); |
| 316 | }, | 324 | }, |
| 317 | } | 325 | } |
| ... | @@ -324,20 +332,20 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { | ... | @@ -324,20 +332,20 @@ fn runStepNames(b: *std.Build, step_names: []const []const u8) !void { |
| 324 | | 332 | |
| 325 | fn checkForDependencyLoop( | 333 | fn checkForDependencyLoop( |
| 326 | b: *std.Build, | 334 | b: *std.Build, |
| 327 | s: *std.Build.Step, | 335 | s: *Step, |
| 328 | step_stack: *ArrayList(*std.Build.Step), | 336 | step_stack: *ArrayList(*Step), |
| 329 | ) !void { | 337 | ) !void { |
| 330 | switch (s.loop_tag) { | 338 | switch (s.state) { |
| 331 | .started => { | 339 | .precheck_started => { |
| 332 | std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); | 340 | std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); |
| 333 | return error.DependencyLoopDetected; | 341 | return error.DependencyLoopDetected; |
| 334 | }, | 342 | }, |
| 335 | .unstarted => { | 343 | .precheck_unstarted => { |
| 336 | s.loop_tag = .started; | 344 | s.state = .precheck_started; |
| 337 | | | |
| 338 | try step_stack.append(s); | | |
| 339 | | 345 | |
| 340 | for (s.dependencies.items) |dep| { | 346 | for (s.dependencies.items) |dep| { |
| | 347 | try step_stack.append(dep); |
| | 348 | try dep.dependants.append(b.allocator, s); |
| 341 | checkForDependencyLoop(b, dep, step_stack) catch |err| { | 349 | checkForDependencyLoop(b, dep, step_stack) catch |err| { |
| 342 | if (err == error.DependencyLoopDetected) { | 350 | if (err == error.DependencyLoopDetected) { |
| 343 | std.debug.print(" {s}\n", .{s.name}); | 351 | std.debug.print(" {s}\n", .{s.name}); |
| ... | @@ -346,31 +354,70 @@ fn checkForDependencyLoop( | ... | @@ -346,31 +354,70 @@ fn checkForDependencyLoop( |
| 346 | }; | 354 | }; |
| 347 | } | 355 | } |
| 348 | | 356 | |
| 349 | s.loop_tag = .done; | 357 | s.state = .precheck_done; |
| 350 | }, | 358 | }, |
| 351 | .done => {}, | 359 | .precheck_done => {}, |
| | 360 | |
| | 361 | // These don't happen until we actually run the step graph. |
| | 362 | .dependency_failure => unreachable, |
| | 363 | .running => unreachable, |
| | 364 | .success => unreachable, |
| | 365 | .failure => unreachable, |
| 352 | } | 366 | } |
| 353 | } | 367 | } |
| 354 | | 368 | |
| 355 | fn workerMakeOneStep(wg: *std.Thread.WaitGroup, b: *std.Build, s: *std.Build.Step) void { | 369 | fn workerMakeOneStep( |
| | 370 | wg: *std.Thread.WaitGroup, |
| | 371 | thread_pool: *std.Thread.Pool, |
| | 372 | b: *std.Build, |
| | 373 | s: *Step, |
| | 374 | ) void { |
| 356 | defer wg.finish(); | 375 | defer wg.finish(); |
| 357 | | 376 | |
| 358 | _ = b; | 377 | // First, check the conditions for running this step. If they are not met, |
| | 378 | // then we return without doing the step, relying on another worker to |
| | 379 | // queue this step up again when dependencies are met. |
| | 380 | for (s.dependencies.items) |dep| { |
| | 381 | switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { |
| | 382 | .success => continue, |
| | 383 | .failure, .dependency_failure => { |
| | 384 | @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); |
| | 385 | return; |
| | 386 | }, |
| | 387 | .precheck_done, .running => { |
| | 388 | // dependency is not finished yet. |
| | 389 | return; |
| | 390 | }, |
| | 391 | .precheck_unstarted => unreachable, |
| | 392 | .precheck_started => unreachable, |
| | 393 | } |
| | 394 | } |
| 359 | | 395 | |
| 360 | if (s.make()) |_| { | 396 | // Avoid running steps twice. |
| 361 | s.result = .success; | 397 | if (@cmpxchgStrong(Step.State, &s.state, .precheck_done, .running, .SeqCst, .SeqCst) != null) { |
| 362 | } else |err| { | 398 | // Another worker got the job. |
| 363 | s.result = .{ .failure = .{ | 399 | return; |
| 364 | .err_code = err, | | |
| 365 | } }; | | |
| 366 | } | 400 | } |
| 367 | } | | |
| 368 | | 401 | |
| 369 | fn makeOneStep(b: *std.Build, s: *std.Build.Step) anyerror!void { | 402 | // I suspect we will want to pass `b` to make() in a future modification. |
| 370 | for (s.dependencies.items) |dep| { | 403 | // For example, CompileStep does some sus things with modifying the saved |
| 371 | try makeOneStep(b, dep); | 404 | // *Build object in install header steps that might be able to be removed |
| | 405 | // by passing the *Build object through the make() functions. |
| | 406 | s.make() catch |err| { |
| | 407 | s.result = .{ |
| | 408 | .err_code = err, |
| | 409 | }; |
| | 410 | @atomicStore(Step.State, &s.state, .failure, .SeqCst); |
| | 411 | return; |
| | 412 | }; |
| | 413 | |
| | 414 | @atomicStore(Step.State, &s.state, .success, .SeqCst); |
| | 415 | |
| | 416 | // Successful completion of a step, so we queue up its dependants as well. |
| | 417 | for (s.dependants.items) |dep| { |
| | 418 | wg.start(); |
| | 419 | thread_pool.spawn(workerMakeOneStep, .{ wg, thread_pool, b, dep }) catch @panic("OOM"); |
| 372 | } | 420 | } |
| 373 | try s.make(); | | |
| 374 | } | 421 | } |
| 375 | | 422 | |
| 376 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { | 423 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { |