caller owns returned memory
fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![]u8
fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![]u8 {
// Detect infinite loops.
var environ_map = try args.environ_map.clone(gpa);
defer environ_map.deinit();
const skip_cc_env_var = if (environ_map.get(inf_loop_env_key)) |phase| blk: {
if (std.mem.eql(u8, phase, "1")) {
try environ_map.put(inf_loop_env_key, "2");
break :blk true;
} else {
return error.ZigIsTheCCompiler;
}
} else blk: {
try environ_map.put(inf_loop_env_key, "1");
break :blk false;
};
var argv = std.array_list.Managed([]const u8).init(gpa);
defer argv.deinit();
const arg1 = try std.fmt.allocPrint(gpa, "-print-file-name={s}", .{args.search_basename});
defer gpa.free(arg1);
try appendCcExe(&argv, skip_cc_env_var, &environ_map);
try argv.append(arg1);
const run_res = std.process.run(gpa, io, .{
.stdout_limit = .limited(1024 * 1024),
.stderr_limit = .limited(1024 * 1024),
.argv = argv.items,
.environ_map = &environ_map,
// Some C compilers, such as Clang, are known to rely on argv[0] to find the path
// to their own executable, without even bothering to resolve PATH. This results in the message:
// error: unable to execute command: Executable "" doesn't exist!
// So we use the expandArg0 variant of ChildProcess to give them a helping hand.
.expand_arg0 = .expand,
}) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => return error.UnableToSpawnCCompiler,
};
defer {
gpa.free(run_res.stdout);
gpa.free(run_res.stderr);
}
switch (run_res.term) {
.exited => |code| if (code != 0) {
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
return error.CCompilerExitCode;
},
else => {
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
return error.CCompilerCrashed;
},
}
var it = std.mem.tokenizeAny(u8, run_res.stdout, "\n\r");
const line = it.next() orelse return error.LibCRuntimeNotFound;
// When this command fails, it returns exit code 0 and duplicates the input file name.
// So we detect failure by checking if the output matches exactly the input.
if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
switch (args.want_dirname) {
.full_path => return gpa.dupe(u8, line),
.only_dir => {
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
return gpa.dupe(u8, dirname);
},
}
}