| ... | ... | @@ -111,10 +111,9 @@ pub const Extended = union(enum) { |
| 111 | 111 | progress_node: std.Progress.Node, |
| 112 | 112 | ) Step.ExtendedMakeError!void { |
| 113 | 113 | _ = todo; |
| 114 | | _ = step_index; |
| 115 | 114 | _ = maker; |
| 116 | 115 | _ = progress_node; |
| 117 | | @panic("TODO implement another step type"); |
| 116 | std.debug.panic("TODO implement another step type (index {d})", .{step_index}); |
| 118 | 117 | } |
| 119 | 118 | }; |
| 120 | 119 | }; |
| ... | ... | @@ -146,7 +145,7 @@ pub const Inputs = struct { |
| 146 | 145 | .table = .{}, |
| 147 | 146 | }; |
| 148 | 147 | |
| 149 | | pub const Table = std.ArrayHashMapUnmanaged(Cache.Path, Files, Cache.Path.TableAdapter, false); |
| 148 | pub const Table = std.ArrayHashMapUnmanaged(Path, Files, Path.TableAdapter, false); |
| 150 | 149 | /// The special file name "." means any changes inside the directory. |
| 151 | 150 | pub const Files = std.ArrayList([]const u8); |
| 152 | 151 | |
| ... | ... | @@ -205,7 +204,7 @@ pub const MakeError = error{ |
| 205 | 204 | /// Indicates the error is already reported. |
| 206 | 205 | MakeFailed, |
| 207 | 206 | MakeSkipped, |
| 208 | | }; |
| 207 | } || Io.Cancelable; |
| 209 | 208 | |
| 210 | 209 | pub const ExtendedMakeError = MakeError || Allocator.Error; |
| 211 | 210 | |
| ... | ... | @@ -215,7 +214,7 @@ pub fn make( |
| 215 | 214 | progress_node: std.Progress.Node, |
| 216 | 215 | ) MakeError!void { |
| 217 | 216 | const graph = maker.graph; |
| 218 | | const process_arena = graph.arena; // TODO don't leak into the process arena |
| 217 | const arena = graph.arena; // TODO don't leak into the process arena |
| 219 | 218 | const io = graph.io; |
| 220 | 219 | const c = &maker.scanned_config.configuration; |
| 221 | 220 | const conf_step = step_index.ptr(c); |
| ... | ... | @@ -248,6 +247,7 @@ pub fn make( |
| 248 | 247 | s.result_oom = true; |
| 249 | 248 | return error.MakeFailed; |
| 250 | 249 | }, |
| 250 | error.Canceled => |e| return e, |
| 251 | 251 | }; |
| 252 | 252 | |
| 253 | 253 | if (!s.test_results.isSuccess()) { |
| ... | ... | @@ -257,11 +257,11 @@ pub fn make( |
| 257 | 257 | const max_rss = conf_step.max_rss.toBytes(); |
| 258 | 258 | if (max_rss != 0 and s.result_peak_rss > max_rss) { |
| 259 | 259 | if (std.fmt.allocPrint( |
| 260 | | process_arena, |
| 260 | arena, |
| 261 | 261 | "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)", |
| 262 | 262 | .{ s.result_peak_rss, max_rss }, |
| 263 | 263 | )) |msg| { |
| 264 | | s.oomWrap(s.result_error_msgs.append(process_arena, msg)); |
| 264 | s.oomWrap(s.result_error_msgs.append(arena, msg)); |
| 265 | 265 | } else |_| s.result_oom = true; |
| 266 | 266 | } |
| 267 | 267 | } |
| ... | ... | @@ -288,11 +288,12 @@ pub fn reset(step: *Step, gpa: Allocator) void { |
| 288 | 288 | /// Populates `s.result_failed_command`. |
| 289 | 289 | pub fn captureChildProcess( |
| 290 | 290 | s: *Step, |
| 291 | | gpa: Allocator, |
| 291 | maker: *Maker, |
| 292 | 292 | progress_node: std.Progress.Node, |
| 293 | 293 | argv: []const []const u8, |
| 294 | 294 | ) !std.process.RunResult { |
| 295 | | const graph = s.owner.graph; |
| 295 | const gpa = maker.gpa; |
| 296 | const graph = maker.graph; |
| 296 | 297 | const arena = graph.arena; |
| 297 | 298 | const io = graph.io; |
| 298 | 299 | |
| ... | ... | @@ -300,14 +301,14 @@ pub fn captureChildProcess( |
| 300 | 301 | assert(s.result_failed_command == null); |
| 301 | 302 | s.result_failed_command = try std.zig.allocPrintCmd(gpa, .inherit, null, argv); |
| 302 | 303 | |
| 303 | | try handleChildProcUnsupported(s); |
| 304 | | try handleVerbose(s, .inherit, argv); |
| 304 | try handleChildProcUnsupported(s, maker); |
| 305 | try graph.handleVerbose(.inherit, null, argv); |
| 305 | 306 | |
| 306 | 307 | const result = std.process.run(arena, io, .{ |
| 307 | 308 | .argv = argv, |
| 308 | 309 | .environ_map = &graph.environ_map, |
| 309 | 310 | .progress_node = progress_node, |
| 310 | | }) catch |err| return s.fail("failed to run {s}: {t}", .{ argv[0], err }); |
| 311 | }) catch |err| return s.fail(maker, "failed to run {s}: {t}", .{ argv[0], err }); |
| 311 | 312 | |
| 312 | 313 | if (result.stderr.len > 0) { |
| 313 | 314 | try s.result_error_msgs.append(arena, result.stderr); |
| ... | ... | @@ -316,7 +317,9 @@ pub fn captureChildProcess( |
| 316 | 317 | return result; |
| 317 | 318 | } |
| 318 | 319 | |
| 319 | | pub fn fail(step: *Step, maker: *const Maker, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { |
| 320 | pub const FailError = error{ OutOfMemory, MakeFailed }; |
| 321 | |
| 322 | pub fn fail(step: *Step, maker: *const Maker, comptime fmt: []const u8, args: anytype) FailError { |
| 320 | 323 | try step.addError(maker, fmt, args); |
| 321 | 324 | return error.MakeFailed; |
| 322 | 325 | } |
| ... | ... | @@ -351,15 +354,16 @@ pub const ZigProcess = struct { |
| 351 | 354 | /// is the zig compiler - the same version that compiled the build runner. |
| 352 | 355 | /// Populates `s.result_failed_command`. |
| 353 | 356 | pub fn evalZigProcess( |
| 354 | | s: *Step, |
| 357 | step_index: Configuration.Step.Index, |
| 358 | maker: *Maker, |
| 355 | 359 | argv: []const []const u8, |
| 356 | 360 | prog_node: std.Progress.Node, |
| 357 | 361 | watch: bool, |
| 358 | | maker: *Maker, |
| 359 | | ) !?Cache.Path { |
| 362 | ) (Step.ExtendedMakeError || error{NeedCompileErrorCheck})!?Path { |
| 363 | const s = maker.stepByIndex(step_index); |
| 360 | 364 | const gpa = maker.gpa; |
| 361 | | const b = s.owner; |
| 362 | | const io = b.graph.io; |
| 365 | const graph = maker.graph; |
| 366 | const io = graph.io; |
| 363 | 367 | |
| 364 | 368 | // If an error occurs, it's happened in this command: |
| 365 | 369 | assert(s.result_failed_command == null); |
| ... | ... | @@ -371,36 +375,33 @@ pub fn evalZigProcess( |
| 371 | 375 | zp.progress_ipc_index = null; |
| 372 | 376 | var exited = false; |
| 373 | 377 | defer if (exited) { |
| 374 | | s.cast(Compile).?.zig_process = null; |
| 378 | s.extended.compile.zig_process = null; |
| 375 | 379 | zp.deinit(io); |
| 376 | 380 | gpa.destroy(zp); |
| 377 | 381 | } else zp.saveState(prog_node); |
| 378 | | const result = zigProcessUpdate(s, zp, watch, maker) catch |err| switch (err) { |
| 382 | const result = zigProcessUpdate(step_index, maker, zp, watch) catch |err| switch (err) { |
| 379 | 383 | error.BrokenPipe, error.EndOfStream => |reason| { |
| 380 | | std.log.info("{s} restart required: {t}", .{ argv[0], reason }); |
| 381 | 384 | // Process restart required. |
| 382 | | const term = zp.child.wait(io) catch |e| { |
| 383 | | return s.fail("unable to wait for {s}: {t}", .{ argv[0], e }); |
| 384 | | }; |
| 385 | | _ = term; |
| 385 | std.log.info("{s} restart required: {t}", .{ argv[0], reason }); |
| 386 | _ = zp.child.wait(io) catch |e| return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], e }); |
| 386 | 387 | exited = true; |
| 387 | 388 | break :update; |
| 388 | 389 | }, |
| 389 | | else => |e| return e, |
| 390 | error.OutOfMemory, error.Canceled, error.MakeFailed => |e| return e, |
| 391 | else => |e| return s.fail(maker, "zig child process monitoring failed: {t}", .{e}), |
| 390 | 392 | }; |
| 391 | 393 | |
| 392 | | if (s.result_error_bundle.errorMessageCount() > 0) { |
| 393 | | return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()}); |
| 394 | | } |
| 394 | if (s.result_error_bundle.errorMessageCount() > 0) |
| 395 | return s.fail(maker, "{d} compilation errors", .{s.result_error_bundle.errorMessageCount()}); |
| 395 | 396 | |
| 396 | 397 | if (s.result_error_msgs.items.len > 0 and result == null) { |
| 397 | 398 | // Crash detected. |
| 398 | 399 | const term = zp.child.wait(io) catch |e| { |
| 399 | | return s.fail("unable to wait for {s}: {t}", .{ argv[0], e }); |
| 400 | return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], e }); |
| 400 | 401 | }; |
| 401 | 402 | s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0; |
| 402 | 403 | exited = true; |
| 403 | | try handleChildProcessTerm(s, term); |
| 404 | try handleChildProcessTerm(s, maker, term); |
| 404 | 405 | return error.MakeFailed; |
| 405 | 406 | } |
| 406 | 407 | |
| ... | ... | @@ -408,31 +409,34 @@ pub fn evalZigProcess( |
| 408 | 409 | } |
| 409 | 410 | assert(argv.len != 0); |
| 410 | 411 | |
| 411 | | try handleChildProcUnsupported(s); |
| 412 | | try handleVerbose(s, .inherit, argv); |
| 412 | try handleChildProcUnsupported(s, maker); |
| 413 | try graph.handleVerbose(.inherit, null, argv); |
| 413 | 414 | |
| 414 | 415 | const zp = try gpa.create(ZigProcess); |
| 415 | 416 | defer if (!watch) gpa.destroy(zp); |
| 416 | 417 | |
| 417 | 418 | zp.child = std.process.spawn(io, .{ |
| 418 | 419 | .argv = argv, |
| 419 | | .environ_map = &b.graph.environ_map, |
| 420 | .environ_map = &graph.environ_map, |
| 420 | 421 | .stdin = .pipe, |
| 421 | 422 | .stdout = .pipe, |
| 422 | 423 | .stderr = .pipe, |
| 423 | 424 | .request_resource_usage_statistics = true, |
| 424 | 425 | .progress_node = prog_node, |
| 425 | | }) catch |err| return s.fail("failed to spawn zig compiler {s}: {t}", .{ argv[0], err }); |
| 426 | }) catch |err| return s.fail(maker, "failed to spawn zig compiler {s}: {t}", .{ argv[0], err }); |
| 426 | 427 | |
| 427 | 428 | zp.multi_reader.init(gpa, io, zp.multi_reader_buffer.toStreams(), &.{ |
| 428 | 429 | zp.child.stdout.?, zp.child.stderr.?, |
| 429 | 430 | }); |
| 430 | | if (watch) s.cast(Compile).?.zig_process = zp; |
| 431 | if (watch) s.extended.compile.zig_process = zp; |
| 431 | 432 | defer if (!watch) zp.deinit(io); |
| 432 | 433 | |
| 433 | 434 | const result = result: { |
| 434 | 435 | defer if (watch) zp.saveState(prog_node); |
| 435 | | break :result try zigProcessUpdate(s, zp, watch, maker); |
| 436 | break :result zigProcessUpdate(step_index, maker, zp, watch) catch |err| switch (err) { |
| 437 | error.OutOfMemory, error.Canceled, error.MakeFailed => |e| return e, |
| 438 | else => |e| return s.fail(maker, "zig child process monitoring failed: {t}", .{e}), |
| 439 | }; |
| 436 | 440 | }; |
| 437 | 441 | |
| 438 | 442 | if (!watch) { |
| ... | ... | @@ -441,56 +445,36 @@ pub fn evalZigProcess( |
| 441 | 445 | zp.child.stdin = null; |
| 442 | 446 | |
| 443 | 447 | const term = zp.child.wait(io) catch |err| { |
| 444 | | return s.fail("unable to wait for {s}: {t}", .{ argv[0], err }); |
| 448 | return s.fail(maker, "unable to wait for {s}: {t}", .{ argv[0], err }); |
| 445 | 449 | }; |
| 446 | 450 | s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0; |
| 447 | 451 | |
| 448 | | // Special handling for Compile step that is expecting compile errors. |
| 449 | | if (s.cast(Compile)) |compile| switch (term) { |
| 450 | | .exited => { |
| 452 | // Special handling for compile step that is expecting compile errors. |
| 453 | const conf = &maker.scanned_config.configuration; |
| 454 | if (term == .exited) switch (step_index.ptr(conf).extended.get(conf.extra)) { |
| 455 | .compile => |compile| if (compile.flags4.expect_errors != .none) { |
| 451 | 456 | // Note that the exit code may be 0 in this case due to the |
| 452 | 457 | // compiler server protocol. |
| 453 | | if (compile.expect_errors != null) { |
| 454 | | return error.NeedCompileErrorCheck; |
| 455 | | } |
| 458 | return error.NeedCompileErrorCheck; |
| 456 | 459 | }, |
| 457 | 460 | else => {}, |
| 458 | 461 | }; |
| 459 | | |
| 460 | | try handleChildProcessTerm(s, term); |
| 462 | try handleChildProcessTerm(s, maker, term); |
| 461 | 463 | } |
| 462 | 464 | |
| 463 | 465 | if (s.result_error_bundle.errorMessageCount() > 0) { |
| 464 | | return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()}); |
| 466 | return s.fail(maker, "{d} compilation errors", .{s.result_error_bundle.errorMessageCount()}); |
| 465 | 467 | } |
| 466 | 468 | |
| 467 | 469 | return result; |
| 468 | 470 | } |
| 469 | 471 | |
| 470 | | /// Wrapper around `Io.Dir.updateFile` that handles verbose and error output. |
| 471 | | pub fn installFile(s: *Step, src_lazy_path: LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus { |
| 472 | | const b = s.owner; |
| 473 | | const io = b.graph.io; |
| 474 | | const src_path = src_lazy_path.getPath3(b, s); |
| 475 | | try handleVerbose(s, .inherit, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path }); |
| 476 | | return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err| |
| 477 | | return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err }); |
| 478 | | } |
| 479 | | |
| 480 | | /// Wrapper around `Io.Dir.createDirPathStatus` that handles verbose and error output. |
| 481 | | pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.CreatePathStatus { |
| 482 | | const b = s.owner; |
| 483 | | const io = b.graph.io; |
| 484 | | try handleVerbose(s, .inherit, &.{ "install", "-d", dest_path }); |
| 485 | | return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err| |
| 486 | | return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err }); |
| 487 | | } |
| 488 | | |
| 489 | | fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Path { |
| 472 | fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *ZigProcess, watch: bool) !?Path { |
| 473 | const s = maker.stepByIndex(step_index); |
| 490 | 474 | const gpa = maker.gpa; |
| 491 | | const b = s.owner; |
| 492 | | const arena = b.allocator; |
| 493 | | const io = b.graph.io; |
| 475 | const graph = maker.graph; |
| 476 | const arena = graph.arena; // TODO don't leak into the process arena |
| 477 | const io = graph.io; |
| 494 | 478 | |
| 495 | 479 | const start_ts = Io.Clock.awake.now(io); |
| 496 | 480 | |
| ... | ... | @@ -522,6 +506,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Pat |
| 522 | 506 | .zig_version => { |
| 523 | 507 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
| 524 | 508 | return s.fail( |
| 509 | maker, |
| 525 | 510 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", |
| 526 | 511 | .{ builtin.zig_version_string, body }, |
| 527 | 512 | ); |
| ... | ... | @@ -538,61 +523,65 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, maker: *Maker) !?Pat |
| 538 | 523 | s.result_cached = emit_digest.flags.cache_hit; |
| 539 | 524 | const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len]; |
| 540 | 525 | result = .{ |
| 541 | | .root_dir = b.cache_root, |
| 542 | | .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), |
| 526 | .root_dir = graph.local_cache_root, |
| 527 | .sub_path = try arena.dupe(u8, "o" ++ Io.Dir.path.sep_str ++ Cache.binToHex(digest.*)), |
| 543 | 528 | }; |
| 544 | 529 | }, |
| 545 | 530 | .file_system_inputs => { |
| 546 | | s.clearWatchInputs(); |
| 531 | clearWatchInputs(s, maker); |
| 532 | const conf = &maker.scanned_config.configuration; |
| 533 | const conf_step = step_index.ptr(conf); |
| 547 | 534 | var it = std.mem.splitScalar(u8, body, 0); |
| 548 | 535 | while (it.next()) |prefixed_path| { |
| 549 | 536 | const prefix_index: std.zig.Server.Message.PathPrefix = @enumFromInt(prefixed_path[0] - 1); |
| 550 | 537 | const sub_path = try arena.dupe(u8, prefixed_path[1..]); |
| 551 | | const sub_path_dirname = std.fs.path.dirname(sub_path) orelse ""; |
| 538 | const sub_path_dirname = Io.Dir.path.dirname(sub_path) orelse ""; |
| 552 | 539 | switch (prefix_index) { |
| 553 | 540 | .cwd => { |
| 554 | | const path: Cache.Path = .{ |
| 555 | | .root_dir = Cache.Directory.cwd(), |
| 541 | const path: Path = .{ |
| 542 | .root_dir = .cwd(), |
| 556 | 543 | .sub_path = sub_path_dirname, |
| 557 | 544 | }; |
| 558 | | try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path)); |
| 545 | try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path)); |
| 559 | 546 | }, |
| 560 | 547 | .zig_lib => zl: { |
| 561 | | if (s.cast(Step.Compile)) |compile| { |
| 562 | | if (compile.zig_lib_dir) |zig_lib_dir| { |
| 563 | | const lp = try zig_lib_dir.join(arena, sub_path); |
| 564 | | try addWatchInput(s, lp); |
| 548 | switch (conf_step.extended.get(conf.extra)) { |
| 549 | .compile => |compile| if (compile.zig_lib_dir.value) |zig_lib_dir| { |
| 550 | const resolved = try maker.resolveLazyPathIndex(arena, zig_lib_dir, step_index); |
| 551 | const appended = try resolved.join(arena, sub_path); |
| 552 | try addWatchInputPath(s, maker, appended); |
| 565 | 553 | break :zl; |
| 566 | | } |
| 554 | }, |
| 555 | else => {}, |
| 567 | 556 | } |
| 568 | | const path: Cache.Path = .{ |
| 569 | | .root_dir = s.owner.graph.zig_lib_directory, |
| 557 | const path: Path = .{ |
| 558 | .root_dir = graph.zig_lib_directory, |
| 570 | 559 | .sub_path = sub_path_dirname, |
| 571 | 560 | }; |
| 572 | | try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path)); |
| 561 | try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path)); |
| 573 | 562 | }, |
| 574 | 563 | .local_cache => { |
| 575 | | const path: Cache.Path = .{ |
| 576 | | .root_dir = b.cache_root, |
| 564 | const path: Path = .{ |
| 565 | .root_dir = graph.local_cache_root, |
| 577 | 566 | .sub_path = sub_path_dirname, |
| 578 | 567 | }; |
| 579 | | try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path)); |
| 568 | try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path)); |
| 580 | 569 | }, |
| 581 | 570 | .global_cache => { |
| 582 | | const path: Cache.Path = .{ |
| 583 | | .root_dir = s.owner.graph.global_cache_root, |
| 571 | const path: Path = .{ |
| 572 | .root_dir = graph.global_cache_root, |
| 584 | 573 | .sub_path = sub_path_dirname, |
| 585 | 574 | }; |
| 586 | | try addWatchInputFromPath(s, path, std.fs.path.basename(sub_path)); |
| 575 | try addWatchInputFromPath(s, maker, path, Io.Dir.path.basename(sub_path)); |
| 587 | 576 | }, |
| 588 | 577 | } |
| 589 | 578 | } |
| 590 | 579 | }, |
| 591 | | .time_report => if (maker.web_server) |ws| { |
| 580 | .time_report => if (maker.web_server) |*ws| { |
| 592 | 581 | const TimeReport = std.zig.Server.Message.TimeReport; |
| 593 | 582 | const tr: *align(1) const TimeReport = @ptrCast(body[0..@sizeOf(TimeReport)]); |
| 594 | 583 | ws.updateTimeReportCompile(.{ |
| 595 | | .compile = s.cast(Step.Compile).?, |
| 584 | .compile_step = step_index, |
| 596 | 585 | .use_llvm = tr.flags.use_llvm, |
| 597 | 586 | .stats = tr.stats, |
| 598 | 587 | .ns_total = @intCast(start_ts.untilNow(io, .awake).toNanoseconds()), |
| ... | ... | @@ -636,46 +625,29 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { |
| 636 | 625 | }; |
| 637 | 626 | } |
| 638 | 627 | |
| 639 | | pub fn handleVerbose( |
| 640 | | s: *Step, |
| 641 | | arena: Allocator, |
| 642 | | cwd: std.process.Child.Cwd, |
| 643 | | opt_env: ?*const std.process.Environ.Map, |
| 644 | | argv: []const []const u8, |
| 645 | | ) error{OutOfMemory}!void { |
| 646 | | const graph = s.graph; |
| 647 | | if (!graph.verbose) return; |
| 648 | | // Intention of verbose is to print all sub-process command lines to |
| 649 | | // stderr before spawning them. |
| 650 | | const text = try std.zig.allocPrintCmd(arena, cwd, if (opt_env) |env| .{ |
| 651 | | .child = env, |
| 652 | | .parent = &graph.environ_map, |
| 653 | | } else null, argv); |
| 654 | | std.log.scoped(.verbose).info("{s}", .{text}); |
| 655 | | } |
| 656 | | |
| 657 | 628 | /// Asserts that the caller has already populated `s.result_failed_command`. |
| 658 | | pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFailed }!void { |
| 629 | pub inline fn handleChildProcUnsupported(s: *Step, maker: *Maker) FailError!void { |
| 630 | assert(s.result_failed_command != null); |
| 659 | 631 | if (!std.process.can_spawn) { |
| 660 | | return s.fail("unable to spawn process: host cannot spawn child processes", .{}); |
| 632 | return s.fail(maker, "unable to spawn process: host cannot spawn child processes", .{}); |
| 661 | 633 | } |
| 662 | 634 | } |
| 663 | 635 | |
| 664 | 636 | /// Asserts that the caller has already populated `s.result_failed_command`. |
| 665 | | pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void { |
| 637 | pub fn handleChildProcessTerm(s: *Step, maker: *Maker, term: std.process.Child.Term) FailError!void { |
| 666 | 638 | assert(s.result_failed_command != null); |
| 667 | 639 | return switch (term) { |
| 668 | | .exited => |code| if (code != 0) s.fail("process exited with error code {d}", .{code}), |
| 669 | | .signal => |sig| s.fail("process terminated with signal {t}", .{sig}), |
| 670 | | .stopped => |sig| s.fail("process stopped with signal {t}", .{sig}), |
| 671 | | .unknown => s.fail("process terminated unexpectedly", .{}), |
| 640 | .exited => |code| if (code != 0) s.fail(maker, "process exited with error code {d}", .{code}), |
| 641 | .signal => |sig| s.fail(maker, "process terminated with signal {t}", .{sig}), |
| 642 | .stopped => |sig| s.fail(maker, "process stopped with signal {t}", .{sig}), |
| 643 | .unknown => s.fail(maker, "process terminated unexpectedly", .{}), |
| 672 | 644 | }; |
| 673 | 645 | } |
| 674 | 646 | |
| 675 | 647 | /// Prefer `cacheHitAndWatch` unless you already added watch inputs |
| 676 | 648 | /// separately from using the cache system. |
| 677 | | pub fn cacheHit(s: *Step, man: *Cache.Manifest) !bool { |
| 678 | | s.result_cached = man.hit() catch |err| return failWithCacheError(s, man, err); |
| 649 | pub fn cacheHit(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool { |
| 650 | s.result_cached = man.hit() catch |err| return failWithCacheError(s, maker, man, err); |
| 679 | 651 | return s.result_cached; |
| 680 | 652 | } |
| 681 | 653 | |
| ... | ... | @@ -683,36 +655,37 @@ pub fn cacheHit(s: *Step, man: *Cache.Manifest) !bool { |
| 683 | 655 | /// the full set of files picked up by the cache manifest. |
| 684 | 656 | /// |
| 685 | 657 | /// Must be accompanied with `writeManifestAndWatch`. |
| 686 | | pub fn cacheHitAndWatch(s: *Step, man: *Cache.Manifest) !bool { |
| 687 | | const is_hit = man.hit() catch |err| return failWithCacheError(s, man, err); |
| 658 | pub fn cacheHitAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !bool { |
| 659 | const is_hit = man.hit() catch |err| return failWithCacheError(s, maker, man, err); |
| 688 | 660 | s.result_cached = is_hit; |
| 689 | 661 | // The above call to hit() populates the manifest with files, so in case of |
| 690 | 662 | // a hit, we need to populate watch inputs. |
| 691 | | if (is_hit) try setWatchInputsFromManifest(s, man); |
| 663 | if (is_hit) try setWatchInputsFromManifest(s, maker, man); |
| 692 | 664 | return is_hit; |
| 693 | 665 | } |
| 694 | 666 | |
| 695 | 667 | fn failWithCacheError( |
| 696 | 668 | s: *Step, |
| 669 | maker: *Maker, |
| 697 | 670 | man: *const Cache.Manifest, |
| 698 | 671 | err: Cache.Manifest.HitError, |
| 699 | 672 | ) error{ OutOfMemory, Canceled, MakeFailed } { |
| 700 | 673 | switch (err) { |
| 701 | 674 | error.CacheCheckFailed => switch (man.diagnostic) { |
| 702 | 675 | .none => unreachable, |
| 703 | | .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail("failed to check cache: {t} {t}", .{ |
| 676 | .manifest_create, .manifest_read, .manifest_lock => |e| return s.fail(maker, "failed to check cache: {t} {t}", .{ |
| 704 | 677 | man.diagnostic, e, |
| 705 | 678 | }), |
| 706 | 679 | .file_open, .file_stat, .file_read, .file_hash => |op| { |
| 707 | 680 | const pp = man.files.keys()[op.file_index].prefixed_path; |
| 708 | 681 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; |
| 709 | | return s.fail("failed to check cache: '{s}{c}{s}' {t} {t}", .{ |
| 710 | | prefix, std.fs.path.sep, pp.sub_path, man.diagnostic, op.err, |
| 682 | return s.fail(maker, "failed to check cache: '{s}{c}{s}' {t} {t}", .{ |
| 683 | prefix, Io.Dir.path.sep, pp.sub_path, man.diagnostic, op.err, |
| 711 | 684 | }); |
| 712 | 685 | }, |
| 713 | 686 | }, |
| 714 | 687 | error.OutOfMemory, error.Canceled => |e| return e, |
| 715 | | error.InvalidFormat => return s.fail("failed to check cache: invalid manifest file format", .{}), |
| 688 | error.InvalidFormat => return s.fail(maker, "failed to check cache: invalid manifest file format", .{}), |
| 716 | 689 | } |
| 717 | 690 | } |
| 718 | 691 | |
| ... | ... | @@ -730,48 +703,48 @@ pub fn writeManifest(s: *Step, man: *Cache.Manifest) !void { |
| 730 | 703 | /// the full set of files picked up by the cache manifest. |
| 731 | 704 | /// |
| 732 | 705 | /// Must be accompanied with `cacheHitAndWatch`. |
| 733 | | pub fn writeManifestAndWatch(s: *Step, man: *Cache.Manifest) !void { |
| 706 | pub fn writeManifestAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !void { |
| 734 | 707 | try writeManifest(s, man); |
| 735 | | try setWatchInputsFromManifest(s, man); |
| 708 | try setWatchInputsFromManifest(s, maker, man); |
| 736 | 709 | } |
| 737 | 710 | |
| 738 | | fn setWatchInputsFromManifest(s: *Step, man: *Cache.Manifest) !void { |
| 739 | | const arena = s.owner.allocator; |
| 711 | fn setWatchInputsFromManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void { |
| 712 | const graph = maker.graph; |
| 713 | const arena = graph.arena; // TODO don't leak into process arena |
| 740 | 714 | const prefixes = man.cache.prefixes(); |
| 741 | | clearWatchInputs(s); |
| 715 | clearWatchInputs(s, maker); |
| 742 | 716 | for (man.files.keys()) |file| { |
| 743 | 717 | // The file path data is freed when the cache manifest is cleaned up at the end of `make`. |
| 744 | 718 | const sub_path = try arena.dupe(u8, file.prefixed_path.sub_path); |
| 745 | | try addWatchInputFromPath(s, .{ |
| 719 | try addWatchInputFromPath(s, maker, .{ |
| 746 | 720 | .root_dir = prefixes[file.prefixed_path.prefix], |
| 747 | | .sub_path = std.fs.path.dirname(sub_path) orelse "", |
| 748 | | }, std.fs.path.basename(sub_path)); |
| 721 | .sub_path = Io.Dir.path.dirname(sub_path) orelse "", |
| 722 | }, Io.Dir.path.basename(sub_path)); |
| 749 | 723 | } |
| 750 | 724 | } |
| 751 | 725 | |
| 752 | 726 | /// For steps that have a single input that never changes when re-running `make`. |
| 753 | | pub fn singleUnchangingWatchInput(step: *Step, lazy_path: LazyPath) Allocator.Error!void { |
| 754 | | if (!step.inputs.populated()) try step.addWatchInput(lazy_path); |
| 727 | pub fn singleUnchangingWatchInput(step: *Step, maker: *Maker, lazy_path: LazyPath) Allocator.Error!void { |
| 728 | if (!step.inputs.populated()) try step.addWatchInput(maker, lazy_path); |
| 755 | 729 | } |
| 756 | 730 | |
| 757 | | pub fn clearWatchInputs(step: *Step) void { |
| 758 | | const gpa = step.owner.allocator; |
| 759 | | step.inputs.clear(gpa); |
| 731 | pub fn clearWatchInputs(step: *Step, maker: *Maker) void { |
| 732 | step.inputs.clear(maker.gpa); |
| 760 | 733 | } |
| 761 | 734 | |
| 762 | 735 | /// Places a *file* dependency on the path. |
| 763 | | pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void { |
| 736 | pub fn addWatchInput(step: *Step, maker: *Maker, lazy_file: LazyPath) Allocator.Error!void { |
| 764 | 737 | switch (lazy_file) { |
| 765 | 738 | .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path), |
| 766 | 739 | .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path), |
| 767 | 740 | .cwd_relative => |path_string| { |
| 768 | | try addWatchInputFromPath(step, .{ |
| 741 | try addWatchInputFromPath(step, maker, .{ |
| 769 | 742 | .root_dir = .{ |
| 770 | 743 | .path = null, |
| 771 | 744 | .handle = Io.Dir.cwd(), |
| 772 | 745 | }, |
| 773 | | .sub_path = std.fs.path.dirname(path_string) orelse "", |
| 774 | | }, std.fs.path.basename(path_string)); |
| 746 | .sub_path = Io.Dir.path.dirname(path_string) orelse "", |
| 747 | }, Io.Dir.path.basename(path_string)); |
| 775 | 748 | }, |
| 776 | 749 | // Nothing to watch because this dependency edge is modeled instead via `dependants`. |
| 777 | 750 | .generated => {}, |
| ... | ... | @@ -780,7 +753,7 @@ pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void { |
| 780 | 753 | |
| 781 | 754 | /// Any changes inside the directory will trigger invalidation. |
| 782 | 755 | /// |
| 783 | | /// See also `addDirectoryWatchInputFromPath` which takes a `Cache.Path` instead. |
| 756 | /// See also `addDirectoryWatchInputFromPath` which takes a `Path` instead. |
| 784 | 757 | /// |
| 785 | 758 | /// Paths derived from this directory should also be manually added via |
| 786 | 759 | /// `addDirectoryWatchInputFromPath` if and only if this function returns |
| ... | ... | @@ -812,15 +785,15 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.E |
| 812 | 785 | /// dependency on `path` is not already accounted for by a `Step` dependency. |
| 813 | 786 | /// In other words, before calling this function, first check that the |
| 814 | 787 | /// `LazyPath` which this `path` is derived from is not `generated`. |
| 815 | | pub fn addDirectoryWatchInputFromPath(step: *Step, path: Cache.Path) !void { |
| 816 | | return addWatchInputFromPath(step, path, "."); |
| 788 | pub fn addDirectoryWatchInputFromPath(step: *Step, maker: *Maker, path: Path) !void { |
| 789 | return addWatchInputFromPath(step, maker, path, "."); |
| 817 | 790 | } |
| 818 | 791 | |
| 819 | | fn addWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void { |
| 820 | | return addWatchInputFromPath(step, .{ |
| 792 | fn addWatchInputFromBuilder(step: *Step, maker: *Maker, package: Package, sub_path: []const u8) !void { |
| 793 | return addWatchInputFromPath(step, maker, .{ |
| 821 | 794 | .root_dir = package.build_root, |
| 822 | | .sub_path = std.fs.path.dirname(sub_path) orelse "", |
| 823 | | }, std.fs.path.basename(sub_path)); |
| 795 | .sub_path = Io.Dir.path.dirname(sub_path) orelse "", |
| 796 | }, Io.Dir.path.basename(sub_path)); |
| 824 | 797 | } |
| 825 | 798 | |
| 826 | 799 | fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void { |
| ... | ... | @@ -830,9 +803,16 @@ fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: [] |
| 830 | 803 | }); |
| 831 | 804 | } |
| 832 | 805 | |
| 833 | | fn addWatchInputFromPath(step: *Step, path: Cache.Path, basename: []const u8) !void { |
| 834 | | const gpa = step.owner.allocator; |
| 835 | | const gop = try step.inputs.table.getOrPut(gpa, path); |
| 806 | fn addWatchInputPath(step: *Step, maker: *Maker, path: Path) Allocator.Error!void { |
| 807 | return addWatchInputFromPath(step, maker, .{ |
| 808 | .root_dir = path.root_dir, |
| 809 | .sub_path = Io.Dir.path.dirname(path.sub_path) orelse "", |
| 810 | }, Io.Dir.path.basename(path.sub_path)); |
| 811 | } |
| 812 | |
| 813 | fn addWatchInputFromPath(step: *Step, maker: *Maker, directory: Path, basename: []const u8) Allocator.Error!void { |
| 814 | const gpa = maker.gpa; |
| 815 | const gop = try step.inputs.table.getOrPut(gpa, directory); |
| 836 | 816 | if (!gop.found_existing) gop.value_ptr.* = .empty; |
| 837 | 817 | try gop.value_ptr.append(gpa, basename); |
| 838 | 818 | } |