예제 #1
0
static pbxspec::PBX::BuildSystem::shared_ptr
TargetBuildSystem(pbxspec::Manager::shared_ptr const &specManager, std::vector<std::string> const &specDomains, pbxproj::PBX::Target::shared_ptr const &target)
{
    if (target->type() == pbxproj::PBX::Target::Type::Native) {
        return specManager->buildSystem("com.apple.build-system.native", specDomains);
    } else if (target->type() == pbxproj::PBX::Target::Type::Legacy) {
        return specManager->buildSystem("com.apple.build-system.external", specDomains);
    } else if (target->type() == pbxproj::PBX::Target::Type::Aggregate) {
       return specManager->buildSystem("com.apple.build-system.external", specDomains);
    } else {
        fprintf(stderr, "error: unknown target type\n");
        return nullptr;
    }
}
예제 #2
0
Target::BuildRules Target::BuildRules::
Create(pbxspec::Manager::shared_ptr const &specManager, std::vector<std::string> const &domains, pbxproj::PBX::Target::shared_ptr const &target)
{
    Target::BuildRules::BuildRule::vector buildRules;

    if (target->type() == pbxproj::PBX::Target::kTypeNative) {
        pbxproj::PBX::NativeTarget::shared_ptr nativeTarget = std::static_pointer_cast <pbxproj::PBX::NativeTarget> (target);
        for (pbxproj::PBX::BuildRule::shared_ptr const &projBuildRule : nativeTarget->buildRules()) {
            if (Target::BuildRules::BuildRule::shared_ptr buildRule = ProjectBuildRule(specManager, domains, projBuildRule)) {
                buildRules.push_back(buildRule);
            }
        }
    }

    for (pbxspec::PBX::BuildRule::shared_ptr const &specBuildRule : specManager->buildRules()) {
        if (Target::BuildRules::BuildRule::shared_ptr buildRule = SpecificationBuildRule(specManager, domains, specBuildRule)) {
            buildRules.push_back(buildRule);
        }
    }

    for (pbxspec::PBX::BuildRule::shared_ptr const &specBuildRule : specManager->synthesizedBuildRules(domains)) {
        if (BuildRule::shared_ptr buildRule = SpecificationBuildRule(specManager, domains, specBuildRule)) {
            buildRules.push_back(buildRule);
        }
    }

    return Target::BuildRules(buildRules);
}
예제 #3
0
static std::unordered_map<pbxproj::PBX::BuildFile::shared_ptr, std::string>
BuildFileDisambiguation(pbxproj::PBX::Target::shared_ptr const &target)
{
    std::unordered_multimap<std::string, pbxproj::PBX::BuildFile::shared_ptr> buildFileUnambiguous;
    std::unordered_map<pbxproj::PBX::BuildFile::shared_ptr, std::string> buildFileDisambiguation;

    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 name = FSUtil::GetBaseNameWithoutExtension(buildFile->fileRef()->name());

            /* Use a case-insensitive key to detect conflicts. */
            std::string lower;
            std::transform(name.begin(), name.end(), std::back_inserter(lower), ::tolower);

            auto range = buildFileUnambiguous.equal_range(lower);
            if (range.first != buildFileUnambiguous.end()) {
                /* Conflicts with at least one other file, add a disambiguation. */
                buildFileDisambiguation.insert({ buildFile, name + "-" + buildFile->blueprintIdentifier() });

                /* Add disambiguations for all the conflicting files. */
                for (auto it = range.first; it != range.second; ++it) {
                    pbxproj::PBX::BuildFile::shared_ptr const &otherBuildFile = it->second;
                    std::string otherName = FSUtil::GetBaseNameWithoutExtension(otherBuildFile->fileRef()->name());
                    buildFileDisambiguation.insert({ otherBuildFile, otherName + "-" + otherBuildFile->blueprintIdentifier() });
                }
            }
            buildFileUnambiguous.insert({ lower, buildFile });
        }
    }

    return buildFileDisambiguation;
}
예제 #4
0
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;
}
예제 #5
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);
}
예제 #6
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;
}
예제 #7
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 = pbxsetting::Environment(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) {
        Filesystem::GetDefaultUNSAFE()->readDirectory(path, false, [&](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(Filesystem::GetDefaultUNSAFE(), _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(Filesystem::GetDefaultUNSAFE(), _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<Tool::AuxiliaryFile> auxiliaryFiles = {
        Tool::AuxiliaryFile::Data(headermapFile, targetName.write()),
        Tool::AuxiliaryFile::Data(headermapFileForOwnTargetHeaders, ownTargetHeaders.write()),
        Tool::AuxiliaryFile::Data(headermapFileForAllTargetHeaders, allTargetHeaders.write()),
        Tool::AuxiliaryFile::Data(headermapFileForAllNonFrameworkTargetHeaders, allNonFrameworkTargetHeaders.write()),
        Tool::AuxiliaryFile::Data(headermapFileForGeneratedFiles, generatedFiles.write()),
        Tool::AuxiliaryFile::Data(headermapFileForProjectFiles, projectHeaders.write()),
    };

    toolContext->auxiliaryFiles().insert(toolContext->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);
        }
    }

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