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