clang-tools 20.0.0git
ModuleAssistant.cpp
Go to the documentation of this file.
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:
47 Module(llvm::StringRef Name, bool Problem);
48 ~Module();
49 Module(const Module &other) = delete;
50 Module &operator=(const Module &other) = delete;
51 bool output(llvm::raw_fd_ostream &OS, int Indent);
52 Module *findSubModule(llvm::StringRef SubName);
53
54public:
55 std::string Name;
56 std::vector<std::string> HeaderFileNames;
57 std::vector<Module *> SubModules;
58 bool IsProblem;
59};
60
61} // end anonymous namespace.
62
63// Module functions:
64
65// Constructors.
66Module::Module(llvm::StringRef Name, bool Problem)
67 : Name(Name), IsProblem(Problem) {}
68
69// Destructor.
70Module::~Module() {
71 // Free submodules.
72 while (!SubModules.empty()) {
73 Module *last = SubModules.back();
74 SubModules.pop_back();
75 delete last;
76 }
77}
78
79// Write a module hierarchy to the given output stream.
80bool Module::output(llvm::raw_fd_ostream &OS, int Indent) {
81 // If this is not the nameless root module, start a module definition.
82 if (Name.size() != 0) {
83 OS.indent(Indent);
84 OS << "module " << Name << " {\n";
85 Indent += 2;
86 }
87
88 // Output submodules.
89 for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
90 if (!(*I)->output(OS, Indent))
91 return false;
92 }
93
94 // Output header files.
95 for (auto I = HeaderFileNames.begin(), E = HeaderFileNames.end(); I != E;
96 ++I) {
97 OS.indent(Indent);
98 if (IsProblem || strstr((*I).c_str(), ".inl"))
99 OS << "exclude header \"" << *I << "\"\n";
100 else
101 OS << "header \"" << *I << "\"\n";
102 }
103
104 // If this module has header files, output export directive.
105 if (HeaderFileNames.size() != 0) {
106 OS.indent(Indent);
107 OS << "export *\n";
108 }
109
110 // If this is not the nameless root module, close the module definition.
111 if (Name.size() != 0) {
112 Indent -= 2;
113 OS.indent(Indent);
114 OS << "}\n";
115 }
116
117 return true;
118}
119
120// Lookup a sub-module.
121Module *Module::findSubModule(llvm::StringRef SubName) {
122 for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
123 if ((*I)->Name == SubName)
124 return *I;
125 }
126 return nullptr;
127}
128
129// Implementation functions:
130
131// Reserved keywords in module.modulemap syntax.
132// Keep in sync with keywords in module map parser in Lex/ModuleMap.cpp,
133// such as in ModuleMapParser::consumeToken().
134static const char *const ReservedNames[] = {
135 "config_macros", "export", "module", "conflict", "framework",
136 "requires", "exclude", "header", "private", "explicit",
137 "link", "umbrella", "extern", "use", nullptr // Flag end.
138};
139
140// Convert module name to a non-keyword.
141// Prepends a '_' to the name if and only if the name is a keyword.
142static std::string
143ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName) {
144 std::string SafeName(MightBeReservedName);
145 for (int Index = 0; ReservedNames[Index] != nullptr; ++Index) {
146 if (MightBeReservedName == ReservedNames[Index]) {
147 SafeName.insert(0, "_");
148 break;
149 }
150 }
151 return SafeName;
152}
153
154// Convert module name to a non-keyword.
155// Prepends a '_' to the name if and only if the name is a keyword.
156static std::string
157ensureVaidModuleName(llvm::StringRef MightBeInvalidName) {
158 std::string SafeName(MightBeInvalidName);
159 std::replace(SafeName.begin(), SafeName.end(), '-', '_');
160 std::replace(SafeName.begin(), SafeName.end(), '.', '_');
161 if (isdigit(SafeName[0]))
162 SafeName = "_" + SafeName;
163 return SafeName;
164}
165
166// Add one module, given a header file path.
168 llvm::StringRef HeaderFilePath,
169 llvm::StringRef HeaderPrefix,
170 DependencyMap &Dependencies,
171 bool IsProblemFile) {
172 Module *CurrentModule = RootModule;
173 DependentsVector &FileDependents = Dependencies[HeaderFilePath];
174 std::string FilePath;
175 // Strip prefix.
176 // HeaderFilePath should be compared to natively-canonicalized Prefix.
177 llvm::SmallString<256> NativePath, NativePrefix;
178 llvm::sys::path::native(HeaderFilePath, NativePath);
179 llvm::sys::path::native(HeaderPrefix, NativePrefix);
180 if (NativePath.starts_with(NativePrefix))
181 FilePath = std::string(NativePath.substr(NativePrefix.size() + 1));
182 else
183 FilePath = std::string(HeaderFilePath);
184 int Count = FileDependents.size();
185 // Headers that go into modules must not depend on other files being
186 // included first. If there are any dependents, warn user and omit.
187 if (Count != 0) {
188 llvm::errs() << "warning: " << FilePath
189 << " depends on other headers being included first,"
190 " meaning the module.modulemap won't compile."
191 " This header will be omitted from the module map.\n";
192 return true;
193 }
194 // Make canonical.
195 std::replace(FilePath.begin(), FilePath.end(), '\\', '/');
196 // Insert module into tree, using subdirectories as submodules.
197 for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath),
198 E = llvm::sys::path::end(FilePath);
199 I != E; ++I) {
200 if ((*I)[0] == '.')
201 continue;
202 std::string Stem(llvm::sys::path::stem(*I));
204 Stem = ensureVaidModuleName(Stem);
205 Module *SubModule = CurrentModule->findSubModule(Stem);
206 if (!SubModule) {
207 SubModule = new Module(Stem, IsProblemFile);
208 CurrentModule->SubModules.push_back(SubModule);
209 }
210 CurrentModule = SubModule;
211 }
212 // Add header file name to headers.
213 CurrentModule->HeaderFileNames.push_back(FilePath);
214 return true;
215}
216
217// Create the internal module tree representation.
219 llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> HeaderFileNames,
220 llvm::ArrayRef<std::string> ProblemFileNames,
221 DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) {
222
223 // Create root module.
224 auto *RootModule = new Module(RootModuleName, false);
225
226 llvm::SmallString<256> CurrentDirectory;
227 llvm::sys::fs::current_path(CurrentDirectory);
228
229 // If no header prefix, use current directory.
230 if (HeaderPrefix.size() == 0)
231 HeaderPrefix = CurrentDirectory;
232
233 // Walk the header file names and output the module map.
234 for (llvm::ArrayRef<std::string>::iterator I = HeaderFileNames.begin(),
235 E = HeaderFileNames.end();
236 I != E; ++I) {
237 std::string Header(*I);
238 bool IsProblemFile = false;
239 for (auto &ProblemFile : ProblemFileNames) {
240 if (ProblemFile == Header) {
241 IsProblemFile = true;
242 break;
243 }
244 }
245 // Add as a module.
246 if (!addModuleDescription(RootModule, Header, HeaderPrefix, Dependencies, IsProblemFile))
247 return nullptr;
248 }
249
250 return RootModule;
251}
252
253// Kick off the writing of the module map.
254static bool writeModuleMap(llvm::StringRef ModuleMapPath,
255 llvm::StringRef HeaderPrefix, Module *RootModule) {
256 llvm::SmallString<256> HeaderDirectory(ModuleMapPath);
257 llvm::sys::path::remove_filename(HeaderDirectory);
258 llvm::SmallString<256> FilePath;
259
260 // Get the module map file path to be used.
261 if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) {
262 FilePath = HeaderPrefix;
263 // Prepend header file name prefix if it's not absolute.
264 llvm::sys::path::append(FilePath, ModuleMapPath);
265 llvm::sys::path::native(FilePath);
266 } else {
267 FilePath = ModuleMapPath;
268 llvm::sys::path::native(FilePath);
269 }
270
271 // Set up module map output file.
272 std::error_code EC;
273 llvm::ToolOutputFile Out(FilePath, EC, llvm::sys::fs::OF_TextWithCRLF);
274 if (EC) {
275 llvm::errs() << Argv0 << ": error opening " << FilePath << ":"
276 << EC.message() << "\n";
277 return false;
278 }
279
280 // Get output stream from tool output buffer/manager.
281 llvm::raw_fd_ostream &OS = Out.os();
282
283 // Output file comment.
284 OS << "// " << ModuleMapPath << "\n";
285 OS << "// Generated by: " << CommandLine << "\n\n";
286
287 // Write module hierarchy from internal representation.
288 if (!RootModule->output(OS, 0))
289 return false;
290
291 // Tell ToolOutputFile that we want to keep the file.
292 Out.keep();
293
294 return true;
295}
296
297// Global functions:
298
299// Module map generation entry point.
300bool createModuleMap(llvm::StringRef ModuleMapPath,
301 llvm::ArrayRef<std::string> HeaderFileNames,
302 llvm::ArrayRef<std::string> ProblemFileNames,
303 DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
304 llvm::StringRef RootModuleName) {
305 // Load internal representation of modules.
306 std::unique_ptr<Module> RootModule(
308 RootModuleName, HeaderFileNames, ProblemFileNames, Dependencies,
309 HeaderPrefix));
310 if (!RootModule)
311 return false;
312
313 // Write module map file.
315}
const Expr * E
unsigned Indent
Definition: Bracket.cpp:78
llvm::SmallString< 256U > Name
CompiledFragmentImpl & Out
llvm::raw_ostream & OS
static cl::opt< std::string > HeaderPrefix("prefix", cl::init(""), cl::desc("Prepend header file paths with this prefix." " If not specified," " the files are considered to be relative to the header list file."))
static cl::opt< std::string > RootModule("root-module", cl::init(""), cl::desc("Specify the name of the root module."))
static cl::opt< std::string > ModuleMapPath("module-map-path", cl::init(""), cl::desc("Turn on module map output and specify output path or file name." " If no path is specified and if prefix option is specified," " use prefix for file path."))
const char * Argv0
Definition: Modularize.cpp:333
Common definitions for Modularize.
llvm::SmallVector< std::string, 4 > DependentsVector
Definition: Modularize.h:31
llvm::StringMap< DependentsVector > DependencyMap
Definition: Modularize.h:32
static std::string ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName)
static Module * loadModuleDescriptions(llvm::StringRef RootModuleName, llvm::ArrayRef< std::string > HeaderFileNames, llvm::ArrayRef< std::string > ProblemFileNames, DependencyMap &Dependencies, llvm::StringRef HeaderPrefix)
static std::string ensureVaidModuleName(llvm::StringRef MightBeInvalidName)
bool createModuleMap(llvm::StringRef ModuleMapPath, llvm::ArrayRef< std::string > HeaderFileNames, llvm::ArrayRef< std::string > ProblemFileNames, DependencyMap &Dependencies, llvm::StringRef HeaderPrefix, llvm::StringRef RootModuleName)
Create the module map file.
static bool writeModuleMap(llvm::StringRef ModuleMapPath, llvm::StringRef HeaderPrefix, Module *RootModule)
static bool addModuleDescription(Module *RootModule, llvm::StringRef HeaderFilePath, llvm::StringRef HeaderPrefix, DependencyMap &Dependencies, bool IsProblemFile)
static const char *const ReservedNames[]
std::vector< llvm::StringRef > CommandLine