llvm-project
313 строк · 10.4 Кб
1//===--- ModuleAssistant.cpp - Module map generation manager --*- C++ -*---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the module generation entry point function,
10// createModuleMap, a Module class for representing a module,
11// and various implementation functions for doing the underlying
12// work, described below.
13//
14// The "Module" class represents a module, with members for storing the module
15// name, associated header file names, and sub-modules, and an "output"
16// function that recursively writes the module definitions.
17//
18// The "createModuleMap" function implements the top-level logic of the
19// assistant mode. It calls a loadModuleDescriptions function to walk
20// the header list passed to it and creates a tree of Module objects
21// representing the module hierarchy, represented by a "Module" object,
22// the "RootModule". This root module may or may not represent an actual
23// module in the module map, depending on the "--root-module" option passed
24// to modularize. It then calls a writeModuleMap function to set up the
25// module map file output and walk the module tree, outputting the module
26// map file using a stream obtained and managed by an
27// llvm::ToolOutputFile object.
28//
29//===----------------------------------------------------------------------===//
30
31#include "Modularize.h"32#include "llvm/ADT/SmallString.h"33#include "llvm/Support/FileSystem.h"34#include "llvm/Support/Path.h"35#include "llvm/Support/ToolOutputFile.h"36#include <vector>37
38// Local definitions:
39
40namespace {41
42// Internal class definitions:
43
44// Represents a module.
45class Module {46public:47Module(llvm::StringRef Name, bool Problem);48~Module();49bool output(llvm::raw_fd_ostream &OS, int Indent);50Module *findSubModule(llvm::StringRef SubName);51
52public:53std::string Name;54std::vector<std::string> HeaderFileNames;55std::vector<Module *> SubModules;56bool IsProblem;57};58
59} // end anonymous namespace.60
61// Module functions:
62
63// Constructors.
64Module::Module(llvm::StringRef Name, bool Problem)65: Name(Name), IsProblem(Problem) {}66
67// Destructor.
68Module::~Module() {69// Free submodules.70while (!SubModules.empty()) {71Module *last = SubModules.back();72SubModules.pop_back();73delete last;74}75}
76
77// Write a module hierarchy to the given output stream.
78bool Module::output(llvm::raw_fd_ostream &OS, int Indent) {79// If this is not the nameless root module, start a module definition.80if (Name.size() != 0) {81OS.indent(Indent);82OS << "module " << Name << " {\n";83Indent += 2;84}85
86// Output submodules.87for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {88if (!(*I)->output(OS, Indent))89return false;90}91
92// Output header files.93for (auto I = HeaderFileNames.begin(), E = HeaderFileNames.end(); I != E;94++I) {95OS.indent(Indent);96if (IsProblem || strstr((*I).c_str(), ".inl"))97OS << "exclude header \"" << *I << "\"\n";98else99OS << "header \"" << *I << "\"\n";100}101
102// If this module has header files, output export directive.103if (HeaderFileNames.size() != 0) {104OS.indent(Indent);105OS << "export *\n";106}107
108// If this is not the nameless root module, close the module definition.109if (Name.size() != 0) {110Indent -= 2;111OS.indent(Indent);112OS << "}\n";113}114
115return true;116}
117
118// Lookup a sub-module.
119Module *Module::findSubModule(llvm::StringRef SubName) {120for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {121if ((*I)->Name == SubName)122return *I;123}124return nullptr;125}
126
127// Implementation functions:
128
129// Reserved keywords in module.modulemap syntax.
130// Keep in sync with keywords in module map parser in Lex/ModuleMap.cpp,
131// such as in ModuleMapParser::consumeToken().
132static const char *const ReservedNames[] = {133"config_macros", "export", "module", "conflict", "framework",134"requires", "exclude", "header", "private", "explicit",135"link", "umbrella", "extern", "use", nullptr // Flag end.136};137
138// Convert module name to a non-keyword.
139// Prepends a '_' to the name if and only if the name is a keyword.
140static std::string141ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName) {142std::string SafeName(MightBeReservedName);143for (int Index = 0; ReservedNames[Index] != nullptr; ++Index) {144if (MightBeReservedName == ReservedNames[Index]) {145SafeName.insert(0, "_");146break;147}148}149return SafeName;150}
151
152// Convert module name to a non-keyword.
153// Prepends a '_' to the name if and only if the name is a keyword.
154static std::string155ensureVaidModuleName(llvm::StringRef MightBeInvalidName) {156std::string SafeName(MightBeInvalidName);157std::replace(SafeName.begin(), SafeName.end(), '-', '_');158std::replace(SafeName.begin(), SafeName.end(), '.', '_');159if (isdigit(SafeName[0]))160SafeName = "_" + SafeName;161return SafeName;162}
163
164// Add one module, given a header file path.
165static bool addModuleDescription(Module *RootModule,166llvm::StringRef HeaderFilePath,167llvm::StringRef HeaderPrefix,168DependencyMap &Dependencies,169bool IsProblemFile) {170Module *CurrentModule = RootModule;171DependentsVector &FileDependents = Dependencies[HeaderFilePath];172std::string FilePath;173// Strip prefix.174// HeaderFilePath should be compared to natively-canonicalized Prefix.175llvm::SmallString<256> NativePath, NativePrefix;176llvm::sys::path::native(HeaderFilePath, NativePath);177llvm::sys::path::native(HeaderPrefix, NativePrefix);178if (NativePath.starts_with(NativePrefix))179FilePath = std::string(NativePath.substr(NativePrefix.size() + 1));180else181FilePath = std::string(HeaderFilePath);182int Count = FileDependents.size();183// Headers that go into modules must not depend on other files being184// included first. If there are any dependents, warn user and omit.185if (Count != 0) {186llvm::errs() << "warning: " << FilePath187<< " depends on other headers being included first,"188" meaning the module.modulemap won't compile."189" This header will be omitted from the module map.\n";190return true;191}192// Make canonical.193std::replace(FilePath.begin(), FilePath.end(), '\\', '/');194// Insert module into tree, using subdirectories as submodules.195for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath),196E = llvm::sys::path::end(FilePath);197I != E; ++I) {198if ((*I)[0] == '.')199continue;200std::string Stem(llvm::sys::path::stem(*I));201Stem = ensureNoCollisionWithReservedName(Stem);202Stem = ensureVaidModuleName(Stem);203Module *SubModule = CurrentModule->findSubModule(Stem);204if (!SubModule) {205SubModule = new Module(Stem, IsProblemFile);206CurrentModule->SubModules.push_back(SubModule);207}208CurrentModule = SubModule;209}210// Add header file name to headers.211CurrentModule->HeaderFileNames.push_back(FilePath);212return true;213}
214
215// Create the internal module tree representation.
216static Module *loadModuleDescriptions(217llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> HeaderFileNames,218llvm::ArrayRef<std::string> ProblemFileNames,219DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) {220
221// Create root module.222auto *RootModule = new Module(RootModuleName, false);223
224llvm::SmallString<256> CurrentDirectory;225llvm::sys::fs::current_path(CurrentDirectory);226
227// If no header prefix, use current directory.228if (HeaderPrefix.size() == 0)229HeaderPrefix = CurrentDirectory;230
231// Walk the header file names and output the module map.232for (llvm::ArrayRef<std::string>::iterator I = HeaderFileNames.begin(),233E = HeaderFileNames.end();234I != E; ++I) {235std::string Header(*I);236bool IsProblemFile = false;237for (auto &ProblemFile : ProblemFileNames) {238if (ProblemFile == Header) {239IsProblemFile = true;240break;241}242}243// Add as a module.244if (!addModuleDescription(RootModule, Header, HeaderPrefix, Dependencies, IsProblemFile))245return nullptr;246}247
248return RootModule;249}
250
251// Kick off the writing of the module map.
252static bool writeModuleMap(llvm::StringRef ModuleMapPath,253llvm::StringRef HeaderPrefix, Module *RootModule) {254llvm::SmallString<256> HeaderDirectory(ModuleMapPath);255llvm::sys::path::remove_filename(HeaderDirectory);256llvm::SmallString<256> FilePath;257
258// Get the module map file path to be used.259if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) {260FilePath = HeaderPrefix;261// Prepend header file name prefix if it's not absolute.262llvm::sys::path::append(FilePath, ModuleMapPath);263llvm::sys::path::native(FilePath);264} else {265FilePath = ModuleMapPath;266llvm::sys::path::native(FilePath);267}268
269// Set up module map output file.270std::error_code EC;271llvm::ToolOutputFile Out(FilePath, EC, llvm::sys::fs::OF_TextWithCRLF);272if (EC) {273llvm::errs() << Argv0 << ": error opening " << FilePath << ":"274<< EC.message() << "\n";275return false;276}277
278// Get output stream from tool output buffer/manager.279llvm::raw_fd_ostream &OS = Out.os();280
281// Output file comment.282OS << "// " << ModuleMapPath << "\n";283OS << "// Generated by: " << CommandLine << "\n\n";284
285// Write module hierarchy from internal representation.286if (!RootModule->output(OS, 0))287return false;288
289// Tell ToolOutputFile that we want to keep the file.290Out.keep();291
292return true;293}
294
295// Global functions:
296
297// Module map generation entry point.
298bool createModuleMap(llvm::StringRef ModuleMapPath,299llvm::ArrayRef<std::string> HeaderFileNames,300llvm::ArrayRef<std::string> ProblemFileNames,301DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,302llvm::StringRef RootModuleName) {303// Load internal representation of modules.304std::unique_ptr<Module> RootModule(305loadModuleDescriptions(306RootModuleName, HeaderFileNames, ProblemFileNames, Dependencies,307HeaderPrefix));308if (!RootModule)309return false;310
311// Write module map file.312return writeModuleMap(ModuleMapPath, HeaderPrefix, RootModule.get());313}
314