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