1 //===--- ModuleAssistant.cpp - Module map generation manager --*- C++ -*---===//
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
7 //===----------------------------------------------------------------------===//
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.
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.
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.
29 //===----------------------------------------------------------------------===//
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"
42 // Internal class definitions:
44 // Represents a module.
47 Module(llvm::StringRef Name
, bool Problem
);
49 bool output(llvm::raw_fd_ostream
&OS
, int Indent
);
50 Module
*findSubModule(llvm::StringRef SubName
);
54 std::vector
<std::string
> HeaderFileNames
;
55 std::vector
<Module
*> SubModules
;
59 } // end anonymous namespace.
64 Module::Module(llvm::StringRef Name
, bool Problem
)
65 : Name(Name
), IsProblem(Problem
) {}
70 while (!SubModules
.empty()) {
71 Module
*last
= SubModules
.back();
72 SubModules
.pop_back();
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) {
82 OS
<< "module " << Name
<< " {\n";
87 for (auto I
= SubModules
.begin(), E
= SubModules
.end(); I
!= E
; ++I
) {
88 if (!(*I
)->output(OS
, Indent
))
92 // Output header files.
93 for (auto I
= HeaderFileNames
.begin(), E
= HeaderFileNames
.end(); I
!= E
;
96 if (IsProblem
|| strstr((*I
).c_str(), ".inl"))
97 OS
<< "exclude header \"" << *I
<< "\"\n";
99 OS
<< "header \"" << *I
<< "\"\n";
102 // If this module has header files, output export directive.
103 if (HeaderFileNames
.size() != 0) {
108 // If this is not the nameless root module, close the module definition.
109 if (Name
.size() != 0) {
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
)
127 // Implementation functions:
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.
138 // Convert module name to a non-keyword.
139 // Prepends a '_' to the name if and only if the name is a keyword.
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(0, "_");
152 // Convert module name to a non-keyword.
153 // Prepends a '_' to the name if and only if the name is a keyword.
155 ensureVaidModuleName(llvm::StringRef MightBeInvalidName
) {
156 std::string
SafeName(MightBeInvalidName
);
157 std::replace(SafeName
.begin(), SafeName
.end(), '-', '_');
158 std::replace(SafeName
.begin(), SafeName
.end(), '.', '_');
159 if (isdigit(SafeName
[0]))
160 SafeName
= "_" + SafeName
;
164 // Add one module, given a header file path.
165 static bool addModuleDescription(Module
*RootModule
,
166 llvm::StringRef HeaderFilePath
,
167 llvm::StringRef HeaderPrefix
,
168 DependencyMap
&Dependencies
,
169 bool IsProblemFile
) {
170 Module
*CurrentModule
= RootModule
;
171 DependentsVector
&FileDependents
= Dependencies
[HeaderFilePath
];
172 std::string FilePath
;
174 // HeaderFilePath should be compared to natively-canonicalized Prefix.
175 llvm::SmallString
<256> NativePath
, NativePrefix
;
176 llvm::sys::path::native(HeaderFilePath
, NativePath
);
177 llvm::sys::path::native(HeaderPrefix
, NativePrefix
);
178 if (NativePath
.starts_with(NativePrefix
))
179 FilePath
= std::string(NativePath
.substr(NativePrefix
.size() + 1));
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.
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";
193 std::replace(FilePath
.begin(), FilePath
.end(), '\\', '/');
194 // Insert module into tree, using subdirectories as submodules.
195 for (llvm::sys::path::const_iterator I
= llvm::sys::path::begin(FilePath
),
196 E
= llvm::sys::path::end(FilePath
);
200 std::string
Stem(llvm::sys::path::stem(*I
));
201 Stem
= ensureNoCollisionWithReservedName(Stem
);
202 Stem
= ensureVaidModuleName(Stem
);
203 Module
*SubModule
= CurrentModule
->findSubModule(Stem
);
205 SubModule
= new Module(Stem
, IsProblemFile
);
206 CurrentModule
->SubModules
.push_back(SubModule
);
208 CurrentModule
= SubModule
;
210 // Add header file name to headers.
211 CurrentModule
->HeaderFileNames
.push_back(FilePath
);
215 // Create the internal module tree representation.
216 static Module
*loadModuleDescriptions(
217 llvm::StringRef RootModuleName
, llvm::ArrayRef
<std::string
> HeaderFileNames
,
218 llvm::ArrayRef
<std::string
> ProblemFileNames
,
219 DependencyMap
&Dependencies
, llvm::StringRef HeaderPrefix
) {
221 // Create root module.
222 auto *RootModule
= new Module(RootModuleName
, false);
224 llvm::SmallString
<256> CurrentDirectory
;
225 llvm::sys::fs::current_path(CurrentDirectory
);
227 // If no header prefix, use current directory.
228 if (HeaderPrefix
.size() == 0)
229 HeaderPrefix
= CurrentDirectory
;
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();
235 std::string
Header(*I
);
236 bool IsProblemFile
= false;
237 for (auto &ProblemFile
: ProblemFileNames
) {
238 if (ProblemFile
== Header
) {
239 IsProblemFile
= true;
244 if (!addModuleDescription(RootModule
, Header
, HeaderPrefix
, Dependencies
, IsProblemFile
))
251 // Kick off the writing of the module map.
252 static bool writeModuleMap(llvm::StringRef ModuleMapPath
,
253 llvm::StringRef HeaderPrefix
, Module
*RootModule
) {
254 llvm::SmallString
<256> HeaderDirectory(ModuleMapPath
);
255 llvm::sys::path::remove_filename(HeaderDirectory
);
256 llvm::SmallString
<256> FilePath
;
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(FilePath
, ModuleMapPath
);
263 llvm::sys::path::native(FilePath
);
265 FilePath
= ModuleMapPath
;
266 llvm::sys::path::native(FilePath
);
269 // Set up module map output file.
271 llvm::ToolOutputFile
Out(FilePath
, EC
, llvm::sys::fs::OF_TextWithCRLF
);
273 llvm::errs() << Argv0
<< ": error opening " << FilePath
<< ":"
274 << EC
.message() << "\n";
278 // Get output stream from tool output buffer/manager.
279 llvm::raw_fd_ostream
&OS
= Out
.os();
281 // Output file comment.
282 OS
<< "// " << ModuleMapPath
<< "\n";
283 OS
<< "// Generated by: " << CommandLine
<< "\n\n";
285 // Write module hierarchy from internal representation.
286 if (!RootModule
->output(OS
, 0))
289 // Tell ToolOutputFile that we want to keep the file.
297 // Module map generation entry point.
298 bool createModuleMap(llvm::StringRef ModuleMapPath
,
299 llvm::ArrayRef
<std::string
> HeaderFileNames
,
300 llvm::ArrayRef
<std::string
> ProblemFileNames
,
301 DependencyMap
&Dependencies
, llvm::StringRef HeaderPrefix
,
302 llvm::StringRef RootModuleName
) {
303 // Load internal representation of modules.
304 std::unique_ptr
<Module
> RootModule(
305 loadModuleDescriptions(
306 RootModuleName
, HeaderFileNames
, ProblemFileNames
, Dependencies
,
311 // Write module map file.
312 return writeModuleMap(ModuleMapPath
, HeaderPrefix
, RootModule
.get());