Delay resolving executable paths until execution time.

Rather than looking at the filesystem to find an executable path at
configuration time, find the executable at execution time. This allows
different executors to handle searching for paths differently (for
example, using internal or external builtin tools), and avoids using
the filesystem during configuration.
This commit is contained in:
Grant Paul 2016-10-19 16:06:19 -07:00 committed by Grant Paul
parent fd3a151699
commit ef95e55f47
23 changed files with 184 additions and 139 deletions

View File

@ -28,7 +28,6 @@ class Context {
private:
xcsdk::SDK::Target::shared_ptr _sdk;
std::vector<xcsdk::SDK::Toolchain::shared_ptr> _toolchains;
std::vector<std::string> _executablePaths;
std::string _workingDirectory;
private:
@ -49,7 +48,6 @@ public:
Context(
xcsdk::SDK::Target::shared_ptr const &sdk,
std::vector<xcsdk::SDK::Toolchain::shared_ptr> const &toolchains,
std::vector<std::string> const &executablePaths,
std::string const &workingDirectory,
SearchPaths const &searchPaths);
~Context();
@ -59,8 +57,6 @@ public:
{ return _sdk; }
std::vector<xcsdk::SDK::Toolchain::shared_ptr> const &toolchains() const
{ return _toolchains; }
std::vector<std::string> const &executablePaths() const
{ return _executablePaths; }
std::string const &workingDirectory() const
{ return _workingDirectory; }

View File

@ -98,57 +98,50 @@ public:
*/
class Executable {
private:
std::string _path;
std::string _builtin;
ext::optional<std::string> _external;
ext::optional<std::string> _builtin;
public:
Executable(std::string const &path, std::string const &builtin);
private:
Executable(
ext::optional<std::string> const &external,
ext::optional<std::string> const &builtin);
public:
/*
* The path to the executable. If this executable is a bulitin tool, points
* to a standalone executable verison of that tool.
* An external executable, relative or absolute path.
*/
std::string const &path() const
{ return _path; }
ext::optional<std::string> const &external() const
{ return _external; }
/*
* The name of the builtin tool this executable corresponds to.
* A builtin executable name.
*/
std::string const &builtin() const
ext::optional<std::string> const &builtin() const
{ return _builtin; }
public:
/*
* The user-facing name to show for the executable. For builtin tools, this
* uses the shorter name of the bulitin tool rather than the filesystem path.
*/
std::string const &displayName() const;
public:
/*
* Creates an executable from an unknown string. The executable could
* be a builtin tool (starts with "builtin-"), an absolute path to a tool,
* or a relative path to search in the executable paths.
* Creates an executable with a known external path.
*/
static Executable
Determine(std::string const &executable, std::vector<std::string> const &executablePaths);
/*
* Creates an executable with a known absolute path.
*/
static Executable
Absolute(std::string const &path);
External(std::string const &path);
/*
* Creates an executable for a known built-in tool.
*/
static Executable
Builtin(std::string const &name);
/*
* Creates an executable from an unknown string. The executable could
* be a builtin tool (starts with "builtin-") or an external tool path.
*/
static ext::optional<Executable>
Determine(std::string const &executable);
};
private:
Executable _executable;
ext::optional<Executable> _executable;
std::vector<std::string> _arguments;
std::unordered_map<std::string, std::string> _environment;
std::string _workingDirectory;
@ -178,7 +171,7 @@ public:
~Invocation();
public:
Executable const &executable() const
ext::optional<Executable> const &executable() const
{ return _executable; }
std::vector<std::string> const &arguments() const
{ return _arguments; }
@ -188,7 +181,7 @@ public:
{ return _workingDirectory; }
public:
Executable &executable()
ext::optional<Executable> &executable()
{ return _executable; }
std::vector<std::string> &arguments()
{ return _arguments; }

View File

@ -52,7 +52,6 @@ Create(Phase::Environment const &phaseEnvironment, pbxproj::PBX::Target::shared_
Tool::Context toolContext = Tool::Context(
targetEnvironment.sdk(),
targetEnvironment.toolchains(),
targetEnvironment.executablePaths(),
targetEnvironment.workingDirectory(),
searchPaths);

View File

@ -101,7 +101,7 @@ resolve(
* Create the asset catalog invocation.
*/
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = environmentVariables;
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -184,7 +184,7 @@ resolvePrecompiledHeader(
}
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();
@ -282,7 +282,7 @@ resolveSource(
}
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -15,12 +15,10 @@ Tool::Context::
Context(
xcsdk::SDK::Target::shared_ptr const &sdk,
std::vector<xcsdk::SDK::Toolchain::shared_ptr> const &toolchains,
std::vector<std::string> const &executablePaths,
std::string const &workingDirectory,
Tool::SearchPaths const &searchPaths) :
_sdk (sdk),
_toolchains (toolchains),
_executablePaths (executablePaths),
_workingDirectory(workingDirectory),
_searchPaths (searchPaths)
{

View File

@ -42,8 +42,8 @@ ResolveInternal(
* Add the copy-specific build settings.
*/
pbxsetting::Level copyLevel = pbxsetting::Level({
pbxsetting::Setting::Create("PBXCP_STRIP_TOOL", Filesystem::GetDefaultUNSAFE()->findExecutable("strip", toolContext->executablePaths()).value_or(std::string())),
pbxsetting::Setting::Create("PBXCP_BITCODE_STRIP_TOOL", Filesystem::GetDefaultUNSAFE()->findExecutable("bitcode_strip", toolContext->executablePaths()).value_or(std::string())),
// TODO: pbxsetting::Setting::Create("PBXCP_STRIP_TOOL", toolchain->path() + "/usr/bin/strip"),
// TODO: pbxsetting::Setting::Create("PBXCP_BITCODE_STRIP_TOOL", toolchain->path() + "/usr/bin/bitcode_strip"),
pbxsetting::Setting::Create("pbxcp_rule_name", logMessageTitle),
});
@ -91,7 +91,7 @@ ResolveInternal(
* Create the copy invocation.
*/
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = tokens.arguments();
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -29,7 +29,7 @@ resolve(
std::string logMessage = "Ditto " + targetPath + " " + sourcePath;
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Absolute("/usr/bin/ditto"); // TODO(grp): Ditto is not portable.
invocation.executable() = Tool::Invocation::Executable::External("/usr/bin/ditto"); // TODO(grp): Ditto is not portable.
invocation.arguments() = { "-rsrc", sourcePath, targetPath };
invocation.workingDirectory() = toolContext->workingDirectory();
invocation.inputs() = { FSUtil::ResolveRelativePath(sourcePath, toolContext->workingDirectory()) };

View File

@ -63,7 +63,7 @@ resolve(
environmentVariables.insert(buildSettingValues.begin(), buildSettingValues.end());
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = tokens.arguments();
invocation.environment() = environmentVariables;
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -98,7 +98,7 @@ resolve(
* Create the invocation.
*/
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = environmentVariables;
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -80,7 +80,7 @@ resolve(
* Create the invocation.
*/
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = environmentVariables;
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -13,9 +13,9 @@
#include <process/Context.h>
namespace Tool = pbxbuild::Tool;
using AuxiliaryFile = pbxbuild::Tool::Invocation::AuxiliaryFile;
using DependencyInfo = pbxbuild::Tool::Invocation::DependencyInfo;
using Executable = pbxbuild::Tool::Invocation::Executable;
using AuxiliaryFile = Tool::Invocation::AuxiliaryFile;
using DependencyInfo = Tool::Invocation::DependencyInfo;
using Executable = Tool::Invocation::Executable;
using libutil::Filesystem;
using libutil::FSUtil;
@ -67,61 +67,45 @@ DependencyInfo(dependency::DependencyInfoFormat format, std::string const &path)
}
Executable::
Executable(std::string const &path, std::string const &builtin) :
_path (path),
_builtin(builtin)
Executable(
ext::optional<std::string> const &external,
ext::optional<std::string> const &builtin) :
_external(external),
_builtin (builtin)
{
}
std::string const &Executable::
displayName() const
{
/* If the tool is builtin. */
bool builtin = !_builtin.empty();
/* The user-facing name of the executable. For builtin ones, prefer the shorter built-in name. */
return (builtin ? _builtin : _path);
}
Executable Executable::
Determine(std::string const &executable, std::vector<std::string> const &executablePaths)
External(std::string const &path)
{
std::string builtinPrefix = "builtin-";
bool builtin = executable.compare(0, builtinPrefix.size(), builtinPrefix) == 0;
if (builtin) {
/* Has a builtin prefix. */
return Builtin(executable);
} else {
std::string path = executable;
if (!FSUtil::IsAbsolutePath(executable)) {
/* Not absolute, look in the search paths. */
// TODO(grp): Handle when the executable is not found.
path = Filesystem::GetDefaultUNSAFE()->findExecutable(executable, executablePaths).value_or(std::string());
}
return Absolute(path);
}
}
Executable Executable::
Absolute(std::string const &path)
{
return Executable(path, std::string());
return Executable(path, ext::nullopt);
}
Executable Executable::
Builtin(std::string const &name)
{
std::string executableRoot = FSUtil::GetDirectoryName(process::Context::GetDefaultUNSAFE()->executablePath());
std::string path = executableRoot + "/" + name;
return Executable(path, name);
return Executable(ext::nullopt, name);
}
ext::optional<Executable> Executable::
Determine(std::string const &executable)
{
if (executable.empty()) {
return ext::nullopt;
}
std::string builtinPrefix = "builtin-";
if (executable.compare(0, builtinPrefix.size(), builtinPrefix) == 0) {
/* Has a builtin prefix. */
return Builtin(executable);
} else {
/* Unknown external tool. */
return External(executable);
}
}
Tool::Invocation::
Invocation() :
_executable (Executable(std::string(), std::string())),
_showEnvironmentInLog (true),
_createsProductStructure(false)
{

View File

@ -120,7 +120,7 @@ resolve(
}
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -29,7 +29,7 @@ resolve(
std::string logMessage = "MkDir " + directory;
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Absolute("/bin/mkdir");
invocation.executable() = Tool::Invocation::Executable::External("/bin/mkdir");
invocation.arguments() = { "-p", directory };
invocation.workingDirectory() = toolContext->workingDirectory();
invocation.outputs() = { FSUtil::ResolveRelativePath(directory, toolContext->workingDirectory()) };

View File

@ -69,7 +69,7 @@ resolve(
std::string fullWorkingDirectory = FSUtil::ResolveRelativePath(legacyTarget->buildWorkingDirectory(), toolContext->workingDirectory());
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(legacyTarget->buildToolPath(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(legacyTarget->buildToolPath());
invocation.arguments() = pbxsetting::Type::ParseList(script);
invocation.environment() = environmentVariables;
invocation.workingDirectory() = fullWorkingDirectory;
@ -115,7 +115,7 @@ resolve(
std::unordered_map<std::string, std::string> environmentVariables = scriptEnvironment.computeValues(pbxsetting::Condition::Empty());
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Absolute("/bin/sh");
invocation.executable() = Tool::Invocation::Executable::External("/bin/sh");
invocation.arguments() = { "-c", Escape::Shell(scriptFilePath) };
invocation.environment() = environmentVariables;
invocation.workingDirectory() = toolContext->workingDirectory();
@ -177,7 +177,7 @@ resolve(
std::unordered_map<std::string, std::string> environmentVariables = ruleEnvironment.computeValues(pbxsetting::Condition::Empty());
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Absolute("/bin/sh");
invocation.executable() = Tool::Invocation::Executable::External("/bin/sh");
invocation.arguments() = { "-c", buildRule->script() };
invocation.environment() = environmentVariables;
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -395,7 +395,7 @@ resolve(
* Add the invocation.
*/
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = arguments;
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -45,7 +45,7 @@ resolve(
Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = tokens.arguments();
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -31,7 +31,7 @@ resolve(
std::string logMessage = "SymLink " + targetPath + " " + symlinkPath;
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Absolute("/bin/ln");
invocation.executable() = Tool::Invocation::Executable::External("/bin/ln");
invocation.arguments() = { "-sfh", targetPath, symlinkPath };
invocation.workingDirectory() = workingDirectory;
invocation.phonyInputs() = { FSUtil::ResolveRelativePath(targetPath, workingDirectory) };

View File

@ -51,7 +51,7 @@ resolve(
}
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = tokens.arguments();
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();
@ -76,7 +76,7 @@ resolve(
std::string const &resolvedLogMessage = (!logMessage.empty() ? logMessage : tokens.logMessage());
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
invocation.arguments() = tokens.arguments();
invocation.environment() = options.environment();
invocation.workingDirectory() = toolContext->workingDirectory();

View File

@ -42,7 +42,7 @@ resolve(
}
Tool::Invocation invocation;
invocation.executable() = Tool::Invocation::Executable::Absolute("/usr/bin/touch");
invocation.executable() = Tool::Invocation::Executable::External("/usr/bin/touch");
invocation.arguments() = { "-c", input };
invocation.workingDirectory() = toolContext->workingDirectory();
invocation.outputs() = { output };

View File

@ -51,6 +51,7 @@ private:
std::vector<pbxbuild::Tool::Invocation> const &invocations,
std::unordered_set<std::string> *seenDirectories);
bool buildTargetInvocations(
process::Context const *processContext,
libutil::Filesystem *filesystem,
std::string const &dependencyInfoToolPath,
pbxproj::PBX::Target::shared_ptr const &target,
@ -65,6 +66,7 @@ private:
bool buildInvocation(
ninja::Writer *writer,
pbxbuild::Tool::Invocation const &invocation,
std::string const &executablePath,
std::string const &dependencyInfoToolPath,
std::string const &temporaryDirectory,
std::string const &after);

View File

@ -114,6 +114,31 @@ NinjaHash(std::string const &input)
return ss.str();
}
static ext::optional<std::string>
NinjaExecutablePath(
process::Context const *processContext,
Filesystem const *filesystem,
std::vector<std::string> const &executablePaths,
pbxbuild::Tool::Invocation::Executable const &executable)
{
if (ext::optional<std::string> const &builtin = executable.builtin()) {
std::string path = FSUtil::GetDirectoryName(processContext->executablePath()) + "/" + *builtin;
if (filesystem->isExecutable(path)) {
return path;
} else {
return ext::nullopt;
}
} else if (ext::optional<std::string> const &external = executable.external()) {
if (FSUtil::IsAbsolutePath(*external)) {
return *external;
} else {
return filesystem->findExecutable(*external, executablePaths);
}
} else {
abort();
}
}
static std::string
NinjaInvocationPhonyOutput(pbxbuild::Tool::Invocation const &invocation)
{
@ -128,7 +153,18 @@ NinjaInvocationPhonyOutput(pbxbuild::Tool::Invocation const &invocation)
*/
// TODO(grp): Handle identical phony output invocations in a build.
std::string key = invocation.executable().path();
std::string key;
if (invocation.executable()) {
if (invocation.executable()->builtin()) {
key += *invocation.executable()->builtin();
} else if (invocation.executable()->external()) {
key += *invocation.executable()->external();
} else {
abort();
}
}
for (std::string const &arg : invocation.arguments()) {
key += " " + arg;
}
@ -544,7 +580,7 @@ buildAction(
/*
* Write out the Ninja file to build this target.
*/
if (!buildTargetInvocations(filesystem, dependencyInfoToolPath, target, *targetEnvironment, phaseInvocations.invocations())) {
if (!buildTargetInvocations(processContext, filesystem, dependencyInfoToolPath, target, *targetEnvironment, phaseInvocations.invocations())) {
fprintf(stderr, "error: failed to build target ninja\n");
return false;
}
@ -560,7 +596,7 @@ buildAction(
*/
std::unordered_set<std::string> invocationOutputs;
for (pbxbuild::Tool::Invocation const &invocation : phaseInvocations.invocations()) {
if (invocation.executable().path().empty()) {
if (!invocation.executable()) {
/* No outputs. */
continue;
}
@ -629,6 +665,7 @@ buildAction(
bool NinjaExecutor::
buildTargetInvocations(
process::Context const *processContext,
Filesystem *filesystem,
std::string const &dependencyInfoToolPath,
pbxproj::PBX::Target::shared_ptr const &target,
@ -660,10 +697,18 @@ buildTargetInvocations(
}
}
/* Write invocations to run after auxiliary files. */
// TODO(grp): This should perhaps be a separate flag for a 'phony' invocation.
if (!invocation.executable().path().empty()) {
if (!buildInvocation(&writer, invocation, dependencyInfoToolPath, temporaryDirectory, targetWriteAuxiliaryFiles)) {
if (invocation.executable()) {
/* Find invocation executable. */
ext::optional<std::string> executablePath = NinjaExecutablePath(processContext, filesystem, targetEnvironment.executablePaths(), *invocation.executable());
if (!executablePath) {
fprintf(stderr, "unable to find executable: %s\n", invocation.executable()->builtin().value_or(invocation.executable()->external().value_or("<NONE>")).c_str());
return false;
}
/* Write invocations to run after auxiliary files. */
if (!buildInvocation(&writer, invocation, *executablePath, dependencyInfoToolPath, temporaryDirectory, targetWriteAuxiliaryFiles)) {
return false;
}
}
@ -745,6 +790,7 @@ bool NinjaExecutor::
buildInvocation(
ninja::Writer *writer,
pbxbuild::Tool::Invocation const &invocation,
std::string const &executablePath,
std::string const &dependencyInfoToolPath,
std::string const &temporaryDirectory,
std::string const &after)
@ -753,7 +799,7 @@ buildInvocation(
* Build the invocation arguments. Must escape for shell arguments as Ninja passes
* the command string directly to the shell, which would interpret spaces, etc as meaningful.
*/
std::string exec = Escape::Shell(invocation.executable().path());
std::string exec = Escape::Shell(executablePath);
for (std::string const &arg : invocation.arguments()) {
exec += " " + Escape::Shell(arg);
}
@ -774,7 +820,8 @@ buildInvocation(
/*
* Determine the status message for Ninja to print for this invocation.
*/
std::string description = NinjaDescription(_formatter->beginInvocation(invocation, invocation.executable().displayName(), false));
std::string executableDisplayName = invocation.executable()->builtin().value_or(executablePath);
std::string description = NinjaDescription(_formatter->beginInvocation(invocation, executableDisplayName, false));
/*
* Add the dependency info converter & file.

View File

@ -224,19 +224,18 @@ performInvocations(
{
for (pbxbuild::Tool::Invocation const &invocation : orderedInvocations) {
// TODO(grp): This should perhaps be a separate flag for a 'phony' invocation.
if (invocation.executable().path().empty()) {
if (!invocation.executable()) {
continue;
}
pbxbuild::Tool::Invocation::Executable const &executable = *invocation.executable();
if (invocation.createsProductStructure() != createProductStructure) {
continue;
}
std::map<std::string, std::string> sortedEnvironment = std::map<std::string, std::string>(invocation.environment().begin(), invocation.environment().end());
xcformatter::Formatter::Print(_formatter->beginInvocation(invocation, invocation.executable().displayName(), createProductStructure));
if (!_dryRun) {
bool success = true;
for (std::string const &output : invocation.outputs()) {
std::string directory = FSUtil::GetDirectoryName(output);
@ -245,39 +244,66 @@ performInvocations(
}
}
process::MemoryContext context = process::MemoryContext(
invocation.executable().path(),
invocation.workingDirectory(),
invocation.arguments(),
invocation.environment(),
processContext->userID(),
processContext->groupID(),
processContext->userName(),
processContext->groupName());
if (ext::optional<std::string> const &builtin = executable.builtin()) {
/* Builtin tool, find and run in-process. */
if (std::shared_ptr<builtin::Driver> driver = _builtins.driver(*builtin)) {
xcformatter::Formatter::Print(_formatter->beginInvocation(invocation, *builtin, createProductStructure));
if (!invocation.executable().builtin().empty()) {
/* For built-in tools, run them in-process. */
std::shared_ptr<builtin::Driver> driver = _builtins.driver(invocation.executable().builtin());
if (driver == nullptr) {
xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure));
process::MemoryContext context = process::MemoryContext(
*builtin,
invocation.workingDirectory(),
invocation.arguments(),
invocation.environment(),
processContext->userID(),
processContext->groupID(),
processContext->userName(),
processContext->groupName());
success = driver->run(&context, filesystem) != 0;
xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, *builtin, createProductStructure));
} else {
/* Failed to find builtin tool. */
return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation }));
}
} else if (ext::optional<std::string> const &external = executable.external()) {
/* External tool, find on the filesystem. */
ext::optional<std::string> path;
if (FSUtil::IsAbsolutePath(*external)) {
if (filesystem->isExecutable(*external)) {
path = external;
}
} else {
path = filesystem->findExecutable(*external, targetEnvironment.executablePaths());
}
if (driver->run(&context, filesystem) != 0) {
xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure));
if (path) {
xcformatter::Formatter::Print(_formatter->beginInvocation(invocation, *path, createProductStructure));
process::MemoryContext context = process::MemoryContext(
*path,
invocation.workingDirectory(),
invocation.arguments(),
invocation.environment(),
processContext->userID(),
processContext->groupID(),
processContext->userName(),
processContext->groupName());
ext::optional<int> exitCode = processLauncher->launch(filesystem, &context);
success = (exitCode && *exitCode == 0);
xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, *path, createProductStructure));
} else {
/* Failed to find executable. */
return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation }));
}
} else {
/* External tool, run the tool externally. */
ext::optional<int> exitCode = processLauncher->launch(filesystem, &context);
if (!exitCode || *exitCode != 0) {
xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure));
return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation }));
}
abort();
}
if (!success) {
return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation }));
}
}
xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure));
}
return std::make_pair(true, std::vector<pbxbuild::Tool::Invocation>());