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); }
static bool EvaluateCondition(std::string const &condition, pbxsetting::Environment const &environment) { #define WARN_UNHANDLED_CONDITION 0 // TODO(grp): Evaluate condition expression language correctly. std::string expression = environment.expand(pbxsetting::Value::Parse(condition)); std::string::size_type eq = expression.find(" == "); if (eq != std::string::npos) { std::string lhs = expression.substr(0, eq); std::string rhs = expression.substr(eq + 4); #if WARN_UNHANDLED_CONDITION fprintf(stderr, "warning: unhandled condition evaluation '%s' == '%s'\n", lhs.c_str(), rhs.c_str()); #endif return (lhs == rhs); } std::string::size_type noteq = expression.find(" != "); if (noteq != std::string::npos) { std::string lhs = expression.substr(0, noteq); std::string rhs = expression.substr(noteq + 4); #if WARN_UNHANDLED_CONDITION fprintf(stderr, "warning: unhandled condition evaluation '%s' != '%s'\n", lhs.c_str(), rhs.c_str()); #endif return (lhs != rhs); } #if WARN_UNHANDLED_CONDITION fprintf(stderr, "warning: unhandled condition evaluation '%s'\n", expression.c_str()); #endif return expression != "NO"; }
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); }
static void AppendDependencyInfoFlags(std::vector<std::string> *args, pbxspec::PBX::Compiler::shared_ptr const &compiler, pbxsetting::Environment const &environment) { if (compiler->dependencyInfoArgs()) { for (pbxsetting::Value const &arg : *compiler->dependencyInfoArgs()) { args->push_back(environment.expand(arg)); } } }
static pbxsetting::XC::Config::shared_ptr LoadConfigurationFile(pbxproj::XC::BuildConfiguration::shared_ptr const &buildConfiguration, pbxsetting::Environment const &environment) { if (buildConfiguration->baseConfigurationReference() == nullptr) { return nullptr; } pbxsetting::Value configurationValue = buildConfiguration->baseConfigurationReference()->resolve(); std::string configurationPath = environment.expand(configurationValue); return pbxsetting::XC::Config::Open(configurationPath, environment); }
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); }
static void ExpandBuildSettings(plist::Object *value, pbxsetting::Environment const &environment) { /* * Recursively expand any strings in the plist. Dictionary keys are not expanded. */ if (auto dictionary = plist::CastTo<plist::Dictionary>(value)) { for (size_t n = 0; n < dictionary->count(); n++) { ExpandBuildSettings(dictionary->value(n), environment); } } else if (auto array = plist::CastTo<plist::Array>(value)) { for (size_t n = 0; n < array->count(); n++) { ExpandBuildSettings(array->value(n), environment); } } else if (auto string = plist::CastTo<plist::String>(value)) { pbxsetting::Value parsed = pbxsetting::Value::Parse(string->value()); string->setValue(environment.expand(parsed)); } }
static std::vector<std::string> HeadermapSearchPaths(pbxspec::Manager::shared_ptr const &specManager, pbxsetting::Environment const &environment, pbxproj::PBX::Target::shared_ptr const &target, Tool::SearchPaths const &searchPaths, std::string const &workingDirectory) { std::unordered_set<std::string> allHeaderSearchPaths; std::vector<std::string> orderedHeaderSearchPaths; for (pbxproj::PBX::BuildPhase::shared_ptr const &buildPhase : target->buildPhases()) { if (buildPhase->type() != pbxproj::PBX::BuildPhase::Type::Sources) { continue; } for (pbxproj::PBX::BuildFile::shared_ptr const &buildFile : buildPhase->files()) { if (buildFile->fileRef() == nullptr) { continue; } std::string filePath = environment.expand(buildFile->fileRef()->resolve()); std::string fullPath = FSUtil::GetDirectoryName(filePath); if (allHeaderSearchPaths.insert(fullPath).second) { orderedHeaderSearchPaths.push_back(fullPath); } } } for (std::string const &path : searchPaths.userHeaderSearchPaths()) { std::string fullPath = FSUtil::ResolveRelativePath(path, workingDirectory); if (allHeaderSearchPaths.insert(fullPath).second) { orderedHeaderSearchPaths.push_back(fullPath); } } for (std::string const &path : searchPaths.headerSearchPaths()) { std::string fullPath = FSUtil::ResolveRelativePath(path, workingDirectory); if (allHeaderSearchPaths.insert(fullPath).second) { orderedHeaderSearchPaths.push_back(fullPath); } } return orderedHeaderSearchPaths; }
Tool::OptionsResult Tool::OptionsResult:: Create( pbxsetting::Environment const &environment, std::string const &workingDirectory, std::vector<pbxspec::PBX::PropertyOption::shared_ptr> const &options, pbxspec::PBX::FileType::shared_ptr const &fileType, std::unordered_set<std::string> const &deletedSettings) { std::vector<std::string> arguments; std::unordered_map<std::string, std::string> environmentVariables; std::vector<std::string> linkerArgs; std::string architecture = environment.resolve("arch"); for (pbxspec::PBX::PropertyOption::shared_ptr const &option : options) { if (deletedSettings.find(option->name()) != deletedSettings.end()) { continue; } if (option->condition() && !EvaluateCondition(*option->condition(), environment)) { continue; } if (option->commandLineCondition() && !EvaluateCondition(*option->commandLineCondition(), environment)) { continue; } if (option->architectures() && std::find(option->architectures()->begin(), option->architectures()->end(), architecture) == option->architectures()->end()) { continue; } if (option->fileTypes() && fileType != nullptr && std::find(option->fileTypes()->begin(), option->fileTypes()->end(), fileType->identifier()) == option->fileTypes()->end()) { continue; } // TODO(grp): Use PropertyOption::conditionFlavors(). std::string value = environment.resolve(option->name()); if (option->type() == "Boolean" || option->type() == "bool") { bool booleanValue = pbxsetting::Type::ParseBoolean(value); ext::optional<pbxsetting::Value> const &flag = (booleanValue ? option->commandLineFlag() : option->commandLineFlagIfFalse()); if (flag) { /* Boolean flags don't get the flag value after, since that would be just YES or NO. */ arguments.push_back(environment.expand(*flag)); } } else { if (!value.empty()) { if (option->commandLineFlag()) { pbxsetting::Value const &flag = *option->commandLineFlag(); /* Pass both the command line flag and the option value itself. */ std::vector<pbxsetting::Value> values = { flag, pbxsetting::Value::Variable("value") }; AddOptionArgumentValues(&arguments, environment, workingDirectory, values, option); } } } AddOptionValuesArguments(&arguments, environment, workingDirectory, plist::CastTo<plist::Array>(option->values()), value, option); AddOptionValuesArguments(&arguments, environment, workingDirectory, plist::CastTo<plist::Array>(option->allowedValues()), value, option); if (!value.empty()) { /* Pass the prefix then the option value in the same argument. */ if (option->commandLinePrefixFlag()) { pbxsetting::Value const &prefix = *option->commandLinePrefixFlag(); pbxsetting::Value prefixValue = prefix + pbxsetting::Value::Variable("value"); AddOptionArgumentValues(&arguments, environment, workingDirectory, { prefixValue }, option); } } AddOptionArgsArguments(&arguments, environment, workingDirectory, option->commandLineArgs(), value, option); AddOptionArgsArguments(&linkerArgs, environment, workingDirectory, option->additionalLinkerArgs(), value, option); if (option->setValueInEnvironmentVariable()) { std::string const &variable = environment.expand(*option->setValueInEnvironmentVariable()); environmentVariables.insert({ variable, value }); } // TODO(grp): Use PropertyOption::conditionFlavors(). // TODO(grp): Use PropertyOption::isCommand{Input,Output}(). // TODO(grp): Use PropertyOption::isInputDependency(), PropertyOption::outputDependencies(). // TODO(grp): Use PropertyOption::outputsAreSourceFiles(). } return Tool::OptionsResult(arguments, environmentVariables, linkerArgs); }
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); } } }