ext::optional<Target::Environment> Target::Environment:: Create(Build::Environment const &buildEnvironment, Build::Context const &buildContext, pbxproj::PBX::Target::shared_ptr const &target) { /* Use the source root, which could have been modified by project options, rather than the raw project path. */ std::string workingDirectory = target->project()->sourceRoot(); xcsdk::SDK::Target::shared_ptr sdk; std::vector<std::string> specDomains; pbxproj::XC::BuildConfiguration::shared_ptr projectConfiguration; pbxproj::XC::BuildConfiguration::shared_ptr targetConfiguration; ext::optional<pbxsetting::XC::Config> projectConfigurationFile; ext::optional<pbxsetting::XC::Config> targetConfigurationFile; { /* * Create a synthetic build setting environment to determine the SDK to use. The real build * setting environment will interleave in SDK build settings, but those aren't available until * the target SDK is itself determined. */ pbxsetting::Environment determinationEnvironment = pbxsetting::Environment(buildEnvironment.baseEnvironment()); /* * Add build base settings. */ determinationEnvironment.insertFront(buildContext.baseSettings(), false); /* * Add project build settings. */ determinationEnvironment.insertFront(target->project()->settings(), false); projectConfiguration = ConfigurationNamed(target->project()->buildConfigurationList(), buildContext.configuration()); if (projectConfiguration == nullptr) { fprintf(stderr, "error: unable to find project configuration %s\n", buildContext.configuration().c_str()); return ext::nullopt; } projectConfigurationFile = ConfigurationFile(buildContext.workspaceContext(), projectConfiguration); if (projectConfigurationFile) { determinationEnvironment.insertFront(projectConfigurationFile->level(), false); } determinationEnvironment.insertFront(projectConfiguration->buildSettings(), false); /* * Add target build settings. */ determinationEnvironment.insertFront(target->settings(), false); targetConfiguration = ConfigurationNamed(target->buildConfigurationList(), buildContext.configuration()); if (targetConfiguration == nullptr) { fprintf(stderr, "error: unable to find target configuration %s\n", buildContext.configuration().c_str()); return ext::nullopt; } targetConfigurationFile = ConfigurationFile(buildContext.workspaceContext(), targetConfiguration); if (targetConfigurationFile) { determinationEnvironment.insertFront(targetConfigurationFile->level(), false); } determinationEnvironment.insertFront(targetConfiguration->buildSettings(), false); /* * Add build override settings. */ determinationEnvironment.insertFront(buildContext.actionSettings(), false); for (pbxsetting::Level const &level : buildContext.overrideLevels()) { determinationEnvironment.insertFront(level, false); } /* * All settings added; determine target SDK. */ std::string sdkroot = determinationEnvironment.resolve("SDKROOT"); sdk = buildEnvironment.sdkManager()->findTarget(nullptr, sdkroot); if (sdk == nullptr) { fprintf(stderr, "error: unable to find sdkroot %s\n", sdkroot.c_str()); return ext::nullopt; } specDomains = SDKSpecificationDomains(sdk); } pbxspec::PBX::BuildSystem::shared_ptr buildSystem = TargetBuildSystem(buildEnvironment.specManager(), specDomains, target); if (buildSystem == nullptr) { fprintf(stderr, "error: unable to create build system\n"); return ext::nullopt; } pbxspec::PBX::ProductType::shared_ptr productType = nullptr; pbxspec::PBX::PackageType::shared_ptr packageType = nullptr; if (target->type() == pbxproj::PBX::Target::Type::Native) { pbxproj::PBX::NativeTarget::shared_ptr nativeTarget = std::static_pointer_cast<pbxproj::PBX::NativeTarget>(target); productType = buildEnvironment.specManager()->productType(nativeTarget->productType(), specDomains); if (productType == nullptr) { fprintf(stderr, "error: unable to find product type %s\n", nativeTarget->productType().c_str()); return ext::nullopt; } // FIXME(grp): Should this always use the first package type? if (productType->packageTypes() && !productType->packageTypes()->empty()) { packageType = buildEnvironment.specManager()->packageType(productType->packageTypes()->at(0), specDomains); if (packageType == nullptr) { fprintf(stderr, "error: unable to find package type %s\n", productType->packageTypes()->at(0).c_str()); return ext::nullopt; } } } /* * Now we have $(SDKROOT), and can make the real levels. */ pbxsetting::Environment environment = pbxsetting::Environment(buildEnvironment.baseEnvironment()); environment.insertFront(buildSystem->defaultSettings(), true); environment.insertFront(buildContext.baseSettings(), false); environment.insertFront(pbxsetting::Level({ pbxsetting::Setting::Parse("GCC_VERSION", "$(DEFAULT_COMPILER)"), }), false); if (sdk->platform()->defaultProperties()) { environment.insertFront(*sdk->platform()->defaultProperties(), false); } environment.insertFront(PlatformArchitecturesLevel(buildEnvironment.specManager(), specDomains), false); if (sdk->defaultProperties()) { environment.insertFront(*sdk->defaultProperties(), false); } environment.insertFront(sdk->platform()->settings(), false); environment.insertFront(sdk->settings(), false); if (sdk->customProperties()) { environment.insertFront(*sdk->customProperties(), false); } if (sdk->platform()->overrideProperties()) { environment.insertFront(*sdk->platform()->overrideProperties(), false); } if (packageType != nullptr) { environment.insertFront(PackageTypeLevel(packageType), false); } if (productType != nullptr) { environment.insertFront(ProductTypeLevel(productType), false); } /* * Add project build settings. */ environment.insertFront(target->project()->settings(), false); if (projectConfigurationFile) { environment.insertFront(projectConfigurationFile->level(), false); } environment.insertFront(projectConfiguration->buildSettings(), false); /* * Add target build settings. */ environment.insertFront(target->settings(), false); if (targetConfigurationFile) { environment.insertFront(targetConfigurationFile->level(), false); } environment.insertFront(targetConfiguration->buildSettings(), false); environment.insertFront(buildContext.actionSettings(), false); for (pbxsetting::Level const &level : buildContext.overrideLevels()) { environment.insertFront(level, false); } std::vector<std::string> architectures = ResolveArchitectures(environment); std::vector<std::string> variants = ResolveVariants(environment); environment.insertFront(ArchitecturesVariantsLevel(architectures, variants), false); /* Determine toolchains. Must be after the SDK levels are added, so they can be a fallback. */ std::vector<xcsdk::SDK::Toolchain::shared_ptr> toolchains; std::vector<std::string> effectiveToolchainPaths; for (std::string const &toolchainName : pbxsetting::Type::ParseList(environment.resolve("TOOLCHAINS"))) { if (xcsdk::SDK::Toolchain::shared_ptr toolchain = buildEnvironment.sdkManager()->findToolchain(nullptr, toolchainName)) { // TODO: Apply toolchain override build settings. toolchains.push_back(toolchain); effectiveToolchainPaths.push_back(toolchain->path()); } } environment.insertFront(pbxsetting::Level({ /* At the target level and below, the SDKROOT changes to always be a SDK path. */ pbxsetting::Setting::Create("SDKROOT", sdk->path()), pbxsetting::Setting::Create("EFFECTIVE_TOOLCHAINS_DIRS", pbxsetting::Type::FormatList(effectiveToolchainPaths)), }), false); /* Tool search directories. Use the toolchains just discovered. */ std::shared_ptr<xcsdk::SDK::Manager> const &sdkManager = buildEnvironment.sdkManager(); std::vector<std::string> executablePaths = sdkManager->executablePaths(sdk->platform(), sdk, toolchains); executablePaths.insert(executablePaths.end(), buildEnvironment.baseExecutablePaths().begin(), buildEnvironment.baseExecutablePaths().end()); environment.insertFront(ExecutablePathsLevel(executablePaths), false); auto buildRules = Target::BuildRules::Create(buildEnvironment.specManager(), specDomains, target); auto buildFileDisambiguation = BuildFileDisambiguation(target); return Environment( sdk, toolchains, executablePaths, buildRules, specDomains, buildSystem, productType, packageType, environment, variants, architectures, workingDirectory, buildFileDisambiguation); }
ext::optional<Target::Environment> Target::Environment:: Create(Build::Environment const &buildEnvironment, Build::Context const &buildContext, pbxproj::PBX::Target::shared_ptr const &target) { xcsdk::SDK::Target::shared_ptr sdk; std::vector<std::string> specDomains; pbxproj::XC::BuildConfiguration::shared_ptr projectConfiguration; pbxproj::XC::BuildConfiguration::shared_ptr targetConfiguration; pbxsetting::XC::Config::shared_ptr projectConfigurationFile; pbxsetting::XC::Config::shared_ptr targetConfigurationFile; { // FIXME(grp): $(SRCROOT) must be set in order to find the xcconfig, but we need the xcconfig to know $(SDKROOT). So this can't // use the default level order, because $(SRCROOT) comes below $(SDKROOT). Hack around this for now with a synthetic environment. // It's also in the wrong order because project settings should be below the SDK, but are needed to *load* the xcconfig. pbxsetting::Environment determinationEnvironment = buildEnvironment.baseEnvironment(); determinationEnvironment.insertFront(buildContext.baseSettings(), false); projectConfiguration = ConfigurationNamed(target->project()->buildConfigurationList(), buildContext.configuration()); if (projectConfiguration == nullptr) { fprintf(stderr, "error: unable to find project configuration %s\n", buildContext.configuration().c_str()); return ext::nullopt; } determinationEnvironment.insertFront(target->project()->settings(), false); determinationEnvironment.insertFront(projectConfiguration->buildSettings(), false); pbxsetting::Environment projectActionEnvironment = determinationEnvironment; projectActionEnvironment.insertFront(buildContext.actionSettings(), false); for (pbxsetting::Level const &level : buildContext.overrideLevels()) { projectActionEnvironment.insertFront(level, false); } projectConfigurationFile = LoadConfigurationFile(projectConfiguration, projectActionEnvironment); if (projectConfigurationFile != nullptr) { determinationEnvironment.insertFront(projectConfigurationFile->level(), false); } targetConfiguration = ConfigurationNamed(target->buildConfigurationList(), buildContext.configuration()); if (targetConfiguration == nullptr) { fprintf(stderr, "error: unable to find target configuration %s\n", buildContext.configuration().c_str()); return ext::nullopt; } determinationEnvironment.insertFront(target->settings(), false); determinationEnvironment.insertFront(targetConfiguration->buildSettings(), false); // FIXME(grp): Similar issue for the target xcconfig. These levels aren't complete (no platform) but are needed to *get* which SDK to use. pbxsetting::Environment targetActionEnvironment = determinationEnvironment; targetActionEnvironment.insertFront(buildContext.actionSettings(), false); for (pbxsetting::Level const &level : buildContext.overrideLevels()) { targetActionEnvironment.insertFront(level, false); } targetConfigurationFile = LoadConfigurationFile(targetConfiguration, targetActionEnvironment); if (targetConfigurationFile != nullptr) { determinationEnvironment.insertFront(targetConfigurationFile->level(), false); } determinationEnvironment.insertFront(buildContext.actionSettings(), false); for (pbxsetting::Level const &level : buildContext.overrideLevels()) { determinationEnvironment.insertFront(level, false); } std::string sdkroot = determinationEnvironment.resolve("SDKROOT"); sdk = buildEnvironment.sdkManager()->findTarget(sdkroot); if (sdk == nullptr) { fprintf(stderr, "error: unable to find sdkroot %s\n", sdkroot.c_str()); return ext::nullopt; } specDomains = SDKSpecificationDomains(sdk); } pbxspec::PBX::BuildSystem::shared_ptr buildSystem = TargetBuildSystem(buildEnvironment.specManager(), specDomains, target); if (buildSystem == nullptr) { fprintf(stderr, "error: unable to create build system\n"); return ext::nullopt; } pbxspec::PBX::ProductType::shared_ptr productType = nullptr; pbxspec::PBX::PackageType::shared_ptr packageType = nullptr; if (target->type() == pbxproj::PBX::Target::Type::Native) { pbxproj::PBX::NativeTarget::shared_ptr nativeTarget = std::static_pointer_cast<pbxproj::PBX::NativeTarget>(target); productType = buildEnvironment.specManager()->productType(nativeTarget->productType(), specDomains); if (productType == nullptr) { fprintf(stderr, "error: unable to find product type %s\n", nativeTarget->productType().c_str()); return ext::nullopt; } // FIXME(grp): Should this always use the first package type? if (productType->packageTypes() && !productType->packageTypes()->empty()) { packageType = buildEnvironment.specManager()->packageType(productType->packageTypes()->at(0), specDomains); if (packageType == nullptr) { fprintf(stderr, "error: unable to find package type %s\n", productType->packageTypes()->at(0).c_str()); return ext::nullopt; } } } // Now we have $(SDKROOT), and can make the real levels. pbxsetting::Environment environment = buildEnvironment.baseEnvironment(); environment.insertFront(buildSystem->defaultSettings(), true); environment.insertFront(buildContext.baseSettings(), false); environment.insertFront(pbxsetting::Level({ pbxsetting::Setting::Parse("GCC_VERSION", "$(DEFAULT_COMPILER)"), }), false); environment.insertFront(sdk->platform()->defaultProperties(), false); environment.insertFront(PlatformArchitecturesLevel(buildEnvironment.specManager(), specDomains), false); environment.insertFront(sdk->defaultProperties(), false); environment.insertFront(sdk->platform()->settings(), false); environment.insertFront(sdk->settings(), false); environment.insertFront(sdk->customProperties(), false); environment.insertFront(sdk->platform()->overrideProperties(), false); if (packageType != nullptr) { environment.insertFront(PackageTypeLevel(packageType), false); } if (productType != nullptr) { environment.insertFront(ProductTypeLevel(productType), false); } environment.insertFront(target->project()->settings(), false); if (projectConfigurationFile != nullptr) { environment.insertFront(projectConfigurationFile->level(), false); } environment.insertFront(projectConfiguration->buildSettings(), false); environment.insertFront(target->settings(), false); if (targetConfigurationFile != nullptr) { environment.insertFront(targetConfigurationFile->level(), false); } environment.insertFront(targetConfiguration->buildSettings(), false); environment.insertFront(buildContext.actionSettings(), false); for (pbxsetting::Level const &level : buildContext.overrideLevels()) { environment.insertFront(level, false); } std::vector<std::string> architectures = ResolveArchitectures(environment); std::vector<std::string> variants = ResolveVariants(environment); environment.insertFront(ArchitecturesVariantsLevel(architectures, variants), false); /* At the target level and below, the SDKROOT changes to always be a SDK path. */ environment.insertFront(pbxsetting::Level({ pbxsetting::Setting::Create("SDKROOT", sdk->path()), }), false); /* Determine toolchains. Must be after the SDK levels are added, so they can be a fallback. */ xcsdk::SDK::Toolchain::vector toolchains; for (std::string const &toolchainName : pbxsetting::Type::ParseList(environment.resolve("TOOLCHAINS"))) { if (xcsdk::SDK::Toolchain::shared_ptr toolchain = buildEnvironment.sdkManager()->findToolchain(toolchainName)) { toolchains.push_back(toolchain); } } /* Tool search directories. Use the toolchains just discovered. */ std::vector<std::string> executablePaths = sdk->executablePaths(toolchains); auto buildRules = std::make_shared<Target::BuildRules>(Target::BuildRules::Create(buildEnvironment.specManager(), specDomains, target)); auto buildFileDisambiguation = BuildFileDisambiguation(target); /* Use the source root, which could have been modified by project options, rather than the raw project path. */ std::string workingDirectory = target->project()->sourceRoot(); Target::Environment te = Target::Environment(); te._sdk = sdk; te._toolchains = toolchains; te._executablePaths = executablePaths; te._buildRules = buildRules; te._environment = std::unique_ptr<pbxsetting::Environment>(new pbxsetting::Environment(environment)); te._variants = variants; te._architectures = architectures; te._buildSystem = buildSystem; te._packageType = packageType; te._productType = productType; te._specDomains = specDomains; te._projectConfigurationFile = projectConfigurationFile; te._targetConfigurationFile = targetConfigurationFile; te._workingDirectory = workingDirectory; te._buildFileDisambiguation = buildFileDisambiguation; return te; }