示例#1
0
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);
}
示例#2
0
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;
}