blob: 36e899a39e32b42feaeb185c74275867adb656ed [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"
33#include "llvm/ADT/OwningPtr.h"
34#include "llvm/ADT/SmallString.h"
35#include "llvm/Support/FileSystem.h"
36#include "llvm/Support/Path.h"
37#include "llvm/Support/ToolOutputFile.h"
38#include <vector>
39
40// Local definitions:
41
42namespace {
43
44// Internal class definitions:
45
46// Represents a module.
47class Module {
48public:
49 Module(llvm::StringRef Name);
50 Module();
51 ~Module();
52 bool output(llvm::raw_fd_ostream &OS, int Indent);
53 Module *findSubModule(llvm::StringRef SubName);
54
55public:
56 std::string Name;
57 std::vector<std::string> HeaderFileNames;
58 std::vector<Module *> SubModules;
59};
60
61} // end anonymous namespace.
62
63// Module functions:
64
65// Constructors.
66Module::Module(llvm::StringRef Name) : Name(Name) {}
67Module::Module() {}
68
69// Destructor.
70Module::~Module() {
71 // Free submodules.
72 while (SubModules.size()) {
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 (std::vector<Module *>::iterator I = SubModules.begin(),
90 E = SubModules.end();
91 I != E; ++I) {
92 if (!(*I)->output(OS, Indent))
93 return false;
94 }
95
96 // Output header files.
97 for (std::vector<std::string>::iterator I = HeaderFileNames.begin(),
98 E = HeaderFileNames.end();
99 I != E; ++I) {
100 OS.indent(Indent);
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 (std::vector<Module *>::iterator I = SubModules.begin(),
123 E = SubModules.end();
124 I != E; ++I) {
125 if ((*I)->Name == SubName)
126 return *I;
127 }
128 return 0;
129}
130
131// Implementation functions:
132
133// Reserved keywords in module.map syntax.
134// Keep in sync with keywords in module map parser in Lex/ModuleMap.cpp,
135// such as in ModuleMapParser::consumeToken().
136static const char *ReservedNames[] = {
137 "config_macros", "export", "module", "conflict", "framework",
138 "requires", "exclude", "header", "private", "explicit",
139 "link", "umbrella", "extern", "use", 0 // Flag end.
140};
141
142// Convert module name to a non keyword.
143// Prepends a '_' to the name if and only if the name is a keyword.
144static std::string
145ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName) {
146 std::string SafeName = MightBeReservedName;
147 for (int Index = 0; ReservedNames[Index] != 0; ++Index) {
148 if (MightBeReservedName == ReservedNames[Index]) {
149 SafeName.insert(0, "_");
150 break;
151 }
152 }
153 return SafeName;
154}
155
156// Add one module, given a header file path.
157static bool addModuleDescription(Module *RootModule,
158 llvm::StringRef HeaderFilePath,
159 llvm::StringRef HeaderPrefix,
160 DependencyMap &Dependencies) {
161 Module *CurrentModule = RootModule;
162 DependentsVector &FileDependents = Dependencies[HeaderFilePath];
163 std::string FilePath;
164 // Strip prefix.
NAKAMURA Takumibf5391d2013-10-16 01:42:33 +0000165 // HeaderFilePath should be compared to natively-canonicalized Prefix.
166 llvm::SmallString<256> NativePath, NativePrefix;
167 llvm::sys::path::native(HeaderFilePath, NativePath);
168 llvm::sys::path::native(HeaderPrefix, NativePrefix);
169 if (NativePath.startswith(NativePrefix))
170 FilePath = NativePath.substr(NativePrefix.size() + 1);
John Thompson5ab4f112013-10-15 13:52:33 +0000171 else
172 FilePath = HeaderFilePath;
173 int Count = FileDependents.size();
174 // Headers that go into modules must not depend on other files being
175 // included first. If there are any dependents, warn user and omit.
176 if (Count != 0) {
177 llvm::errs() << "warning: " << FilePath
178 << " depends on other headers being included first,"
179 " meaning the module.map won't compile."
180 " This header will be omitted from the module map.\n";
181 return true;
182 }
183 // Make canonical.
184 std::replace(FilePath.begin(), FilePath.end(), '\\', '/');
185 // Insert module into tree, using subdirectories as submodules.
186 for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath),
187 E = llvm::sys::path::end(FilePath);
188 I != E; ++I) {
189 if ((*I)[0] == '.')
190 continue;
191 std::string Stem = llvm::sys::path::stem(*I);
192 Stem = ensureNoCollisionWithReservedName(Stem);
193 Module *SubModule = CurrentModule->findSubModule(Stem);
194 if (SubModule == 0) {
195 SubModule = new Module(Stem);
196 CurrentModule->SubModules.push_back(SubModule);
197 }
198 CurrentModule = SubModule;
199 }
200 // Add header file name to headers.
201 CurrentModule->HeaderFileNames.push_back(FilePath);
202 return true;
203}
204
205// Create the internal module tree representation.
206static Module *loadModuleDescriptions(
207 llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> HeaderFileNames,
208 DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) {
209
210 // Create root module.
211 Module *RootModule = new Module(RootModuleName);
212
213 llvm::SmallString<256> CurrentDirectory;
214 llvm::sys::fs::current_path(CurrentDirectory);
215
216 // If no header prefix, use current directory.
217 if (HeaderPrefix.size() == 0)
218 HeaderPrefix = CurrentDirectory;
219
220 // Walk the header file names and output the module map.
221 for (llvm::ArrayRef<std::string>::iterator I = HeaderFileNames.begin(),
222 E = HeaderFileNames.end();
223 I != E; ++I) {
224 // Add as a module.
225 if (!addModuleDescription(RootModule, *I, HeaderPrefix, Dependencies))
Will Dietz0f3b4aa2013-10-15 15:45:00 +0000226 return NULL;
John Thompson5ab4f112013-10-15 13:52:33 +0000227 }
228
229 return RootModule;
230}
231
232// Kick off the writing of the module map.
233static bool writeModuleMap(llvm::StringRef ModuleMapPath,
234 llvm::StringRef HeaderPrefix, Module *RootModule) {
235 llvm::SmallString<256> HeaderDirectory(ModuleMapPath);
236 llvm::sys::path::remove_filename(HeaderDirectory);
237 llvm::SmallString<256> FilePath;
238
239 // Get the module map file path to be used.
240 if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) {
241 FilePath = HeaderPrefix;
242 // Prepend header file name prefix if it's not absolute.
243 llvm::sys::path::append(FilePath, ModuleMapPath);
244 llvm::sys::path::native(FilePath);
245 } else {
246 FilePath = ModuleMapPath;
247 llvm::sys::path::native(FilePath);
248 }
249
250 // Set up module map output file.
251 std::string Error;
252 llvm::tool_output_file Out(FilePath.c_str(), Error);
253 if (!Error.empty()) {
254 llvm::errs() << Argv0 << ": error opening " << FilePath << ":" << Error
255 << "\n";
256 return false;
257 }
258
259 // Get output stream from tool output buffer/manager.
260 llvm::raw_fd_ostream &OS = Out.os();
261
262 // Output file comment.
263 OS << "// " << ModuleMapPath << "\n";
264 OS << "// Generated by: " << CommandLine << "\n\n";
265
266 // Write module hierarchy from internal representation.
267 if (!RootModule->output(OS, 0))
268 return false;
269
270 // Tell tool_output_file that we want to keep the file.
271 Out.keep();
272
273 return true;
274}
275
276// Global functions:
277
278// Module map generation entry point.
279bool createModuleMap(llvm::StringRef ModuleMapPath,
280 llvm::ArrayRef<std::string> HeaderFileNames,
281 DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
282 llvm::StringRef RootModuleName) {
283 // Load internal representation of modules.
284 llvm::OwningPtr<Module> RootModule(loadModuleDescriptions(
285 RootModuleName, HeaderFileNames, Dependencies, HeaderPrefix));
286 if (!RootModule.get())
287 return false;
288
289 // Write module map file.
290 return writeModuleMap(ModuleMapPath, HeaderPrefix, RootModule.get());
291}