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