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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); } } }
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); }
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()); }