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 | |
40 | namespace { |
41 | |
42 | // Internal class definitions: |
43 | |
44 | // Represents a module. |
45 | class Module { |
46 | public: |
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 | |
54 | public: |
55 | std::string Name; |
56 | std::vector<std::string> ; |
57 | std::vector<Module *> SubModules; |
58 | bool IsProblem; |
59 | }; |
60 | |
61 | } // end anonymous namespace. |
62 | |
63 | // Module functions: |
64 | |
65 | // Constructors. |
66 | Module::Module(llvm::StringRef Name, bool Problem) |
67 | : Name(Name), IsProblem(Problem) {} |
68 | |
69 | // Destructor. |
70 | Module::~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. |
80 | bool 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(NumSpaces: 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(NumSpaces: Indent); |
98 | if (IsProblem || strstr(haystack: (*I).c_str(), needle: ".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(NumSpaces: 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(NumSpaces: Indent); |
114 | OS << "}\n" ; |
115 | } |
116 | |
117 | return true; |
118 | } |
119 | |
120 | // Lookup a sub-module. |
121 | Module *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(). |
134 | static 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. |
142 | static std::string |
143 | ensureNoCollisionWithReservedName(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(pos: 0, s: "_" ); |
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. |
156 | static std::string |
157 | ensureVaidModuleName(llvm::StringRef MightBeInvalidName) { |
158 | std::string SafeName(MightBeInvalidName); |
159 | std::replace(first: SafeName.begin(), last: SafeName.end(), old_value: '-', new_value: '_'); |
160 | std::replace(first: SafeName.begin(), last: SafeName.end(), old_value: '.', new_value: '_'); |
161 | if (isdigit(SafeName[0])) |
162 | SafeName = "_" + SafeName; |
163 | return SafeName; |
164 | } |
165 | |
166 | // Add one module, given a header file path. |
167 | static bool addModuleDescription(Module *RootModule, |
168 | llvm::StringRef , |
169 | llvm::StringRef , |
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(path: HeaderFilePath, result&: NativePath); |
179 | llvm::sys::path::native(path: HeaderPrefix, result&: NativePrefix); |
180 | if (NativePath.starts_with(Prefix: NativePrefix)) |
181 | FilePath = std::string(NativePath.substr(Start: 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(first: FilePath.begin(), last: FilePath.end(), old_value: '\\', new_value: '/'); |
196 | // Insert module into tree, using subdirectories as submodules. |
197 | for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(path: FilePath), |
198 | E = llvm::sys::path::end(path: FilePath); |
199 | I != E; ++I) { |
200 | if ((*I)[0] == '.') |
201 | continue; |
202 | std::string Stem(llvm::sys::path::stem(path: *I)); |
203 | Stem = ensureNoCollisionWithReservedName(MightBeReservedName: Stem); |
204 | Stem = ensureVaidModuleName(MightBeInvalidName: Stem); |
205 | Module *SubModule = CurrentModule->findSubModule(SubName: Stem); |
206 | if (!SubModule) { |
207 | SubModule = new Module(Stem, IsProblemFile); |
208 | CurrentModule->SubModules.push_back(x: SubModule); |
209 | } |
210 | CurrentModule = SubModule; |
211 | } |
212 | // Add header file name to headers. |
213 | CurrentModule->HeaderFileNames.push_back(x: FilePath); |
214 | return true; |
215 | } |
216 | |
217 | // Create the internal module tree representation. |
218 | static Module *loadModuleDescriptions( |
219 | llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> , |
220 | llvm::ArrayRef<std::string> ProblemFileNames, |
221 | DependencyMap &Dependencies, llvm::StringRef ) { |
222 | |
223 | // Create root module. |
224 | auto *RootModule = new Module(RootModuleName, false); |
225 | |
226 | llvm::SmallString<256> CurrentDirectory; |
227 | llvm::sys::fs::current_path(result&: 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 (*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, HeaderFilePath: Header, HeaderPrefix, Dependencies, IsProblemFile)) |
247 | return nullptr; |
248 | } |
249 | |
250 | return RootModule; |
251 | } |
252 | |
253 | // Kick off the writing of the module map. |
254 | static bool writeModuleMap(llvm::StringRef ModuleMapPath, |
255 | llvm::StringRef , Module *RootModule) { |
256 | llvm::SmallString<256> (ModuleMapPath); |
257 | llvm::sys::path::remove_filename(path&: 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(path&: FilePath, a: ModuleMapPath); |
265 | llvm::sys::path::native(path&: FilePath); |
266 | } else { |
267 | FilePath = ModuleMapPath; |
268 | llvm::sys::path::native(path&: 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, Indent: 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. |
300 | bool createModuleMap(llvm::StringRef ModuleMapPath, |
301 | llvm::ArrayRef<std::string> , |
302 | llvm::ArrayRef<std::string> ProblemFileNames, |
303 | DependencyMap &Dependencies, llvm::StringRef , |
304 | llvm::StringRef RootModuleName) { |
305 | // Load internal representation of modules. |
306 | std::unique_ptr<Module> RootModule( |
307 | loadModuleDescriptions( |
308 | RootModuleName, HeaderFileNames, ProblemFileNames, Dependencies, |
309 | HeaderPrefix)); |
310 | if (!RootModule) |
311 | return false; |
312 | |
313 | // Write module map file. |
314 | return writeModuleMap(ModuleMapPath, HeaderPrefix, RootModule: RootModule.get()); |
315 | } |
316 | |