Author: benlangmuir Date: Thu Aug 13 12:13:33 2015 New Revision: 244912 URL: http://llvm.org/viewvc/llvm-project?rev=244912&view=rev Log: [Modules] Add Darwin-specific compatibility module map parsing hacks
This preserves backwards compatibility for two hacks in the Darwin system module map files: 1. The use of 'requires excluded' to make headers non-modular, which should really be mapped to 'textual' now that we have this feature. 2. Silently removes a bogus cplusplus requirement from IOKit.avc. Once we start diagnosing missing requirements and headers on auto-imports these would have broken compatibility with existing Darwin SDKs. Added: cfe/trunk/test/Modules/Inputs/System/usr/include/assert.h cfe/trunk/test/Modules/Inputs/System/usr/include/tcl-private/ cfe/trunk/test/Modules/Inputs/System/usr/include/tcl-private/header.h cfe/trunk/test/Modules/darwin_specific_modulemap_hacks.m Modified: cfe/trunk/include/clang/Basic/Module.h cfe/trunk/lib/Basic/Module.cpp cfe/trunk/lib/Lex/ModuleMap.cpp cfe/trunk/test/Modules/Inputs/System/usr/include/module.map Modified: cfe/trunk/include/clang/Basic/Module.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Module.h?rev=244912&r1=244911&r2=244912&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Module.h (original) +++ cfe/trunk/include/clang/Basic/Module.h Thu Aug 13 12:13:33 2015 @@ -356,6 +356,12 @@ public: /// its top-level module. std::string getFullModuleName() const; + /// \brief Whether the full name of this module is equal to joining + /// \p nameParts with "."s. + /// + /// This is more efficient than getFullModuleName(). + bool fullModuleNameIs(ArrayRef<StringRef> nameParts) const; + /// \brief Retrieve the top-level module for this (sub)module, which may /// be this module. Module *getTopLevelModule() { Modified: cfe/trunk/lib/Basic/Module.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Module.cpp?rev=244912&r1=244911&r2=244912&view=diff ============================================================================== --- cfe/trunk/lib/Basic/Module.cpp (original) +++ cfe/trunk/lib/Basic/Module.cpp Thu Aug 13 12:13:33 2015 @@ -139,6 +139,15 @@ std::string Module::getFullModuleName() return Result; } +bool Module::fullModuleNameIs(ArrayRef<StringRef> nameParts) const { + for (const Module *M = this; M; M = M->Parent) { + if (nameParts.empty() || M->Name != nameParts.back()) + return false; + nameParts = nameParts.drop_back(); + } + return nameParts.empty(); +} + Module::DirectoryName Module::getUmbrellaDir() const { if (Header U = getUmbrellaHeader()) return {"", U.Entry->getDir()}; Modified: cfe/trunk/lib/Lex/ModuleMap.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/ModuleMap.cpp?rev=244912&r1=244911&r2=244912&view=diff ============================================================================== --- cfe/trunk/lib/Lex/ModuleMap.cpp (original) +++ cfe/trunk/lib/Lex/ModuleMap.cpp Thu Aug 13 12:13:33 2015 @@ -1015,7 +1015,17 @@ namespace clang { /// \brief The active module. Module *ActiveModule; - + + /// \brief Whether a module uses the 'requires excluded' hack to mark its + /// contents as 'textual'. + /// + /// On older Darwin SDK versions, 'requires excluded' is used to mark the + /// contents of the Darwin.C.excluded (assert.h) and Tcl.Private modules as + /// non-modular headers. For backwards compatibility, we continue to + /// support this idiom for just these modules, and map the headers to + /// 'textual' to match the original intent. + llvm::SmallPtrSet<Module *, 2> UsesRequiresExcludedHack; + /// \brief Consume the current token and return its location. SourceLocation consumeToken(); @@ -1570,6 +1580,38 @@ void ModuleMapParser::parseExternModuleD : File->getDir(), ExternLoc); } +/// Whether to add the requirement \p Feature to the module \p M. +/// +/// This preserves backwards compatibility for two hacks in the Darwin system +/// module map files: +/// +/// 1. The use of 'requires excluded' to make headers non-modular, which +/// should really be mapped to 'textual' now that we have this feature. We +/// drop the 'excluded' requirement, and set \p IsRequiresExcludedHack to +/// true. Later, this bit will be used to map all the headers inside this +/// module to 'textual'. +/// +/// This affects Darwin.C.excluded (for assert.h) and Tcl.Private. +/// +/// 2. Removes a bogus cplusplus requirement from IOKit.avc. This requirement +/// was never correct and causes issues now that we check it, so drop it. +static bool shouldAddRequirement(Module *M, StringRef Feature, + bool &IsRequiresExcludedHack) { + static const StringRef DarwinCExcluded[] = {"Darwin", "C", "excluded"}; + static const StringRef TclPrivate[] = {"Tcl", "Private"}; + static const StringRef IOKitAVC[] = {"IOKit", "avc"}; + + if (Feature == "excluded" && (M->fullModuleNameIs(DarwinCExcluded) || + M->fullModuleNameIs(TclPrivate))) { + IsRequiresExcludedHack = true; + return false; + } else if (Feature == "cplusplus" && M->fullModuleNameIs(IOKitAVC)) { + return false; + } + + return true; +} + /// \brief Parse a requires declaration. /// /// requires-declaration: @@ -1605,9 +1647,18 @@ void ModuleMapParser::parseRequiresDecl( std::string Feature = Tok.getString(); consumeToken(); - // Add this feature. - ActiveModule->addRequirement(Feature, RequiredState, - Map.LangOpts, *Map.Target); + bool IsRequiresExcludedHack = false; + bool ShouldAddRequirement = + shouldAddRequirement(ActiveModule, Feature, IsRequiresExcludedHack); + + if (IsRequiresExcludedHack) + UsesRequiresExcludedHack.insert(ActiveModule); + + if (ShouldAddRequirement) { + // Add this feature. + ActiveModule->addRequirement(Feature, RequiredState, Map.LangOpts, + *Map.Target); + } if (!Tok.is(MMToken::Comma)) break; @@ -1657,9 +1708,16 @@ void ModuleMapParser::parseHeaderDecl(MM consumeToken(); } } + if (LeadingToken == MMToken::TextualKeyword) Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader); + if (UsesRequiresExcludedHack.count(ActiveModule)) { + // Mark this header 'textual' (see doc comment for + // Module::UsesRequiresExcludedHack). + Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader); + } + if (LeadingToken != MMToken::HeaderKeyword) { if (!Tok.is(MMToken::HeaderKeyword)) { Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) @@ -1838,14 +1896,40 @@ void ModuleMapParser::parseUmbrellaDirDe HadError = true; return; } - + + if (UsesRequiresExcludedHack.count(ActiveModule)) { + // Mark this header 'textual' (see doc comment for + // ModuleMapParser::UsesRequiresExcludedHack). Although iterating over the + // directory is relatively expensive, in practice this only applies to the + // uncommonly used Tcl module on Darwin platforms. + std::error_code EC; + SmallVector<Module::Header, 6> Headers; + for (llvm::sys::fs::recursive_directory_iterator I(Dir->getName(), EC), E; + I != E && !EC; I.increment(EC)) { + if (const FileEntry *FE = SourceMgr.getFileManager().getFile(I->path())) { + + Module::Header Header = {I->path(), FE}; + Headers.push_back(std::move(Header)); + } + } + + // Sort header paths so that the pcm doesn't depend on iteration order. + llvm::array_pod_sort(Headers.begin(), Headers.end(), + [](const Module::Header *A, const Module::Header *B) { + return A->NameAsWritten.compare(B->NameAsWritten); + }); + for (auto &Header : Headers) + Map.addHeader(ActiveModule, std::move(Header), ModuleMap::TextualHeader); + return; + } + if (Module *OwningModule = Map.UmbrellaDirs[Dir]) { Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash) << OwningModule->getFullModuleName(); HadError = true; return; - } - + } + // Record this umbrella directory. Map.setUmbrellaDir(ActiveModule, Dir, DirName); } Added: cfe/trunk/test/Modules/Inputs/System/usr/include/assert.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/System/usr/include/assert.h?rev=244912&view=auto ============================================================================== --- cfe/trunk/test/Modules/Inputs/System/usr/include/assert.h (added) +++ cfe/trunk/test/Modules/Inputs/System/usr/include/assert.h Thu Aug 13 12:13:33 2015 @@ -0,0 +1,2 @@ +// assert.h +#define DARWIN_C_EXCLUDED 1 Modified: cfe/trunk/test/Modules/Inputs/System/usr/include/module.map URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/System/usr/include/module.map?rev=244912&r1=244911&r2=244912&view=diff ============================================================================== --- cfe/trunk/test/Modules/Inputs/System/usr/include/module.map (original) +++ cfe/trunk/test/Modules/Inputs/System/usr/include/module.map Thu Aug 13 12:13:33 2015 @@ -30,3 +30,25 @@ module uses_other_constants { header "uses_other_constants.h" export * } + +module Darwin { + module C { + module excluded { + requires excluded + header "assert.h" + } + } +} + +module Tcl { + module Private { + requires excluded + umbrella "" + } +} + +module IOKit { + module avc { + requires cplusplus + } +} Added: cfe/trunk/test/Modules/Inputs/System/usr/include/tcl-private/header.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/System/usr/include/tcl-private/header.h?rev=244912&view=auto ============================================================================== --- cfe/trunk/test/Modules/Inputs/System/usr/include/tcl-private/header.h (added) +++ cfe/trunk/test/Modules/Inputs/System/usr/include/tcl-private/header.h Thu Aug 13 12:13:33 2015 @@ -0,0 +1,2 @@ +// tcl-private/header.h +#define TCL_PRIVATE 1 Added: cfe/trunk/test/Modules/darwin_specific_modulemap_hacks.m URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/darwin_specific_modulemap_hacks.m?rev=244912&view=auto ============================================================================== --- cfe/trunk/test/Modules/darwin_specific_modulemap_hacks.m (added) +++ cfe/trunk/test/Modules/darwin_specific_modulemap_hacks.m Thu Aug 13 12:13:33 2015 @@ -0,0 +1,22 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -isystem %S/Inputs/System/usr/include -triple x86_64-apple-darwin10 %s -verify -fsyntax-only +// expected-no-diagnostics + +@import Darwin.C.excluded; // no error, header is implicitly 'textual' +@import Tcl.Private; // no error, header is implicitly 'textual' +@import IOKit.avc; // no error, cplusplus requirement removed + +#if defined(DARWIN_C_EXCLUDED) +#error assert.h should be textual +#elif defined(TCL_PRIVATE) +#error tcl-private/header.h should be textual +#endif + +#import <assert.h> +#import <tcl-private/header.h> + +#if !defined(DARWIN_C_EXCLUDED) +#error assert.h missing +#elif !defined(TCL_PRIVATE) +#error tcl-private/header.h missing +#endif _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits