예제 #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);
}
예제 #2
0
void Tool::ToolResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    std::vector<Phase::File> const &inputs,
    std::string const &outputDirectory,
    std::string const &logMessage) const
{
    Tool::Environment toolEnvironment = Tool::Environment::Create(_tool, environment, toolContext->workingDirectory(), inputs);
    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), nullptr);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);
    std::string const &resolvedLogMessage = (!logMessage.empty() ? logMessage : tokens.logMessage());

    std::vector<Tool::Invocation::DependencyInfo> dependencyInfo;
    if (_tool->deeplyStatInputDirectories()) {
        for (Phase::File const &input : inputs) {
            /* Create a dependency info file to track the input directory contents. */
            auto info = Tool::Invocation::DependencyInfo(dependency::DependencyInfoFormat::Directory, input.path());
            dependencyInfo.push_back(info);
        }
    }

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
    invocation.arguments() = tokens.arguments();
    invocation.environment() = options.environment();
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = toolEnvironment.inputs(toolContext->workingDirectory());
    invocation.outputs() = toolEnvironment.outputs(toolContext->workingDirectory());
    invocation.dependencyInfo() = dependencyInfo;
    invocation.logMessage() = resolvedLogMessage;
    toolContext->invocations().push_back(invocation);
}
void Tool::SwiftStandardLibraryResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &baseEnvironment,
    std::string const &executable,
    std::vector<std::string> const &directories) const
{
    pbxsetting::Level level = pbxsetting::Level({
        pbxsetting::Setting::Create("SWIFT_STDLIB_TOOL_FOLDERS_TO_SCAN", pbxsetting::Type::FormatList(directories)),
    });
    pbxsetting::Environment env = baseEnvironment;
    env.insertFront(level, false);

    std::string outputPath = env.resolve("TARGET_BUILD_DIR") + "/" + env.resolve("FULL_PRODUCT_NAME");

    Tool::Environment toolEnvironment = Tool::Environment::Create(_tool, env, toolContext->workingDirectory(), { executable }, { outputPath });
    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), nullptr);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
    invocation.arguments() = tokens.arguments();
    invocation.environment() = options.environment();
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = toolEnvironment.inputs(toolContext->workingDirectory());
    invocation.outputs() = { }; // TODO(grp): Outputs are not known at build time.
    invocation.logMessage() = tokens.logMessage();
    toolContext->invocations().push_back(invocation);
}
예제 #4
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);
}
예제 #5
0
void Tool::ClangResolver::
resolvePrecompiledHeader(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    Tool::PrecompiledHeaderInfo const &precompiledHeaderInfo
) const
{
    std::string const &input = precompiledHeaderInfo.prefixHeader();
    pbxspec::PBX::FileType::shared_ptr const &fileType = precompiledHeaderInfo.fileType();
    std::string output = environment.expand(precompiledHeaderInfo.compileOutputPath());

    pbxspec::PBX::Tool::shared_ptr tool = std::static_pointer_cast <pbxspec::PBX::Tool> (_compiler);
    Tool::Environment toolEnvironment = Tool::Environment::Create(tool, environment, toolContext->workingDirectory(), { input }, { output });
    pbxsetting::Environment const &env = toolEnvironment.environment();
    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), fileType);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);

    std::vector<std::string> arguments = precompiledHeaderInfo.arguments();
    AppendDependencyInfoFlags(&arguments, _compiler, env);
    AppendInputOutputFlags(&arguments, _compiler, input, output);

    ext::optional<std::string> const &dialect = fileType->GCCDialectName();
    std::string logTitle = DialectIsCPlusPlus(dialect) ? "ProcessPCH++" : "ProcessPCH";
    std::string logMessage = CompileLogMessage(_compiler, logTitle, input, fileType, output, env, toolContext->workingDirectory());

    auto serializedFile = Tool::Invocation::AuxiliaryFile(
        env.expand(precompiledHeaderInfo.serializedOutputPath()),
        precompiledHeaderInfo.serialize(),
        false);

    std::vector<Tool::Invocation::DependencyInfo> dependencyInfo;
    if (_compiler->dependencyInfoFile()) {
        dependencyInfo.push_back(Tool::Invocation::DependencyInfo(
            dependency::DependencyInfoFormat::Makefile,
            env.expand(*_compiler->dependencyInfoFile())));
    }

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
    invocation.arguments() = arguments;
    invocation.environment() = options.environment();
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = toolEnvironment.inputs(toolContext->workingDirectory());
    invocation.outputs() = toolEnvironment.outputs(toolContext->workingDirectory());
    invocation.dependencyInfo() = dependencyInfo;
    invocation.auxiliaryFiles().push_back(serializedFile);
    invocation.logMessage() = logMessage;
    toolContext->invocations().push_back(invocation);
}
예제 #6
0
void Tool::ScriptResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    pbxproj::PBX::LegacyTarget::shared_ptr const &legacyTarget) const
{
    std::string logMessage = "ExternalBuildToolExecution " + legacyTarget->name();

    std::string script = environment.expand(legacyTarget->buildArgumentsString());

    std::unordered_map<std::string, std::string> environmentVariables;
    if (legacyTarget->passBuildSettingsInEnvironment()) {
        environmentVariables = environment.computeValues(pbxsetting::Condition::Empty());
    }

    std::string fullWorkingDirectory = FSUtil::ResolveRelativePath(legacyTarget->buildWorkingDirectory(), toolContext->workingDirectory());

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(legacyTarget->buildToolPath(), toolContext->executablePaths());
    invocation.arguments() = pbxsetting::Type::ParseList(script);
    invocation.environment() = environmentVariables;
    invocation.workingDirectory() = fullWorkingDirectory;
    invocation.logMessage() = logMessage;
    toolContext->invocations().push_back(invocation);
}
예제 #7
0
void Tool::SymlinkResolver::
resolve(
    Tool::Context *toolContext,
    std::string const &workingDirectory,
    std::string const &symlinkPath,
    std::string const &targetPath,
    bool productStructure) const
{
    std::string logMessage = "SymLink " + targetPath + " " + symlinkPath;

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::External("/bin/ln");
    invocation.arguments() = { "-sfh", targetPath, symlinkPath };
    invocation.workingDirectory() = workingDirectory;
    invocation.phonyInputs() = { FSUtil::ResolveRelativePath(targetPath, workingDirectory) };
    invocation.outputs() = { FSUtil::ResolveRelativePath(symlinkPath, workingDirectory) };
    invocation.logMessage() = logMessage;
    invocation.createsProductStructure() = productStructure;
    invocation.priority() = toolContext->currentPhaseInvocationPriority();
    toolContext->invocations().push_back(invocation);
}
예제 #8
0
void Tool::ToolResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    std::vector<std::string> const &inputs,
    std::vector<std::string> const &outputs,
    std::string const &logMessage) const
{
    Tool::Environment toolEnvironment = Tool::Environment::Create(_tool, environment, toolContext->workingDirectory(), inputs, outputs);
    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), nullptr);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);
    std::string const &resolvedLogMessage = (!logMessage.empty() ? logMessage : tokens.logMessage());

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
    invocation.arguments() = tokens.arguments();
    invocation.environment() = options.environment();
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = toolEnvironment.inputs(toolContext->workingDirectory());
    invocation.outputs() = toolEnvironment.outputs(toolContext->workingDirectory());
    invocation.logMessage() = resolvedLogMessage;
    toolContext->invocations().push_back(invocation);
}
예제 #9
0
void Tool::ClangResolver::
resolveSource(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    Phase::File const &input,
    std::string const &outputDirectory) const
{
    Tool::HeadermapInfo const &headermapInfo = toolContext->headermapInfo();

    std::string resolvedOutputDirectory;
    if (_compiler->outputDir()) {
        resolvedOutputDirectory = environment.expand(*_compiler->outputDir());
    } else {
        resolvedOutputDirectory = outputDirectory;
    }

    std::string outputExtension = _compiler->outputFileExtension().value_or("o");

    std::string outputBaseName = FSUtil::GetBaseNameWithoutExtension(input.path());
    if (!input.fileNameDisambiguator().empty()) {
        outputBaseName = input.fileNameDisambiguator();
    }
    std::string output = resolvedOutputDirectory + "/" + outputBaseName + "." + outputExtension;

    pbxspec::PBX::FileType::shared_ptr const &fileType = input.fileType();
    std::vector<std::string> const &inputArguments = input.buildFile()->compilerFlags();

    pbxspec::PBX::Tool::shared_ptr tool = std::static_pointer_cast <pbxspec::PBX::Tool> (_compiler);
    Tool::Environment toolEnvironment = Tool::Environment::Create(tool, environment, toolContext->workingDirectory(), { input }, { output });
    pbxsetting::Environment const &env = toolEnvironment.environment();

    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), fileType);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);

    std::vector<std::string> inputDependencies;
    inputDependencies.insert(inputDependencies.end(), headermapInfo.systemHeadermapFiles().begin(), headermapInfo.systemHeadermapFiles().end());
    inputDependencies.insert(inputDependencies.end(), headermapInfo.userHeadermapFiles().begin(), headermapInfo.userHeadermapFiles().end());

    std::vector<std::string> arguments;
    AppendDialectFlags(&arguments, fileType->GCCDialectName());
    size_t dialectOffset = arguments.size();

    arguments.insert(arguments.end(), tokens.arguments().begin(), tokens.arguments().end());
    Tool::CompilerCommon::AppendIncludePathFlags(&arguments, env, toolContext->searchPaths(), headermapInfo);
    AppendFrameworkPathFlags(&arguments, env, toolContext->searchPaths());
    AppendCustomFlags(&arguments, env, fileType->GCCDialectName());

    bool precompilePrefixHeader = pbxsetting::Type::ParseBoolean(env.resolve("GCC_PRECOMPILE_PREFIX_HEADER"));
    std::string prefixHeader = env.resolve("GCC_PREFIX_HEADER");
    std::shared_ptr<Tool::PrecompiledHeaderInfo> precompiledHeaderInfo = nullptr;

    if (!prefixHeader.empty()) {
        std::string prefixHeaderFile = FSUtil::ResolveRelativePath(prefixHeader, toolContext->workingDirectory());

        if (precompilePrefixHeader) {
            std::vector<std::string> precompiledHeaderArguments;
            AppendDialectFlags(&precompiledHeaderArguments, fileType->GCCDialectName(), "-header");
            precompiledHeaderArguments.insert(precompiledHeaderArguments.end(), arguments.begin() + dialectOffset, arguments.end());
            // Added below, but need to have here in case it affects the precompiled header (as it often does).
            precompiledHeaderArguments.insert(precompiledHeaderArguments.end(), inputArguments.begin(), inputArguments.end());

            precompiledHeaderInfo = std::make_shared<Tool::PrecompiledHeaderInfo>(PrecompiledHeaderInfo::Create(_compiler, prefixHeaderFile, fileType, precompiledHeaderArguments));
            AppendPrefixHeaderFlags(&arguments, env.expand(precompiledHeaderInfo->logicalOutputPath()));
            inputDependencies.push_back(env.expand(precompiledHeaderInfo->compileOutputPath()));
        } else {
            AppendPrefixHeaderFlags(&arguments, prefixHeaderFile);
            inputDependencies.push_back(prefixHeaderFile);
        }
    }

    AppendNotUsedInPrecompsFlags(&arguments, env);
    // After all of the configurable settings, so they can override.
    arguments.insert(arguments.end(), inputArguments.begin(), inputArguments.end());
    AppendDependencyInfoFlags(&arguments, _compiler, env);
    AppendInputOutputFlags(&arguments, _compiler, input.path(), output);

    std::string logMessage = CompileLogMessage(_compiler, "CompileC", input.path(), fileType, output, env, toolContext->workingDirectory());

    std::vector<Tool::Invocation::DependencyInfo> dependencyInfo;
    if (_compiler->dependencyInfoFile()) {
        dependencyInfo.push_back(Tool::Invocation::DependencyInfo(
            dependency::DependencyInfoFormat::Makefile,
            env.expand(*_compiler->dependencyInfoFile())));
    }

    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
    invocation.arguments() = arguments;
    invocation.environment() = options.environment();
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = toolEnvironment.inputs(toolContext->workingDirectory());
    invocation.outputs() = toolEnvironment.outputs(toolContext->workingDirectory());
    invocation.inputDependencies() = inputDependencies;
    invocation.dependencyInfo() = dependencyInfo;
    invocation.logMessage() = logMessage;

    /* Add the compilation invocation to the context. */
    toolContext->invocations().push_back(invocation);
    auto variantArchitectureKey = std::make_pair(environment.resolve("variant"), environment.resolve("arch"));
    toolContext->variantArchitectureInvocations()[variantArchitectureKey].push_back(invocation);

    Tool::CompilationInfo *compilationInfo = &toolContext->compilationInfo();

    /* If we have precompiled header info, create an invocation for the precompiled header. */
    if (precompiledHeaderInfo != nullptr) {
        std::string hash = precompiledHeaderInfo->hash();

        auto precompiledHeaderInfoMap = &compilationInfo->precompiledHeaderInfo();
        if (precompiledHeaderInfoMap->find(hash) == precompiledHeaderInfoMap->end()) {
            /* This precompiled header wasn't already created, create it now. */
            precompiledHeaderInfoMap->insert({ hash, *precompiledHeaderInfo });

            resolvePrecompiledHeader(
                toolContext,
                environment,
                *precompiledHeaderInfo
            );
        }
    }

    if (DialectIsCPlusPlus(fileType->GCCDialectName()) && _compiler->execCPlusPlusLinkerPath()) {
        /* If a single C++ file is seen, use the C++ linker driver. */
        compilationInfo->linkerDriver() = *_compiler->execCPlusPlusLinkerPath();
    } else if (compilationInfo->linkerDriver().empty() && _compiler->execPath()) {
        /* If a C file is seen after a C++ file, don't reset back to the C driver. */
        compilationInfo->linkerDriver() = _compiler->execPath()->raw();
    }

    for (std::string const &linkerArg : options.linkerArgs()) {
        std::vector<std::string> *linkerArguments = &compilationInfo->linkerArguments();

        /* Avoid duplicating arguments for multiple compiler invocations. */
        if (std::find(linkerArguments->begin(), linkerArguments->end(), linkerArg) == linkerArguments->end()) {
            linkerArguments->push_back(linkerArg);
        }
    }
}
예제 #10
0
void Tool::AssetCatalogResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &baseEnvironment,
    std::vector<Tool::Input> const &inputs) const
{
    std::vector<std::string> absoluteInputPaths;
    std::vector<std::string> assetPaths;
    std::vector<std::string> stickerPackStrings;
    for (Tool::Input const &input : inputs) {
        if (input.fileType() != nullptr && input.fileType()->identifier() == "text.plist.strings") {
            std::string strings;
            strings += FSUtil::GetBaseNameWithoutExtension(input.path());
            strings += ":";
            strings += input.localization().value_or("");
            strings += ":";
            strings += input.path();
            stickerPackStrings.push_back(strings);
        } else {
            assetPaths.push_back(input.path());
        }

        std::string absoluteInputPath = FSUtil::ResolveRelativePath(input.path(), toolContext->workingDirectory());
        absoluteInputPaths.push_back(absoluteInputPath);
    }

    /*
     * Create the custom environment with the tool options.
     */
    pbxsetting::Level level = pbxsetting::Level({
        InterfaceBuilderCommon::TargetedDeviceSetting(baseEnvironment),
        pbxsetting::Setting::Create("ASSETCATALOG_COMPILER_INPUTS", pbxsetting::Type::FormatList(assetPaths)),
        pbxsetting::Setting::Create("ASSETCATALOG_COMPILER_STICKER_PACK_STRINGS", pbxsetting::Type::FormatList(stickerPackStrings)),
    });
    pbxsetting::Environment assetCatalogEnvironment = pbxsetting::Environment(baseEnvironment);
    assetCatalogEnvironment.insertFront(level, false);

    /*
     * Resolve the tool options.
     */
    Tool::Environment toolEnvironment = Tool::Environment::Create(_tool, assetCatalogEnvironment, toolContext->workingDirectory(), std::vector<Tool::Input>());
    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), nullptr);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);

    pbxsetting::Environment const &environment = toolEnvironment.environment();

    /*
     * Create tool outputs.
     */
    std::vector<std::string> outputs = {
        /* Creates the asset catalog, always in the resources dir. */
        environment.expand(pbxsetting::Value::Parse("$(ProductResourcesDir)/Assets.car")),
    };

    /*
     * Add custom arguments to the end.
     */
    std::vector<std::string> arguments = tokens.arguments();
    arguments.push_back("--platform");
    arguments.push_back(environment.resolve("PLATFORM_NAME"));
    std::vector<std::string> deploymentTargetArguments = InterfaceBuilderCommon::DeploymentTargetArguments(environment);
    arguments.insert(arguments.end(), deploymentTargetArguments.begin(), deploymentTargetArguments.end());

    // TODO(grp): This is a hack to work around missing `Condition` support in options.
    arguments.erase(std::remove(arguments.begin(), arguments.end(), "--optimization"), arguments.end());
    arguments.erase(std::remove(arguments.begin(), arguments.end(), ""), arguments.end());

    // TODO(grp): This should be handled generically for all tools.
    if (_tool->generatedInfoPlistContentFilePath()) {
        std::string infoPlistContent = environment.expand(*_tool->generatedInfoPlistContentFilePath());
        toolContext->additionalInfoPlistContents().push_back(infoPlistContent);
        outputs.push_back(infoPlistContent);
    }

    // TODO(grp): This should be handled generically for all tools.
    std::vector<Tool::Invocation::DependencyInfo> dependencyInfo;
    if (_tool->dependencyInfoFile()) {
        dependencyInfo.push_back(Tool::Invocation::DependencyInfo(
            dependency::DependencyInfoFormat::Binary,
            environment.expand(*_tool->dependencyInfoFile())));
    }
    if (_tool->deeplyStatInputDirectories()) {
        for (Tool::Input const &input : inputs) {
            /* Create a dependency info file to track the input directory contents. */
            auto info = Tool::Invocation::DependencyInfo(dependency::DependencyInfoFormat::Directory, input.path());
            dependencyInfo.push_back(info);
        }
    }

    /*
     * Create the asset catalog invocation.
     */
    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable());
    invocation.arguments() = arguments;
    invocation.environment() = options.environment();
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = absoluteInputPaths;
    invocation.outputs() = outputs;
    invocation.dependencyInfo() = dependencyInfo;
    invocation.logMessage() = tokens.logMessage();
    invocation.priority() = toolContext->currentPhaseInvocationPriority();
    toolContext->invocations().push_back(invocation);
}
void Tool::InterfaceBuilderResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &baseEnvironment,
    std::vector<Phase::File> const &inputs) const
{
    /*
     * Filter arguments as either a real input or a localization-specific strings file.
     */
    std::vector<Phase::File> primaryInputs;
    std::vector<std::string> localizationStringsFiles;
    for (Phase::File const &input : inputs) {
        if (input.fileType()->identifier() == "text.plist.strings") {
            /* The format here is as expected by ibtool. */
            localizationStringsFiles.push_back(input.localization() + ":" + input.path());
        } else {
            primaryInputs.push_back(input);
        }
    }

    /*
     * Create the custom environment with the needed options.
     */
    pbxsetting::Level level = pbxsetting::Level({
        InterfaceBuilderCommon::TargetedDeviceSetting(baseEnvironment),
        pbxsetting::Setting::Create("IBC_REGIONS_AND_STRINGS_FILES", pbxsetting::Type::FormatList(localizationStringsFiles)),
    });
    pbxsetting::Environment interfaceBuilderEnvironment = baseEnvironment;
    interfaceBuilderEnvironment.insertFront(level, false);

    /*
     * Resolve the tool options.
     */
    Tool::Environment toolEnvironment = Tool::Environment::Create(_tool, interfaceBuilderEnvironment, toolContext->workingDirectory(), primaryInputs);
    Tool::OptionsResult options = Tool::OptionsResult::Create(toolEnvironment, toolContext->workingDirectory(), nullptr);
    Tool::Tokens::ToolExpansions tokens = Tool::Tokens::ExpandTool(toolEnvironment, options);

    pbxsetting::Environment const &environment = toolEnvironment.environment();

    /*
     * Add custom arguments to the end.
     */
    std::vector<std::string> arguments = tokens.arguments();
    std::vector<std::string> deploymentTargetArguments = InterfaceBuilderCommon::DeploymentTargetArguments(environment);
    arguments.insert(arguments.end(), deploymentTargetArguments.begin(), deploymentTargetArguments.end());

    // TODO(grp): Invocations must emit all their outputs for now, but ibtool can emit both general
    // and device-specific (e.g. ~iphone, ~ipad) variants. For now, assume all files are not variant.
    std::vector<std::string> outputs = toolEnvironment.outputs();
    if (_tool->mightNotEmitAllOutputs() && !outputs.empty()) {
        outputs = { outputs.front() };
    }

    // TODO(grp): These should be handled generically for all tools.
    std::unordered_map<std::string, std::string> environmentVariables = options.environment();
    if (_tool->environmentVariables()) {
        for (auto const &variable : *_tool->environmentVariables()) {
            environmentVariables.insert({ variable.first, environment.expand(variable.second) });
        }
    }

    // TODO(grp): This should be handled generically for all tools.
    if (_tool->generatedInfoPlistContentFilePath()) {
        std::string infoPlistContent = environment.expand(*_tool->generatedInfoPlistContentFilePath());
        toolContext->additionalInfoPlistContents().push_back(infoPlistContent);
        outputs.push_back(infoPlistContent);
    }

    /*
     * Create the invocation.
     */
    Tool::Invocation invocation;
    invocation.executable() = Tool::Invocation::Executable::Determine(tokens.executable(), toolContext->executablePaths());
    invocation.arguments() = arguments;
    invocation.environment() = environmentVariables;
    invocation.workingDirectory() = toolContext->workingDirectory();
    invocation.inputs() = toolEnvironment.inputs(toolContext->workingDirectory());
    invocation.outputs() = outputs;
    invocation.logMessage() = tokens.logMessage();
    toolContext->invocations().push_back(invocation);
}
예제 #12
0
void Tool::HeadermapResolver::
resolve(
    Tool::Context *toolContext,
    pbxsetting::Environment const &environment,
    pbxproj::PBX::Target::shared_ptr const &target
) const
{
    /* Add the compiler default environment, which contains the headermap setting defaults. */
    pbxsetting::Environment compilerEnvironment = environment;
    compilerEnvironment.insertFront(_compiler->defaultSettings(), true);

    if (!pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("USE_HEADERMAP"))) {
        return;
    }

    if (pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_USES_VFS"))) {
        // TODO(grp): Support VFS-based header maps.
    }

    HeaderMap targetName;
    HeaderMap ownTargetHeaders;
    HeaderMap projectHeaders;
    HeaderMap allTargetHeaders;
    HeaderMap allNonFrameworkTargetHeaders;

    bool includeFlatEntriesForTargetBeingBuilt     = pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT"));
    bool includeFrameworkEntriesForAllProductTypes = pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES"));
    bool includeProjectHeaders                     = pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_INCLUDES_PROJECT_HEADERS"));

    // TODO(grp): Populate generated headers.
    HeaderMap generatedFiles;

    pbxproj::PBX::Project::shared_ptr project = target->project();

    std::vector<std::string> headermapSearchPaths = HeadermapSearchPaths(_specManager, compilerEnvironment, target, toolContext->searchPaths(), toolContext->workingDirectory());
    for (std::string const &path : headermapSearchPaths) {
        FSUtil::EnumerateDirectory(path, [&](std::string const &fileName) -> bool {
            // TODO(grp): Use FileTypeResolver when reliable.
            std::string extension = FSUtil::GetFileExtension(fileName);
            if (extension != "h" && extension != "hpp") {
                return true;
            }

            targetName.add(fileName, path + "/", fileName);
            return true;
        });
    }

    for (pbxproj::PBX::FileReference::shared_ptr const &fileReference : project->fileReferences()) {
        std::string filePath = compilerEnvironment.expand(fileReference->resolve());
        pbxspec::PBX::FileType::shared_ptr fileType = FileTypeResolver::Resolve(_specManager, { pbxspec::Manager::AnyDomain() }, fileReference, filePath);
        if (fileType == nullptr || (fileType->identifier() != "sourcecode.c.h" && fileType->identifier() != "sourcecode.cpp.h")) {
            continue;
        }

        std::string fileName = FSUtil::GetBaseName(filePath);
        std::string fileDirectory = FSUtil::GetDirectoryName(filePath) + "/";

        projectHeaders.add(fileName, fileDirectory, fileName);
        if (includeProjectHeaders) {
            targetName.add(fileName, fileDirectory, fileName);
        }
    }

    for (pbxproj::PBX::Target::shared_ptr const &projectTarget : project->targets()) {
       for (pbxproj::PBX::BuildPhase::shared_ptr const &buildPhase : projectTarget->buildPhases()) {
            if (buildPhase->type() != pbxproj::PBX::BuildPhase::Type::Headers) {
                continue;
            }

            for (pbxproj::PBX::BuildFile::shared_ptr const &buildFile : buildPhase->files()) {
                if (buildFile->fileRef() == nullptr || buildFile->fileRef()->type() != pbxproj::PBX::GroupItem::Type::FileReference) {
                    continue;
                }

                pbxproj::PBX::FileReference::shared_ptr const &fileReference = std::static_pointer_cast <pbxproj::PBX::FileReference> (buildFile->fileRef());
                std::string filePath = compilerEnvironment.expand(fileReference->resolve());
                pbxspec::PBX::FileType::shared_ptr fileType = FileTypeResolver::Resolve(_specManager, { pbxspec::Manager::AnyDomain() }, fileReference, filePath);
                if (fileType == nullptr || (fileType->identifier() != "sourcecode.c.h" && fileType->identifier() != "sourcecode.cpp.h")) {
                    continue;
                }

                std::string fileName = FSUtil::GetBaseName(filePath);
                std::string fileDirectory = FSUtil::GetDirectoryName(filePath) + "/";
                std::string frameworkName = projectTarget->productName() + "/" + fileName;

                std::vector<std::string> const &attributes = buildFile->attributes();
                bool isPublic  = std::find(attributes.begin(), attributes.end(), "Public") != attributes.end();
                bool isPrivate = std::find(attributes.begin(), attributes.end(), "Private") != attributes.end();

                if (projectTarget == target) {
                    ownTargetHeaders.add(fileName, fileDirectory, fileName);

                    if (!isPublic && !isPrivate) {
                        ownTargetHeaders.add(frameworkName, fileDirectory, fileName);
                        if (includeFlatEntriesForTargetBeingBuilt) {
                            targetName.add(frameworkName, fileDirectory, fileName);
                        }
                    }
                }

                if (isPublic || isPrivate) {
                    allTargetHeaders.add(frameworkName, fileDirectory, fileName);
                    if (includeFrameworkEntriesForAllProductTypes) {
                        targetName.add(frameworkName, fileDirectory, fileName);
                    }

                    // TODO(grp): This is a little messy. Maybe check the product type specification, or the product reference's file type?
                    if (projectTarget->type() == pbxproj::PBX::Target::Type::Native && std::static_pointer_cast<pbxproj::PBX::NativeTarget>(projectTarget)->productType().find("framework") == std::string::npos) {
                        allNonFrameworkTargetHeaders.add(frameworkName, fileDirectory, fileName);
                        if (!includeFrameworkEntriesForAllProductTypes) {
                            targetName.add(frameworkName, fileDirectory, fileName);
                        }
                    }
                }
            }
        }
    }

    std::string headermapFile                                = compilerEnvironment.resolve("CPP_HEADERMAP_FILE");
    std::string headermapFileForOwnTargetHeaders             = compilerEnvironment.resolve("CPP_HEADERMAP_FILE_FOR_OWN_TARGET_HEADERS");
    std::string headermapFileForAllTargetHeaders             = compilerEnvironment.resolve("CPP_HEADERMAP_FILE_FOR_ALL_TARGET_HEADERS");
    std::string headermapFileForAllNonFrameworkTargetHeaders = compilerEnvironment.resolve("CPP_HEADERMAP_FILE_FOR_ALL_NON_FRAMEWORK_TARGET_HEADERS");
    std::string headermapFileForGeneratedFiles               = compilerEnvironment.resolve("CPP_HEADERMAP_FILE_FOR_GENERATED_FILES");
    std::string headermapFileForProjectFiles                 = compilerEnvironment.resolve("CPP_HEADERMAP_FILE_FOR_PROJECT_FILES");

    std::vector<AuxiliaryFile> auxiliaryFiles = {
        AuxiliaryFile(headermapFile, targetName.write(), false),
        AuxiliaryFile(headermapFileForOwnTargetHeaders, ownTargetHeaders.write(), false),
        AuxiliaryFile(headermapFileForAllTargetHeaders, allTargetHeaders.write(), false),
        AuxiliaryFile(headermapFileForAllNonFrameworkTargetHeaders, allNonFrameworkTargetHeaders.write(), false),
        AuxiliaryFile(headermapFileForGeneratedFiles, generatedFiles.write(), false),
        AuxiliaryFile(headermapFileForProjectFiles, projectHeaders.write(), false),
    };

    Tool::Invocation invocation;
    invocation.auxiliaryFiles().insert(invocation.auxiliaryFiles().end(), auxiliaryFiles.begin(), auxiliaryFiles.end());

    std::vector<std::string> systemHeadermapFiles;
    std::vector<std::string> userHeadermapFiles;

    if (pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("ALWAYS_SEARCH_USER_PATHS")) && !pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("ALWAYS_USE_SEPARATE_HEADERMAPS"))) {
        systemHeadermapFiles.push_back(headermapFile);
    } else {
        if (includeFlatEntriesForTargetBeingBuilt) {
            systemHeadermapFiles.push_back(headermapFileForOwnTargetHeaders);
        }
        if (includeFrameworkEntriesForAllProductTypes) {
            systemHeadermapFiles.push_back(headermapFileForAllTargetHeaders);
        } else {
            systemHeadermapFiles.push_back(headermapFileForAllNonFrameworkTargetHeaders);
        }

        userHeadermapFiles.push_back(headermapFileForGeneratedFiles);
        if (includeProjectHeaders) {
            userHeadermapFiles.push_back(headermapFileForProjectFiles);
        }
    }

    toolContext->invocations().push_back(invocation);

    Tool::HeadermapInfo *headermapInfo = &toolContext->headermapInfo();
    headermapInfo->systemHeadermapFiles().insert(headermapInfo->systemHeadermapFiles().end(), systemHeadermapFiles.begin(), systemHeadermapFiles.end());
    headermapInfo->userHeadermapFiles().insert(headermapInfo->userHeadermapFiles().end(), userHeadermapFiles.begin(), userHeadermapFiles.end());
}