diff --git a/Libraries/pbxbuild/Headers/pbxbuild/Tool/HeadermapInfo.h b/Libraries/pbxbuild/Headers/pbxbuild/Tool/HeadermapInfo.h index dda332895..bdd6b2a51 100644 --- a/Libraries/pbxbuild/Headers/pbxbuild/Tool/HeadermapInfo.h +++ b/Libraries/pbxbuild/Headers/pbxbuild/Tool/HeadermapInfo.h @@ -17,8 +17,11 @@ namespace Tool { class HeadermapInfo { private: - std::vector _systemHeadermapFiles; - std::vector _userHeadermapFiles; + std::vector _systemHeadermapFiles; + std::vector _userHeadermapFiles; + +private: + ext::optional _overlayVFS; public: HeadermapInfo(); @@ -35,6 +38,14 @@ class HeadermapInfo { { return _systemHeadermapFiles; } std::vector &userHeadermapFiles() { return _userHeadermapFiles; } + +public: + ext::optional const &overlayVFS() const + { return _overlayVFS; } + +public: + ext::optional &overlayVFS() + { return _overlayVFS; } }; } diff --git a/Libraries/pbxbuild/Sources/Tool/CompilerCommon.cpp b/Libraries/pbxbuild/Sources/Tool/CompilerCommon.cpp index 4ae93acd9..ecb82c915 100644 --- a/Libraries/pbxbuild/Sources/Tool/CompilerCommon.cpp +++ b/Libraries/pbxbuild/Sources/Tool/CompilerCommon.cpp @@ -33,6 +33,11 @@ AppendIncludePathFlags(std::vector *args, pbxsetting::Environment c AppendCompoundFlags(args, "-I", true, headermapInfo.systemHeadermapFiles()); AppendCompoundFlags(args, "-iquote", false, headermapInfo.userHeadermapFiles()); + if (headermapInfo.overlayVFS()) { + args->push_back("-ivfsoverlay"); + args->push_back(*headermapInfo.overlayVFS()); + } + if (environment.resolve("USE_HEADER_SYMLINKS") == "YES") { // TODO(grp): Create this symlink tree as needed. AppendCompoundFlags(args, "-I", true, { environment.resolve("CPP_HEADER_SYMLINKS_DIR") }); diff --git a/Libraries/pbxbuild/Sources/Tool/HeadermapResolver.cpp b/Libraries/pbxbuild/Sources/Tool/HeadermapResolver.cpp index da498be8c..09d986771 100644 --- a/Libraries/pbxbuild/Sources/Tool/HeadermapResolver.cpp +++ b/Libraries/pbxbuild/Sources/Tool/HeadermapResolver.cpp @@ -14,9 +14,16 @@ #include #include #include +#include +#include +#include +#include +#include #include #include +#include + namespace Tool = pbxbuild::Tool; using AuxiliaryFile = pbxbuild::Tool::Invocation::AuxiliaryFile; using pbxbuild::HeaderMap; @@ -72,6 +79,111 @@ HeadermapSearchPaths(pbxspec::Manager::shared_ptr const &specManager, pbxsetting return orderedHeaderSearchPaths; } +static std::unique_ptr +VFSFileReference(std::string const &fileName, std::string const &sourcePath) +{ + auto file = plist::Dictionary::New(); + file->set("type", plist::String::New("file")); + file->set("name", plist::String::New(fileName)); + file->set("external-contents", plist::String::New(sourcePath)); + return file; +} + +static std::unique_ptr +VFSDirectoryReference(std::string const &directoryPath, std::unique_ptr &&contents) +{ + auto directory = plist::Dictionary::New(); + directory->set("type", plist::String::New("directory")); + directory->set("name", plist::String::New(directoryPath)); + directory->set("contents", std::move(contents)); + return directory; +} + +static std::array, 2> +VFSHeadersDirectories(pbxspec::Manager::shared_ptr const &specManager, pbxproj::PBX::Target::shared_ptr const &target, std::string const &targetRoot, pbxsetting::Environment const &environment) +{ + std::unique_ptr publicHeadersContents = plist::Array::New(); + std::unique_ptr privateHeadersContents = plist::Array::New(); + + for (pbxproj::PBX::BuildPhase::shared_ptr const &buildPhase : target->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 (buildFile->fileRef()); + std::string filePath = environment.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::vector 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 (isPublic) { + std::unique_ptr file = VFSFileReference(fileName, filePath); + publicHeadersContents->append(std::move(file)); + } else if (isPrivate) { + std::unique_ptr file = VFSFileReference(fileName, filePath); + privateHeadersContents->append(std::move(file)); + } + } + } + + std::string publicHeadersRoot = targetRoot + "/" + environment.resolve("PUBLIC_HEADERS_FOLDER_PATH"); + std::string privateHeadersRoot = targetRoot + "/" + environment.resolve("PRIVATE_HEADERS_FOLDER_PATH"); + + std::string swiftObjCInterfaceHeader = environment.resolve("SWIFT_OBJC_INTERFACE_HEADER_NAME"); + if (!swiftObjCInterfaceHeader.empty()) { + std::string swiftObjCInterfacePath = publicHeadersRoot + "/" + swiftObjCInterfaceHeader; + std::unique_ptr swiftObjCInterface = VFSFileReference(swiftObjCInterfaceHeader, swiftObjCInterfacePath); + publicHeadersContents->append(std::move(swiftObjCInterface)); + } + + std::unique_ptr publicHeaders = VFSDirectoryReference(publicHeadersRoot, std::move(publicHeadersContents)); + std::unique_ptr privateHeaders = VFSDirectoryReference(privateHeadersRoot, std::move(privateHeadersContents)); + return {{ std::move(publicHeaders), std::move(privateHeaders) }}; +} + +static std::unique_ptr +VFSModulesDirectory(pbxsetting::Environment const &environment, std::string const &targetRoot) +{ + std::string moduleMapSourceRoot = environment.resolve("TARGET_TEMP_DIR"); + std::unique_ptr modulesContents = plist::Array::New(); + + { + std::string moduleMapName = "module.modulemap"; + std::string moduleMapSource = moduleMapSourceRoot + "/" + moduleMapName; + + std::unique_ptr moduleMap = VFSFileReference(moduleMapName, moduleMapSource); + modulesContents->append(std::move(moduleMap)); + } + + std::string privateModuleMapFile = environment.resolve("MODULEMAP_PRIVATE_FILE"); + if (!privateModuleMapFile.empty()) { + std::string privateModuleMapName = "module.private.modulemap"; + std::string privateModuleMapSource = moduleMapSourceRoot + "/" + privateModuleMapName; + + std::unique_ptr privateModuleMap = VFSFileReference(privateModuleMapName, privateModuleMapSource); + modulesContents->append(std::move(privateModuleMap)); + } + + if (!modulesContents->empty()) { + std::string modulesRoot = targetRoot + "/" + "Modules"; + return VFSDirectoryReference(modulesRoot, std::move(modulesContents)); + } else { + return nullptr; + } +} + void Tool::HeadermapResolver:: resolve( Tool::Context *toolContext, @@ -87,8 +199,32 @@ resolve( return; } - if (pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_USES_VFS"))) { - // TODO(grp): Support VFS-based header maps. + bool definesModule = pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("DEFINES_MODULE")); + bool requiresVFS = definesModule || pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_USES_VFS")); + + std::unique_ptr allProjectHeaders = nullptr; + if (requiresVFS) { + // TODO(grp): Create VFS here. + std::unique_ptr roots = plist::Array::New(); + + // TODO: do this for every target + std::string targetRoot = compilerEnvironment.resolve("BUILT_PRODUCTS_DIR") + "/" + compilerEnvironment.resolve("WRAPPER_NAME"); + + auto headerDirectories = VFSHeadersDirectories(_specManager, target, targetRoot, compilerEnvironment); + for (auto it = headerDirectories.begin(); it != headerDirectories.end(); ++it) { + roots->append(std::move(*it)); + } + + if (definesModule) { + if (std::unique_ptr modules = VFSModulesDirectory(compilerEnvironment, targetRoot)) { + roots->append(std::move(modules)); + } + } + + allProjectHeaders = plist::Dictionary::New(); + allProjectHeaders->set("version", plist::Integer::New(0)); + allProjectHeaders->set("case-sensitive", plist::String::New("false")); + allProjectHeaders->set("roots", std::move(roots)); } HeaderMap targetName; @@ -100,6 +236,8 @@ resolve( 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 + // bool useFrameworkPrefixEntries = pbxsetting::Type::ParseBoolean(compilerEnvironment.resolve("HEADERMAP_USES_FRAMEWORK_PREFIX_ENTRIES")); // TODO(grp): Populate generated headers. HeaderMap generatedFiles; @@ -207,6 +345,19 @@ resolve( AuxiliaryFile::Data(headermapFileForProjectFiles, projectHeaders.write()), }; + std::string vfsAllProductHeaders = compilerEnvironment.resolve("CPP_HEADERMAP_PRODUCT_HEADERS_VFS_FILE"); + + if (allProjectHeaders) { + // TODO(grp): This should be YAML, not JSON, but JSON works for now. + auto result = plist::Format::JSON::Serialize(allProjectHeaders.get(), plist::Format::JSON::Create()); + if (!result.first) { + fprintf(stderr, "error: unable to serialize VFS: %s\n", result.second.c_str()); + } else { + auto auxiliaryFile = AuxiliaryFile::Data(vfsAllProductHeaders, *result.first); + auxiliaryFiles.push_back(auxiliaryFile); + } + } + Tool::Invocation invocation; invocation.auxiliaryFiles().insert(invocation.auxiliaryFiles().end(), auxiliaryFiles.begin(), auxiliaryFiles.end()); @@ -219,10 +370,10 @@ resolve( if (includeFlatEntriesForTargetBeingBuilt) { systemHeadermapFiles.push_back(headermapFileForOwnTargetHeaders); } - if (includeFrameworkEntriesForAllProductTypes) { - systemHeadermapFiles.push_back(headermapFileForAllTargetHeaders); - } else { + if (requiresVFS) { systemHeadermapFiles.push_back(headermapFileForAllNonFrameworkTargetHeaders); + } else { + systemHeadermapFiles.push_back(headermapFileForAllTargetHeaders); } userHeadermapFiles.push_back(headermapFileForGeneratedFiles); @@ -236,6 +387,7 @@ resolve( 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()); + headermapInfo->overlayVFS() = vfsAllProductHeaders; } std::unique_ptr Tool::HeadermapResolver::