18e35f1e7SKirill Bobyrev //===--- ModuleAssistant.cpp - Module map generation manager --*- C++ -*---===//
25ab4f111SJohn Thompson //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ab4f111SJohn Thompson //
78e35f1e7SKirill Bobyrev //===----------------------------------------------------------------------===//
85ab4f111SJohn Thompson //
95ab4f111SJohn Thompson // This file defines the module generation entry point function,
105ab4f111SJohn Thompson // createModuleMap, a Module class for representing a module,
115ab4f111SJohn Thompson // and various implementation functions for doing the underlying
125ab4f111SJohn Thompson // work, described below.
135ab4f111SJohn Thompson //
145ab4f111SJohn Thompson // The "Module" class represents a module, with members for storing the module
155ab4f111SJohn Thompson // name, associated header file names, and sub-modules, and an "output"
165ab4f111SJohn Thompson // function that recursively writes the module definitions.
175ab4f111SJohn Thompson //
185ab4f111SJohn Thompson // The "createModuleMap" function implements the top-level logic of the
195ab4f111SJohn Thompson // assistant mode.  It calls a loadModuleDescriptions function to walk
205ab4f111SJohn Thompson // the header list passed to it and creates a tree of Module objects
215ab4f111SJohn Thompson // representing the module hierarchy, represented by a "Module" object,
225ab4f111SJohn Thompson // the "RootModule".  This root module may or may not represent an actual
235ab4f111SJohn Thompson // module in the module map, depending on the "--root-module" option passed
245ab4f111SJohn Thompson // to modularize.  It then calls a writeModuleMap function to set up the
255ab4f111SJohn Thompson // module map file output and walk the module tree, outputting the module
265ab4f111SJohn Thompson // map file using a stream obtained and managed by an
273fc649cbSReid Kleckner // llvm::ToolOutputFile object.
285ab4f111SJohn Thompson //
298e35f1e7SKirill Bobyrev //===----------------------------------------------------------------------===//
305ab4f111SJohn Thompson 
315ab4f111SJohn Thompson #include "Modularize.h"
325ab4f111SJohn Thompson #include "llvm/ADT/SmallString.h"
335ab4f111SJohn Thompson #include "llvm/Support/FileSystem.h"
345ab4f111SJohn Thompson #include "llvm/Support/Path.h"
355ab4f111SJohn Thompson #include "llvm/Support/ToolOutputFile.h"
365ab4f111SJohn Thompson #include <vector>
375ab4f111SJohn Thompson 
385ab4f111SJohn Thompson // Local definitions:
395ab4f111SJohn Thompson 
405ab4f111SJohn Thompson namespace {
415ab4f111SJohn Thompson 
425ab4f111SJohn Thompson // Internal class definitions:
435ab4f111SJohn Thompson 
445ab4f111SJohn Thompson // Represents a module.
455ab4f111SJohn Thompson class Module {
465ab4f111SJohn Thompson public:
474018c624SJohn Thompson   Module(llvm::StringRef Name, bool Problem);
485ab4f111SJohn Thompson   ~Module();
495ab4f111SJohn Thompson   bool output(llvm::raw_fd_ostream &OS, int Indent);
505ab4f111SJohn Thompson   Module *findSubModule(llvm::StringRef SubName);
515ab4f111SJohn Thompson 
525ab4f111SJohn Thompson public:
535ab4f111SJohn Thompson   std::string Name;
545ab4f111SJohn Thompson   std::vector<std::string> HeaderFileNames;
555ab4f111SJohn Thompson   std::vector<Module *> SubModules;
564018c624SJohn Thompson   bool IsProblem;
575ab4f111SJohn Thompson };
585ab4f111SJohn Thompson 
595ab4f111SJohn Thompson } // end anonymous namespace.
605ab4f111SJohn Thompson 
615ab4f111SJohn Thompson // Module functions:
625ab4f111SJohn Thompson 
635ab4f111SJohn Thompson // Constructors.
Module(llvm::StringRef Name,bool Problem)644018c624SJohn Thompson Module::Module(llvm::StringRef Name, bool Problem)
654018c624SJohn Thompson   : Name(Name), IsProblem(Problem) {}
665ab4f111SJohn Thompson 
675ab4f111SJohn Thompson // Destructor.
~Module()685ab4f111SJohn Thompson Module::~Module() {
695ab4f111SJohn Thompson   // Free submodules.
70bf87a8b7SAlexander Kornienko   while (!SubModules.empty()) {
715ab4f111SJohn Thompson     Module *last = SubModules.back();
725ab4f111SJohn Thompson     SubModules.pop_back();
735ab4f111SJohn Thompson     delete last;
745ab4f111SJohn Thompson   }
755ab4f111SJohn Thompson }
765ab4f111SJohn Thompson 
775ab4f111SJohn Thompson // Write a module hierarchy to the given output stream.
output(llvm::raw_fd_ostream & OS,int Indent)785ab4f111SJohn Thompson bool Module::output(llvm::raw_fd_ostream &OS, int Indent) {
795ab4f111SJohn Thompson   // If this is not the nameless root module, start a module definition.
805ab4f111SJohn Thompson   if (Name.size() != 0) {
815ab4f111SJohn Thompson     OS.indent(Indent);
825ab4f111SJohn Thompson     OS << "module " << Name << " {\n";
835ab4f111SJohn Thompson     Indent += 2;
845ab4f111SJohn Thompson   }
855ab4f111SJohn Thompson 
865ab4f111SJohn Thompson   // Output submodules.
8708124b11SPiotr Padlewski   for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
885ab4f111SJohn Thompson     if (!(*I)->output(OS, Indent))
895ab4f111SJohn Thompson       return false;
905ab4f111SJohn Thompson   }
915ab4f111SJohn Thompson 
925ab4f111SJohn Thompson   // Output header files.
9308124b11SPiotr Padlewski   for (auto I = HeaderFileNames.begin(), E = HeaderFileNames.end(); I != E;
9408124b11SPiotr Padlewski        ++I) {
955ab4f111SJohn Thompson     OS.indent(Indent);
961c158c19SJohn Thompson     if (IsProblem || strstr((*I).c_str(), ".inl"))
974018c624SJohn Thompson       OS << "exclude header \"" << *I << "\"\n";
984018c624SJohn Thompson     else
995ab4f111SJohn Thompson       OS << "header \"" << *I << "\"\n";
1005ab4f111SJohn Thompson   }
1015ab4f111SJohn Thompson 
1025ab4f111SJohn Thompson   // If this module has header files, output export directive.
1035ab4f111SJohn Thompson   if (HeaderFileNames.size() != 0) {
1045ab4f111SJohn Thompson     OS.indent(Indent);
1055ab4f111SJohn Thompson     OS << "export *\n";
1065ab4f111SJohn Thompson   }
1075ab4f111SJohn Thompson 
1085ab4f111SJohn Thompson   // If this is not the nameless root module, close the module definition.
1095ab4f111SJohn Thompson   if (Name.size() != 0) {
1105ab4f111SJohn Thompson     Indent -= 2;
1115ab4f111SJohn Thompson     OS.indent(Indent);
1125ab4f111SJohn Thompson     OS << "}\n";
1135ab4f111SJohn Thompson   }
1145ab4f111SJohn Thompson 
1155ab4f111SJohn Thompson   return true;
1165ab4f111SJohn Thompson }
1175ab4f111SJohn Thompson 
1185ab4f111SJohn Thompson // Lookup a sub-module.
findSubModule(llvm::StringRef SubName)1195ab4f111SJohn Thompson Module *Module::findSubModule(llvm::StringRef SubName) {
12008124b11SPiotr Padlewski   for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
1215ab4f111SJohn Thompson     if ((*I)->Name == SubName)
1225ab4f111SJohn Thompson       return *I;
1235ab4f111SJohn Thompson   }
124f61be9c9SCraig Topper   return nullptr;
1255ab4f111SJohn Thompson }
1265ab4f111SJohn Thompson 
1275ab4f111SJohn Thompson // Implementation functions:
1285ab4f111SJohn Thompson 
1295d9862f0SJohn Thompson // Reserved keywords in module.modulemap syntax.
1305ab4f111SJohn Thompson // Keep in sync with keywords in module map parser in Lex/ModuleMap.cpp,
1315ab4f111SJohn Thompson // such as in ModuleMapParser::consumeToken().
13245857d4bSCraig Topper static const char *const ReservedNames[] = {
1335ab4f111SJohn Thompson   "config_macros", "export",   "module", "conflict", "framework",
1345ab4f111SJohn Thompson   "requires",      "exclude",  "header", "private",  "explicit",
135f61be9c9SCraig Topper   "link",          "umbrella", "extern", "use",      nullptr // Flag end.
1365ab4f111SJohn Thompson };
1375ab4f111SJohn Thompson 
13858983f13SAlp Toker // Convert module name to a non-keyword.
1395ab4f111SJohn Thompson // Prepends a '_' to the name if and only if the name is a keyword.
1405ab4f111SJohn Thompson static std::string
ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName)1415ab4f111SJohn Thompson ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName) {
142adcd0268SBenjamin Kramer   std::string SafeName(MightBeReservedName);
143f61be9c9SCraig Topper   for (int Index = 0; ReservedNames[Index] != nullptr; ++Index) {
1445ab4f111SJohn Thompson     if (MightBeReservedName == ReservedNames[Index]) {
1455ab4f111SJohn Thompson       SafeName.insert(0, "_");
1465ab4f111SJohn Thompson       break;
1475ab4f111SJohn Thompson     }
1485ab4f111SJohn Thompson   }
1495ab4f111SJohn Thompson   return SafeName;
1505ab4f111SJohn Thompson }
1515ab4f111SJohn Thompson 
1521c158c19SJohn Thompson // Convert module name to a non-keyword.
1531c158c19SJohn Thompson // Prepends a '_' to the name if and only if the name is a keyword.
1541c158c19SJohn Thompson static std::string
ensureVaidModuleName(llvm::StringRef MightBeInvalidName)1551c158c19SJohn Thompson ensureVaidModuleName(llvm::StringRef MightBeInvalidName) {
156adcd0268SBenjamin Kramer   std::string SafeName(MightBeInvalidName);
1571c158c19SJohn Thompson   std::replace(SafeName.begin(), SafeName.end(), '-', '_');
1581c158c19SJohn Thompson   std::replace(SafeName.begin(), SafeName.end(), '.', '_');
1591c158c19SJohn Thompson   if (isdigit(SafeName[0]))
1601c158c19SJohn Thompson     SafeName = "_" + SafeName;
1611c158c19SJohn Thompson   return SafeName;
1621c158c19SJohn Thompson }
1631c158c19SJohn Thompson 
1645ab4f111SJohn Thompson // Add one module, given a header file path.
addModuleDescription(Module * RootModule,llvm::StringRef HeaderFilePath,llvm::StringRef HeaderPrefix,DependencyMap & Dependencies,bool IsProblemFile)1655ab4f111SJohn Thompson static bool addModuleDescription(Module *RootModule,
1665ab4f111SJohn Thompson                                  llvm::StringRef HeaderFilePath,
1675ab4f111SJohn Thompson                                  llvm::StringRef HeaderPrefix,
1684018c624SJohn Thompson                                  DependencyMap &Dependencies,
1694018c624SJohn Thompson                                  bool IsProblemFile) {
1705ab4f111SJohn Thompson   Module *CurrentModule = RootModule;
1715ab4f111SJohn Thompson   DependentsVector &FileDependents = Dependencies[HeaderFilePath];
1725ab4f111SJohn Thompson   std::string FilePath;
1735ab4f111SJohn Thompson   // Strip prefix.
174bf5391d2SNAKAMURA Takumi   // HeaderFilePath should be compared to natively-canonicalized Prefix.
175bf5391d2SNAKAMURA Takumi   llvm::SmallString<256> NativePath, NativePrefix;
176bf5391d2SNAKAMURA Takumi   llvm::sys::path::native(HeaderFilePath, NativePath);
177bf5391d2SNAKAMURA Takumi   llvm::sys::path::native(HeaderPrefix, NativePrefix);
178bf5391d2SNAKAMURA Takumi   if (NativePath.startswith(NativePrefix))
179adcd0268SBenjamin Kramer     FilePath = std::string(NativePath.substr(NativePrefix.size() + 1));
1805ab4f111SJohn Thompson   else
181adcd0268SBenjamin Kramer     FilePath = std::string(HeaderFilePath);
1825ab4f111SJohn Thompson   int Count = FileDependents.size();
1835ab4f111SJohn Thompson   // Headers that go into modules must not depend on other files being
1845ab4f111SJohn Thompson   // included first.  If there are any dependents, warn user and omit.
1855ab4f111SJohn Thompson   if (Count != 0) {
1865ab4f111SJohn Thompson     llvm::errs() << "warning: " << FilePath
1875ab4f111SJohn Thompson                  << " depends on other headers being included first,"
1885d9862f0SJohn Thompson                     " meaning the module.modulemap won't compile."
1895ab4f111SJohn Thompson                     "  This header will be omitted from the module map.\n";
1905ab4f111SJohn Thompson     return true;
1915ab4f111SJohn Thompson   }
1925ab4f111SJohn Thompson   // Make canonical.
1935ab4f111SJohn Thompson   std::replace(FilePath.begin(), FilePath.end(), '\\', '/');
1945ab4f111SJohn Thompson   // Insert module into tree, using subdirectories as submodules.
1955ab4f111SJohn Thompson   for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath),
1965ab4f111SJohn Thompson                                        E = llvm::sys::path::end(FilePath);
1975ab4f111SJohn Thompson        I != E; ++I) {
1985ab4f111SJohn Thompson     if ((*I)[0] == '.')
1995ab4f111SJohn Thompson       continue;
200adcd0268SBenjamin Kramer     std::string Stem(llvm::sys::path::stem(*I));
2015ab4f111SJohn Thompson     Stem = ensureNoCollisionWithReservedName(Stem);
2021c158c19SJohn Thompson     Stem = ensureVaidModuleName(Stem);
2035ab4f111SJohn Thompson     Module *SubModule = CurrentModule->findSubModule(Stem);
204f61be9c9SCraig Topper     if (!SubModule) {
2054018c624SJohn Thompson       SubModule = new Module(Stem, IsProblemFile);
2065ab4f111SJohn Thompson       CurrentModule->SubModules.push_back(SubModule);
2075ab4f111SJohn Thompson     }
2085ab4f111SJohn Thompson     CurrentModule = SubModule;
2095ab4f111SJohn Thompson   }
2105ab4f111SJohn Thompson   // Add header file name to headers.
2115ab4f111SJohn Thompson   CurrentModule->HeaderFileNames.push_back(FilePath);
2125ab4f111SJohn Thompson   return true;
2135ab4f111SJohn Thompson }
2145ab4f111SJohn Thompson 
2155ab4f111SJohn Thompson // Create the internal module tree representation.
loadModuleDescriptions(llvm::StringRef RootModuleName,llvm::ArrayRef<std::string> HeaderFileNames,llvm::ArrayRef<std::string> ProblemFileNames,DependencyMap & Dependencies,llvm::StringRef HeaderPrefix)2165ab4f111SJohn Thompson static Module *loadModuleDescriptions(
2175ab4f111SJohn Thompson     llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> HeaderFileNames,
2184018c624SJohn Thompson     llvm::ArrayRef<std::string> ProblemFileNames,
2195ab4f111SJohn Thompson     DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) {
2205ab4f111SJohn Thompson 
2215ab4f111SJohn Thompson   // Create root module.
22208124b11SPiotr Padlewski   auto *RootModule = new Module(RootModuleName, false);
2235ab4f111SJohn Thompson 
2245ab4f111SJohn Thompson   llvm::SmallString<256> CurrentDirectory;
2255ab4f111SJohn Thompson   llvm::sys::fs::current_path(CurrentDirectory);
2265ab4f111SJohn Thompson 
2275ab4f111SJohn Thompson   // If no header prefix, use current directory.
2285ab4f111SJohn Thompson   if (HeaderPrefix.size() == 0)
2295ab4f111SJohn Thompson     HeaderPrefix = CurrentDirectory;
2305ab4f111SJohn Thompson 
2315ab4f111SJohn Thompson   // Walk the header file names and output the module map.
2325ab4f111SJohn Thompson   for (llvm::ArrayRef<std::string>::iterator I = HeaderFileNames.begin(),
2335ab4f111SJohn Thompson                                              E = HeaderFileNames.end();
2345ab4f111SJohn Thompson        I != E; ++I) {
2354018c624SJohn Thompson     std::string Header(*I);
2364018c624SJohn Thompson     bool IsProblemFile = false;
2374018c624SJohn Thompson     for (auto &ProblemFile : ProblemFileNames) {
2384018c624SJohn Thompson       if (ProblemFile == Header) {
2394018c624SJohn Thompson         IsProblemFile = true;
2404018c624SJohn Thompson         break;
2414018c624SJohn Thompson       }
2424018c624SJohn Thompson     }
2435ab4f111SJohn Thompson     // Add as a module.
2444018c624SJohn Thompson     if (!addModuleDescription(RootModule, Header, HeaderPrefix, Dependencies, IsProblemFile))
245f61be9c9SCraig Topper       return nullptr;
2465ab4f111SJohn Thompson   }
2475ab4f111SJohn Thompson 
2485ab4f111SJohn Thompson   return RootModule;
2495ab4f111SJohn Thompson }
2505ab4f111SJohn Thompson 
2515ab4f111SJohn Thompson // Kick off the writing of the module map.
writeModuleMap(llvm::StringRef ModuleMapPath,llvm::StringRef HeaderPrefix,Module * RootModule)2525ab4f111SJohn Thompson static bool writeModuleMap(llvm::StringRef ModuleMapPath,
2535ab4f111SJohn Thompson                            llvm::StringRef HeaderPrefix, Module *RootModule) {
2545ab4f111SJohn Thompson   llvm::SmallString<256> HeaderDirectory(ModuleMapPath);
2555ab4f111SJohn Thompson   llvm::sys::path::remove_filename(HeaderDirectory);
2565ab4f111SJohn Thompson   llvm::SmallString<256> FilePath;
2575ab4f111SJohn Thompson 
2585ab4f111SJohn Thompson   // Get the module map file path to be used.
2595ab4f111SJohn Thompson   if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) {
2605ab4f111SJohn Thompson     FilePath = HeaderPrefix;
2615ab4f111SJohn Thompson     // Prepend header file name prefix if it's not absolute.
2625ab4f111SJohn Thompson     llvm::sys::path::append(FilePath, ModuleMapPath);
2635ab4f111SJohn Thompson     llvm::sys::path::native(FilePath);
2645ab4f111SJohn Thompson   } else {
2655ab4f111SJohn Thompson     FilePath = ModuleMapPath;
2665ab4f111SJohn Thompson     llvm::sys::path::native(FilePath);
2675ab4f111SJohn Thompson   }
2685ab4f111SJohn Thompson 
2695ab4f111SJohn Thompson   // Set up module map output file.
270b14bd53eSRafael Espindola   std::error_code EC;
271*82b3e28eSAbhina Sreeskantharajan   llvm::ToolOutputFile Out(FilePath, EC, llvm::sys::fs::OF_TextWithCRLF);
272b14bd53eSRafael Espindola   if (EC) {
273b14bd53eSRafael Espindola     llvm::errs() << Argv0 << ": error opening " << FilePath << ":"
274b14bd53eSRafael Espindola                  << EC.message() << "\n";
2755ab4f111SJohn Thompson     return false;
2765ab4f111SJohn Thompson   }
2775ab4f111SJohn Thompson 
2785ab4f111SJohn Thompson   // Get output stream from tool output buffer/manager.
2795ab4f111SJohn Thompson   llvm::raw_fd_ostream &OS = Out.os();
2805ab4f111SJohn Thompson 
2815ab4f111SJohn Thompson   // Output file comment.
2825ab4f111SJohn Thompson   OS << "// " << ModuleMapPath << "\n";
2835ab4f111SJohn Thompson   OS << "// Generated by: " << CommandLine << "\n\n";
2845ab4f111SJohn Thompson 
2855ab4f111SJohn Thompson   // Write module hierarchy from internal representation.
2865ab4f111SJohn Thompson   if (!RootModule->output(OS, 0))
2875ab4f111SJohn Thompson     return false;
2885ab4f111SJohn Thompson 
2893fc649cbSReid Kleckner   // Tell ToolOutputFile that we want to keep the file.
2905ab4f111SJohn Thompson   Out.keep();
2915ab4f111SJohn Thompson 
2925ab4f111SJohn Thompson   return true;
2935ab4f111SJohn Thompson }
2945ab4f111SJohn Thompson 
2955ab4f111SJohn Thompson // Global functions:
2965ab4f111SJohn Thompson 
2975ab4f111SJohn Thompson // Module map generation entry point.
createModuleMap(llvm::StringRef ModuleMapPath,llvm::ArrayRef<std::string> HeaderFileNames,llvm::ArrayRef<std::string> ProblemFileNames,DependencyMap & Dependencies,llvm::StringRef HeaderPrefix,llvm::StringRef RootModuleName)2985ab4f111SJohn Thompson bool createModuleMap(llvm::StringRef ModuleMapPath,
2995ab4f111SJohn Thompson                      llvm::ArrayRef<std::string> HeaderFileNames,
3004018c624SJohn Thompson                      llvm::ArrayRef<std::string> ProblemFileNames,
3015ab4f111SJohn Thompson                      DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
3025ab4f111SJohn Thompson                      llvm::StringRef RootModuleName) {
3035ab4f111SJohn Thompson   // Load internal representation of modules.
3044018c624SJohn Thompson   std::unique_ptr<Module> RootModule(
3054018c624SJohn Thompson     loadModuleDescriptions(
3064018c624SJohn Thompson       RootModuleName, HeaderFileNames, ProblemFileNames, Dependencies,
3074018c624SJohn Thompson       HeaderPrefix));
3085ab4f111SJohn Thompson   if (!RootModule.get())
3095ab4f111SJohn Thompson     return false;
3105ab4f111SJohn Thompson 
3115ab4f111SJohn Thompson   // Write module map file.
3125ab4f111SJohn Thompson   return writeModuleMap(ModuleMapPath, HeaderPrefix, RootModule.get());
3135ab4f111SJohn Thompson }
314