Example #1
0
void Tool::ScriptResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    Phase::File const &input) const
{
    Target::BuildRules::BuildRule::shared_ptr const &buildRule = input.buildRule();
    if (buildRule == nullptr || buildRule->script().empty()) {
        fprintf(stderr, "warning: invalid or missing build rule for script\n");
        return;
    }

    std::string inputAbsolutePath = FSUtil::ResolveRelativePath(input.path(), toolContext->workingDirectory());
    std::string inputRelativePath = FSUtil::GetRelativePath(inputAbsolutePath, toolContext->workingDirectory());

    pbxsetting::Value logMessage = pbxsetting::Value::Parse("RuleScriptExecution " + inputRelativePath + " $(variant) $(arch)");

    /*
     * Add the public input file build settings. These can be used within the
     * script or inside the list of output paths.
     */
    pbxsetting::Level level = pbxsetting::Level({
        pbxsetting::Setting::Create("INPUT_FILE_PATH", inputAbsolutePath),
        pbxsetting::Setting::Parse("INPUT_FILE_DIR", "$(INPUT_FILE_PATH:dir)"),
        pbxsetting::Setting::Parse("INPUT_FILE_NAME", "$(INPUT_FILE_PATH:file)"),
        pbxsetting::Setting::Parse("INPUT_FILE_BASE", "$(INPUT_FILE_PATH:base)"),
        pbxsetting::Setting::Parse("INPUT_FILE_SUFFIX", "$(INPUT_FILE_PATH:suffix)"),
        pbxsetting::Setting::Create("INPUT_FILE_REGION_PATH_COMPONENT", input.localization()), // TODO(grp): Verify format of this.
    });

    pbxsetting::Environment ruleEnvironment = environment;
    ruleEnvironment.insertFront(level, false);

    /*
     * Only resolve the output paths once the input paths settings are in the
     * environment. The output paths can use the input path settings.
     */
    std::vector<std::string> outputFiles;
    std::transform(buildRule->outputFiles().begin(), buildRule->outputFiles().end(), std::back_inserter(outputFiles), [&](pbxsetting::Value const &output) -> std::string {
        std::string path = ruleEnvironment.expand(output);
        return FSUtil::ResolveRelativePath(path, toolContext->workingDirectory());
    });

    /*
     * Compute the final environment by adding the standard script levels.
     */
    ruleEnvironment.insertFront(ScriptInputOutputLevel({ inputAbsolutePath }, outputFiles, false), false);
    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.arguments() = { "-c", buildRule->script() };
    invocation.environment() = environmentVariables;
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = { inputAbsolutePath };
    invocation.outputs() = outputFiles;
    invocation.logMessage() = ruleEnvironment.expand(logMessage);
    invocation.showEnvironmentInLog() = true;
    toolContext->invocations().push_back(invocation);
}
Example #2
0
void Tool::ScriptResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    pbxproj::PBX::ShellScriptBuildPhase::shared_ptr const &buildPhase) const
{
    pbxsetting::Level level = pbxsetting::Level({
        pbxsetting::Setting::Create("BuildPhaseName", (!buildPhase->name().empty() ? buildPhase->name() : "Run Script")),
        pbxsetting::Setting::Create("BuildPhaseIdentifier", buildPhase->blueprintIdentifier()),
    });

    pbxsetting::Environment phaseEnvironment = environment;
    phaseEnvironment.insertFront(level, false);

    pbxsetting::Value scriptPath = pbxsetting::Value::Parse("$(TEMP_FILES_DIR)/Script-$(BuildPhaseIdentifier).sh");
    pbxsetting::Value logMessage = pbxsetting::Value::Parse("PhaseScriptExecution $(BuildPhaseName:quote) ") + scriptPath;

    std::vector<std::string> inputFiles;
    std::transform(buildPhase->inputPaths().begin(), buildPhase->inputPaths().end(), std::back_inserter(inputFiles), [&](pbxsetting::Value const &input) -> std::string {
        std::string path = environment.expand(input);
        return FSUtil::ResolveRelativePath(path, toolContext->workingDirectory());
    });

    std::vector<std::string> outputFiles;
    std::transform(buildPhase->outputPaths().begin(), buildPhase->outputPaths().end(), std::back_inserter(outputFiles), [&](pbxsetting::Value const &output) -> std::string {
        std::string path = environment.expand(output);
        return FSUtil::ResolveRelativePath(path, toolContext->workingDirectory());
    });

    std::string scriptFilePath = phaseEnvironment.expand(scriptPath);
    std::string contents = (!buildPhase->shellPath().empty() ? "#!" + buildPhase->shellPath() + "\n" : "") + buildPhase->shellScript();
    Tool::Invocation::AuxiliaryFile scriptFile = Tool::Invocation::AuxiliaryFile(scriptFilePath, contents, true);

    pbxsetting::Environment scriptEnvironment = environment;
    scriptEnvironment.insertFront(ScriptInputOutputLevel(inputFiles, outputFiles, true), false);
    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.arguments() = { "-c", Escape::Shell(scriptFilePath) };
    invocation.environment() = environmentVariables;
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.phonyInputs() = inputFiles; /* User-specified, may not exist. */
    invocation.outputs() = outputFiles;
    invocation.auxiliaryFiles() = { scriptFile };
    invocation.logMessage() = phaseEnvironment.expand(logMessage);
    invocation.showEnvironmentInLog() = buildPhase->showEnvVarsInLog();
    toolContext->invocations().push_back(invocation);
}