make
Function parameters
Parameters
- step:*Step
- options:Step.MakeOptions
Type definitions in this namespace
Types
Functions in this namespace
Functions
- addOutputFileArg
- Provides a file path as a command line argument to the command being run.
- addPrefixedOutputFileArg
- Provides a file path as a command line argument to the command being run.
- addFileArg
- Appends an input file to the command line arguments.
- addPrefixedFileArg
- Appends an input file to the command line arguments prepended with a string.
- addOutputDirectoryArg
- Provides a directory path as a command line argument to the command being run.
- addPrefixedOutputDirectoryArg
- Provides a directory path as a command line argument to the command being run.
- addDepFileOutputArg
- Add a path argument to a dep file (.d) for the child process to write its
- addPrefixedDepFileOutputArg
- Add a prefixed path argument to a dep file (.d) for the child process to
- expectStdErrEqual
- Adds a check for exact stderr match.
- expectStdOutEqual
- Adds a check for exact stdout match as well as a check for exit code 0, if
- addFileInput
- Adds an additional input files that, when modified, indicates that this Run
= .run
Values
- base_id
- = .run
Source
Implementation
fn make(step: *Step, options: Step.MakeOptions) !void {
const prog_node = options.progress_node;
const b = step.owner;
const arena = b.allocator;
const run: *Run = @fieldParentPtr("step", step);
const has_side_effects = run.hasSideEffects();
var argv_list = std.array_list.Managed([]const u8).init(arena);
var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena);
var man = b.graph.cache.obtain();
defer man.deinit();
if (run.env_map) |env_map| {
const KV = struct { []const u8, []const u8 };
var kv_pairs = try std.array_list.Managed(KV).initCapacity(arena, env_map.count());
var iter = env_map.iterator();
while (iter.next()) |entry| {
kv_pairs.appendAssumeCapacity(.{ entry.key_ptr.*, entry.value_ptr.* });
}
std.mem.sortUnstable(KV, kv_pairs.items, {}, struct {
fn lessThan(_: void, kv1: KV, kv2: KV) bool {
const k1 = kv1[0];
const k2 = kv2[0];
if (k1.len != k2.len) return k1.len < k2.len;
for (k1, k2) |c1, c2| {
if (c1 == c2) continue;
return c1 < c2;
}
unreachable; // two keys cannot be equal
}
}.lessThan);
for (kv_pairs.items) |kv| {
man.hash.addBytes(kv[0]);
man.hash.addBytes(kv[1]);
}
}
for (run.argv.items) |arg| {
switch (arg) {
.bytes => |bytes| {
try argv_list.append(bytes);
man.hash.addBytes(bytes);
},
.lazy_path => |file| {
const file_path = file.lazy_path.getPath3(b, step);
try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
man.hash.addBytes(file.prefix);
_ = try man.addFilePath(file_path, null);
},
.decorated_directory => |dd| {
const file_path = dd.lazy_path.getPath3(b, step);
const resolved_arg = b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix });
try argv_list.append(resolved_arg);
man.hash.addBytes(resolved_arg);
},
.artifact => |pa| {
const artifact = pa.artifact;
if (artifact.rootModuleTarget().os.tag == .windows) {
// On Windows we don't have rpaths so we have to add .dll search paths to PATH
run.addPathForDynLibs(artifact);
}
const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;
try argv_list.append(b.fmt("{s}{s}", .{
pa.prefix,
run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
}));
_ = try man.addFile(file_path, null);
},
.output_file, .output_directory => |output| {
man.hash.addBytes(output.prefix);
man.hash.addBytes(output.basename);
// Add a placeholder into the argument list because we need the
// manifest hash to be updated with all arguments before the
// object directory is computed.
try output_placeholders.append(.{
.index = argv_list.items.len,
.tag = arg,
.output = output,
});
_ = try argv_list.addOne();
},
}
}
switch (run.stdin) {
.bytes => |bytes| {
man.hash.addBytes(bytes);
},
.lazy_path => |lazy_path| {
const file_path = lazy_path.getPath2(b, step);
_ = try man.addFile(file_path, null);
},
.none => {},
}
if (run.captured_stdout) |output| {
man.hash.addBytes(output.basename);
}
if (run.captured_stderr) |output| {
man.hash.addBytes(output.basename);
}
hashStdIo(&man.hash, run.stdio);
for (run.file_inputs.items) |lazy_path| {
_ = try man.addFile(lazy_path.getPath2(b, step), null);
}
if (run.cwd) |cwd| {
const cwd_path = cwd.getPath3(b, step);
_ = man.hash.addBytes(try cwd_path.toString(arena));
}
if (!has_side_effects and try step.cacheHitAndWatch(&man)) {
// cache hit, skip running command
const digest = man.final();
try populateGeneratedPaths(
arena,
output_placeholders.items,
run.captured_stdout,
run.captured_stderr,
b.cache_root,
&digest,
);
step.result_cached = true;
return;
}
const dep_output_file = run.dep_output_file orelse {
// We already know the final output paths, use them directly.
const digest = if (has_side_effects)
man.hash.final()
else
man.final();
try populateGeneratedPaths(
arena,
output_placeholders.items,
run.captured_stdout,
run.captured_stderr,
b.cache_root,
&digest,
);
const output_dir_path = "o" ++ fs.path.sep_str ++ &digest;
for (output_placeholders.items) |placeholder| {
const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename });
const output_sub_dir_path = switch (placeholder.tag) {
.output_file => fs.path.dirname(output_sub_path).?,
.output_directory => output_sub_path,
else => unreachable,
};
b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, output_sub_dir_path, @errorName(err),
});
};
const arg_output_path = run.convertPathArg(.{
.root_dir = .cwd(),
.sub_path = placeholder.output.generated_file.getPath(),
});
argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
arg_output_path
else
b.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
}
try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node, null);
if (!has_side_effects) try step.writeManifestAndWatch(&man);
return;
};
// We do not know the final output paths yet, use temp paths to run the command.
const rand_int = std.crypto.random.int(u64);
const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
for (output_placeholders.items) |placeholder| {
const output_components = .{ tmp_dir_path, placeholder.output.basename };
const output_sub_path = b.pathJoin(&output_components);
const output_sub_dir_path = switch (placeholder.tag) {
.output_file => fs.path.dirname(output_sub_path).?,
.output_directory => output_sub_path,
else => unreachable,
};
b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
return step.fail("unable to make path '{f}{s}': {s}", .{
b.cache_root, output_sub_dir_path, @errorName(err),
});
};
const raw_output_path: Build.Cache.Path = .{
.root_dir = b.cache_root,
.sub_path = b.pathJoin(&output_components),
};
placeholder.output.generated_file.path = raw_output_path.toString(b.graph.arena) catch @panic("OOM");
argv_list.items[placeholder.index] = b.fmt("{s}{s}", .{
placeholder.output.prefix,
run.convertPathArg(raw_output_path),
});
}
try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);
const dep_file_dir = std.fs.cwd();
const dep_file_basename = dep_output_file.generated_file.getPath2(b, step);
if (has_side_effects)
try man.addDepFile(dep_file_dir, dep_file_basename)
else
try man.addDepFilePost(dep_file_dir, dep_file_basename);
const digest = if (has_side_effects)
man.hash.final()
else
man.final();
const any_output = output_placeholders.items.len > 0 or
run.captured_stdout != null or run.captured_stderr != null;
// Rename into place
if (any_output) {
const o_sub_path = "o" ++ fs.path.sep_str ++ &digest;
b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |err| {
if (err == error.PathAlreadyExists) {
b.cache_root.handle.deleteTree(o_sub_path) catch |del_err| {
return step.fail("unable to remove dir '{f}'{s}: {s}", .{
b.cache_root,
tmp_dir_path,
@errorName(del_err),
});
};
b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |retry_err| {
return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {s}", .{
b.cache_root, tmp_dir_path,
b.cache_root, o_sub_path,
@errorName(retry_err),
});
};
} else {
return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {s}", .{
b.cache_root, tmp_dir_path,
b.cache_root, o_sub_path,
@errorName(err),
});
}
};
}
if (!has_side_effects) try step.writeManifestAndWatch(&man);
try populateGeneratedPaths(
arena,
output_placeholders.items,
run.captured_stdout,
run.captured_stderr,
b.cache_root,
&digest,
);
}