Index: modularize/CMakeLists.txt =================================================================== --- modularize/CMakeLists.txt +++ modularize/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_executable(modularize Modularize.cpp + ModuleAssistant.cpp PreprocessorTracker.cpp ) Index: modularize/Modularize.h =================================================================== --- modularize/Modularize.h +++ modularize/Modularize.h @@ -0,0 +1,34 @@ +//===--- Modularize.h - Common definitions for Modularize -*- C++ -*-----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +/// +/// \file +/// \brief Common definitions for Modularize. +/// +//===--------------------------------------------------------------------===// + +#ifndef MODULARIZE_H +#define MODULARIZE_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include +#include + +// Save the program name for error messages. +extern const char *Argv0; +// Save the command line for comments. +extern std::string CommandLine; + +// Dependency types. +typedef llvm::SmallVector DependentsVector; +typedef llvm::StringMap DependencyMap; + +#endif // MODULARIZE_H Index: modularize/Modularize.cpp =================================================================== --- modularize/Modularize.cpp +++ modularize/Modularize.cpp @@ -90,6 +90,25 @@ // // See PreprocessorTracker.cpp for additional details. // +// Modularize also has an option ("-module-map-path=module.map") that will +// skip the checks, and instead act as a module.map generation assistant, +// generating a module map file based on the header list. An optional +// "-root-module=(rootName)" argument can specify a root module to be +// created in the generated module.map file. Note that you will likely +// need to edit this file to suit the needs of your headers. +// +// An example command line for generating a module.map file: +// +// modularize -module-map-path=module.map -root-module=myroot headerlist.txt +// +// Note that if the headers in the header list have partial paths, sub-modules +// will be created for the subdirectires involved, assuming that the +// subdirectories contain headers to be grouped into a module, but still with +// individual modules for the headers in the subdirectory. +// +// See the ModuleAssistant.cpp file comments for additional details about the +// implementation of the assistant mode. +// // Future directions: // // Basically, we want to add new checks for whatever we can check with respect @@ -134,8 +153,6 @@ #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringMap.h" #include "llvm/Config/config.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -150,6 +167,8 @@ #include #include #include +#include "Modularize.h" +#include "ModuleAssistant.h" #include "PreprocessorTracker.h" using namespace clang; @@ -178,8 +197,24 @@ " If not specified," " the files are considered to be relative to the header list file.")); -typedef SmallVector DependentsVector; -typedef StringMap DependencyMap; +// Option for assistant mode, telling modularize to output a module map +// based on the headers list, and where to put it. +cl::opt ModuleMapPath( + "module-map-path", cl::init(""), + cl::desc("Turn on module map output and specify output path or file name." + " If no path is specified and if prefix option is specified," + " use prefix for file path.")); + +// Option for assistant mode, telling modularize to output a module map +// based on the headers list, and where to put it. +cl::opt +RootModule("root-module", cl::init(""), + cl::desc("Specify the name of the root module.")); + +// Save the program name for error messages. +const char *Argv0; +// Save the command line for comments. +std::string CommandLine; // Read the header list file and collect the header file names and // optional dependencies. @@ -651,6 +686,16 @@ int main(int Argc, const char **Argv) { + // Save program name for error messages. + Argv0 = Argv[0]; + + // Save program arguments for use in module.map comment. + CommandLine = sys::path::stem(sys::path::filename(Argv0)); + for (int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) { + CommandLine.append(" "); + CommandLine.append(Argv[ArgIndex]); + } + // This causes options to be parsed. cl::ParseCommandLineOptions(Argc, Argv, "modularize.\n"); @@ -670,6 +715,14 @@ return 1; } + // If we are in assistant mode, output the module map and quit. + if (ModuleMapPath[0]) { + if (!createModuleMap(ModuleMapPath, Headers, Dependencies, HeaderPrefix, + RootModule)) + return 1; // Failed. + return 0; // Success - Skip checks in assistant mode. + } + // Create the compilation database. SmallString<256> PathBuf; sys::fs::current_path(PathBuf); Index: modularize/ModuleAssistant.h =================================================================== --- modularize/ModuleAssistant.h +++ modularize/ModuleAssistant.h @@ -0,0 +1,46 @@ +//===--- ModuleAssistant.h - Module map generation manager -*- C++ -*----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +/// +/// \file +/// \brief This file defines the entry points for the module map generation. +/// +/// This feature takes the header list and generates a simple module map +/// file, intended as a starting point. Options include naming the file +/// path (your real module map should be called "module.map" and be found +/// in the top-level directory of the targeted includes), and the inclusion +/// and naming of an optional root namespace. +//===--------------------------------------------------------------------===// + +#ifndef MODULARIZE_MODULE_ASSISTANT_H +#define MODULARIZE_MODULE_ASSISTANT_H + +#include "Modularize.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace Modularize { + +/// Create the module map file. +/// \param ModuleMapPath The path to the module map file to be generated. +/// \param HeaderFileNames The list of header files, absolute native paths. +/// \param Dependencies Map of headers that depend on other headers. +/// \param HeaderPrefix Tells the code where the headers are, if they +/// aren's in the current directory, allowing the generator to strip +/// the leading, non-relative beginning of the header paths. +/// \brief RootModuleName If not empty, specifies that a root module +/// should be created with this name. +/// \returns True if successful. +bool createModuleMap(llvm::StringRef ModuleMapPath, + llvm::ArrayRef HeaderFileNames, + DependencyMap &Dependencies, llvm::StringRef HeaderPrefix, + llvm::StringRef RootModuleName); +} + +#endif // MODULARIZE_MODULE_ASSISTANT_H Index: modularize/ModuleAssistant.cpp =================================================================== --- modularize/ModuleAssistant.cpp +++ modularize/ModuleAssistant.cpp @@ -0,0 +1,356 @@ +//===--- ModuleAssistant.cpp - Module map generation manager -*- C++ -*---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This file defines the module generation entry point function, +// createModuleMap, and the two classes that implement the generation. +// +// The "Module" class represents a module, with members for storing the module +// name, associated header file names, and sub-modules, and an "output" +// function that recursively writes the module definitions. +// +// The "ModuleAssistantImpl" class implements the top-level mechanism of the +// assistant mode. It stores a "Module" object representing the root +// module (whether or not a name root module is specified). It provides a +// createModuleMap function that does the following via sub-functions: +// +// 1. Sets up for writing the module map output file using the +// "tool_output_file" class from LLVM. +// 2. Builds an internal tree structure of "Module" objects to represent +// the modules, based on the header list input. +// 3. Writes the module map file by walking the internal module tree by +// calling the "output" member of the root module. +// 4. Finalizes the output, which consists of flushing the file via +// the tool_output_file object, and destructing the tool_output_file +// object. +// +//===---------------------------------------------------------------------===// + +#include "ModuleAssistant.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" +#include + +using namespace Modularize; + +// Local definitions: + +namespace { + +// Internal class definitions: + +// Represents a module. +class Module { +public: + Module(llvm::StringRef Name); + Module(); + bool output(llvm::raw_fd_ostream &OS, int Indent); + Module *findSubModule(llvm::StringRef SubName); + +public: + std::string Name; + std::vector HeaderFileNames; + std::vector SubModules; +}; + +// Provides the top-level logic for generating the module map. +class ModuleAssistantImpl { +public: + ModuleAssistantImpl(); + bool createModuleMap(llvm::StringRef ModuleMapPath, + llvm::ArrayRef HeaderFileNames, + DependencyMap &Dependencies, + llvm::StringRef HeaderPrefix, + llvm::StringRef RootModuleName); + +private: + bool loadModuleDescriptions(llvm::StringRef RootModuleName, + llvm::ArrayRef HeaderFileNames, + DependencyMap &Dependencies, + llvm::StringRef HeaderPrefix); + bool addModuleDescription(llvm::StringRef HeaderFilePath, + llvm::StringRef HeaderPrefix, + DependencyMap &Dependencies); + std::string makeNonReservedName(llvm::StringRef MightBeReservedName); + bool setUpModuleMapOutput(llvm::StringRef ModuleMapPath, + llvm::StringRef HeaderPrefix); + bool writeModuleMap(llvm::StringRef ModuleMapPath); + bool finalizeModuleMapOutput(); + +private: + Module *RootModule; + llvm::OwningPtr Out; + static const char *ReservedNames[]; +}; + +// Module functions: + +Module::Module(llvm::StringRef Name) : Name(Name) {} + +Module::Module() {} + +// Write a module hierarchy to the given output stream. +bool Module::output(llvm::raw_fd_ostream &OS, int Indent) { + // If this is not the nameless root module, start a module definition. + if (Name.size() != 0) { + OS.indent(Indent); + OS << "module " << Name << " {\n"; + Indent += 2; + } + + // Output submodules. + for (std::vector::iterator I = SubModules.begin(), + E = SubModules.end(); + I != E; ++I) { + if (!(*I)->output(OS, Indent)) + return false; + } + + // Output header files. + for (std::vector::iterator I = HeaderFileNames.begin(), + E = HeaderFileNames.end(); + I != E; ++I) { + OS.indent(Indent); + OS << "header \"" << *I << "\"\n"; + } + + // If this module has header files, output export directive. + if (HeaderFileNames.size() != 0) { + OS.indent(Indent); + OS << "export *\n"; + } + + // If this is not the nameless root module, close the module definition. + if (Name.size() != 0) { + Indent -= 2; + OS.indent(Indent); + OS << "}\n"; + } + + return true; +} + +// Lookup a sub-module. +Module *Module::findSubModule(llvm::StringRef SubName) { + for (std::vector::iterator I = SubModules.begin(), + E = SubModules.end(); + I != E; ++I) { + if ((*I)->Name == SubName) + return *I; + } + return 0; +} + +// ModuleAssistantImpl functions: + +ModuleAssistantImpl::ModuleAssistantImpl() {} + +// The "createModuleMap" function provides the top-level logic for +// generating the module map via sub-functions that do the following: +// +// 1. Sets up for writing the module map output file using the +// "tool_output_file" class from LLVM. +// 2. Builds an internal tree structure of "Module" objects to represent +// the modules, based on the header list input. +// 3. Writes the module map file by walking the internal module tree by +// calling the "output" member of the root module. +// 4. Finalizes the output, which consists of flushing the file via +// the tool_output_file object, and destructing the tool_output_file +// object. +bool ModuleAssistantImpl::createModuleMap( + llvm::StringRef ModuleMapPath, llvm::ArrayRef HeaderFileNames, + DependencyMap &Dependencies, llvm::StringRef HeaderPrefix, + llvm::StringRef RootModuleName) { + + // Set up module map output file. + if (!setUpModuleMapOutput(ModuleMapPath, HeaderPrefix)) + return false; + + // Load internal representation of modules. + if (!loadModuleDescriptions(RootModuleName, HeaderFileNames, Dependencies, + HeaderPrefix)) + return false; + + // Write module map file. + if (!writeModuleMap(ModuleMapPath)) + return false; + + return finalizeModuleMapOutput(); +} + +// Create the internal module tree representation. +bool ModuleAssistantImpl::loadModuleDescriptions( + llvm::StringRef RootModuleName, llvm::ArrayRef HeaderFileNames, + DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) { + + // Create root module. + RootModule = new Module(RootModuleName); + + llvm::SmallString<256> CurrentDirectory; + llvm::sys::fs::current_path(CurrentDirectory); + + // If no header prefix, use current directory. + if (HeaderPrefix.size() == 0) + HeaderPrefix = CurrentDirectory; + + // Walk the header file names and output the module map. + for (llvm::ArrayRef::iterator I = HeaderFileNames.begin(), + E = HeaderFileNames.end(); + I != E; ++I) { + // Add as a module. + if (!addModuleDescription(*I, HeaderPrefix, Dependencies)) + return false; + } + + return true; +} + +// Add one module, given a header file path. +bool ModuleAssistantImpl::addModuleDescription(llvm::StringRef HeaderFilePath, + llvm::StringRef HeaderPrefix, + DependencyMap &Dependencies) { + Module *CurrentModule = RootModule; + DependentsVector &FileDependents = Dependencies[HeaderFilePath]; + std::string FilePath; + // Strip prefix. + if (HeaderFilePath.startswith(HeaderPrefix)) + FilePath = HeaderFilePath.substr(HeaderPrefix.size() + 1); + else + FilePath = HeaderFilePath; + int Count = FileDependents.size(); + // Headers that go into modules must not depend on other files being + // included first. If there are any dependents, warn user and omit. + if (Count != 0) { + llvm::errs() << "warning: " << FilePath + << " depends on other headers being included first," + " meaning the module.map won't compile." + " This header will be omitted from the module map.\n"; + return true; + } + // Make canonical. + std::replace(FilePath.begin(), FilePath.end(), '\\', '/'); + // Insert module into tree, using subdirectories as submodules. + for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath), + E = llvm::sys::path::end(FilePath); + I != E; ++I) { + if ((*I)[0] == '.') + continue; + std::string Stem = llvm::sys::path::stem(*I); + Stem = makeNonReservedName(Stem); + Module *SubModule = CurrentModule->findSubModule(Stem); + if (SubModule == 0) { + SubModule = new Module(Stem); + CurrentModule->SubModules.push_back(SubModule); + } + CurrentModule = SubModule; + } + // Add header file name to headers. + CurrentModule->HeaderFileNames.push_back(FilePath); + return true; +} + +// Reserved keywords in module.map syntax. +const char *ModuleAssistantImpl::ReservedNames[] = { + "config_macros", "export", "module", "conflict", "framework", + "requires", "exclude", "header", "private", "explicit", + "link", "umbrella", "extern", "use", 0 // Flag end. +}; + +std::string +ModuleAssistantImpl::makeNonReservedName(llvm::StringRef MightBeReservedName) { + std::string SafeName = MightBeReservedName; + for (int Index = 0; ReservedNames[Index] != 0; ++Index) { + if (MightBeReservedName == ReservedNames[Index]) { + SafeName.insert(0, "_"); + break; + } + } + return SafeName; +} + +// Set up the module map output file, based on a tool_output_file object. +bool ModuleAssistantImpl::setUpModuleMapOutput(llvm::StringRef ModuleMapPath, + llvm::StringRef HeaderPrefix) { + // By default, use the path component of the map path. + llvm::SmallString<256> HeaderDirectory(ModuleMapPath); + llvm::sys::path::remove_filename(HeaderDirectory); + llvm::SmallString<256> FilePath; + + // Get the module map file path to be used. + if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) { + FilePath = HeaderPrefix; + // Prepend header file name prefix if it's not absolute. + llvm::sys::path::append(FilePath, ModuleMapPath); + llvm::sys::path::native(FilePath); + } else { + FilePath = ModuleMapPath; + llvm::sys::path::native(FilePath); + } + + // Set up module map output file. + std::string Error; + Out.reset(new llvm::tool_output_file(FilePath.c_str(), Error)); + if (!Error.empty()) { + llvm::errs() << Argv0 << ": error opening " << FilePath << ":" << Error + << "\n"; + return false; + } + + return true; +} + +// Kick off the writing of the module map. +bool ModuleAssistantImpl::writeModuleMap(llvm::StringRef ModuleMapPath) { + + // Get output stream from tool output buffer/manager. + llvm::raw_fd_ostream &OS = Out->os(); + + // Output file comment. + OS << "// " << ModuleMapPath << "\n"; + OS << "// Generated by: " << CommandLine << "\n\n"; + + // Write module hierarchy from internal representation. + if (!RootModule->output(OS, 0)) + return false; + + return true; +} + +// Finalize the module map output. +bool ModuleAssistantImpl::finalizeModuleMapOutput() { + // Tell tool_output_file that we want to keep the file. + Out->keep(); + + // Delete the tool_output_file object, which causes the + // file to be written and then closes the stream. + Out.reset(); + + return true; +} + +} // end anonymous namespace. + +// Global functions: + +namespace Modularize { + +// Module map generation entry point. +bool createModuleMap(llvm::StringRef ModuleMapPath, + llvm::ArrayRef HeaderFileNames, + DependencyMap &Dependencies, llvm::StringRef HeaderPrefix, + llvm::StringRef RootModuleName) { + ModuleAssistantImpl ModuleAssistant; + return ModuleAssistant.createModuleMap(ModuleMapPath, HeaderFileNames, + Dependencies, HeaderPrefix, + RootModuleName); +} + +} // end namespace Modularize Index: test/modularize/Inputs/SubModule1/Header1.h =================================================================== --- test/modularize/Inputs/SubModule1/Header1.h +++ test/modularize/Inputs/SubModule1/Header1.h @@ -0,0 +1 @@ +// Header1.h - Empty. Index: test/modularize/Inputs/SubModule1/Header2.h =================================================================== --- test/modularize/Inputs/SubModule1/Header2.h +++ test/modularize/Inputs/SubModule1/Header2.h @@ -0,0 +1 @@ +// Header2.h - Empty. Index: test/modularize/Inputs/SubModule2/Header3.h =================================================================== --- test/modularize/Inputs/SubModule2/Header3.h +++ test/modularize/Inputs/SubModule2/Header3.h @@ -0,0 +1 @@ +// Header3.h - Empty. Index: test/modularize/Inputs/SubModule2/Header4.h =================================================================== --- test/modularize/Inputs/SubModule2/Header4.h +++ test/modularize/Inputs/SubModule2/Header4.h @@ -0,0 +1 @@ +// Header4.h - Empty. Index: test/modularize/NoProblemsAssistant.modularize =================================================================== --- test/modularize/NoProblemsAssistant.modularize +++ test/modularize/NoProblemsAssistant.modularize @@ -0,0 +1,45 @@ +# RUN: modularize -module-map-path=Output/NoProblemsAssistant.txt -root-module=Root -prefix=%S/Input %s +# RUN: FileCheck --input-file=%T/NoProblemsAssistant.txt %s + +SomeTypes.h +SomeDecls.h +SubModule1/Header1.h +SubModule1/Header2.h +SubModule2/Header3.h +SubModule2/Header4.h +SubModule2.h + +# CHECK: // Output/NoProblemsAssistant.txt +# CHECK-NEXT: // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt -root-module=Root -prefix={{.*}}{{[/\\]}}{{.*}} {{.*}}{{[/\\]}}NoProblemsAssistant.modularize +# CHECK: module Root { +# CHECK-NEXT: module SomeTypes { +# CHECK-NEXT: header "SomeTypes.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: module SomeDecls { +# CHECK-NEXT: header "SomeDecls.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: module SubModule1 { +# CHECK-NEXT: module Header1 { +# CHECK-NEXT: header "SubModule1/Header1.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: module Header2 { +# CHECK-NEXT: header "SubModule1/Header2.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: module SubModule2 { +# CHECK-NEXT: module Header3 { +# CHECK-NEXT: header "SubModule2/Header3.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: module Header4 { +# CHECK-NEXT: header "SubModule2/Header4.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: header "SubModule2.h" +# CHECK-NEXT: export * +# CHECK-NEXT: } +# CHECK-NEXT: } Index: test/modularize/SubModule2.h =================================================================== --- test/modularize/SubModule2.h +++ test/modularize/SubModule2.h @@ -0,0 +1,3 @@ +// SubModule2.h - Master header with same name as directory. +#include "SubModule2/Header3.h" +#include "SubModule2/Header4.h"