1d845baecSJohn Thompson //===--- extra/modularize/ModularizeUtilities.cpp -------------------===//
2d845baecSJohn Thompson //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d845baecSJohn Thompson //
7d845baecSJohn Thompson //===----------------------------------------------------------------------===//
8d845baecSJohn Thompson //
9d845baecSJohn Thompson // This file implements a class for loading and validating a module map or
10d845baecSJohn Thompson // header list by checking that all headers in the corresponding directories
11d845baecSJohn Thompson // are accounted for.
12d845baecSJohn Thompson //
13d845baecSJohn Thompson //===----------------------------------------------------------------------===//
14d845baecSJohn Thompson 
159cb79646SJohn Thompson #include "clang/Basic/SourceManager.h"
169cb79646SJohn Thompson #include "clang/Driver/Options.h"
179cb79646SJohn Thompson #include "clang/Frontend/CompilerInstance.h"
189cb79646SJohn Thompson #include "clang/Frontend/FrontendActions.h"
198eb8d936SJohn Thompson #include "CoverageChecker.h"
20d845baecSJohn Thompson #include "llvm/ADT/SmallString.h"
21d845baecSJohn Thompson #include "llvm/Support/FileUtilities.h"
22d845baecSJohn Thompson #include "llvm/Support/MemoryBuffer.h"
23d845baecSJohn Thompson #include "llvm/Support/Path.h"
24d845baecSJohn Thompson #include "llvm/Support/raw_ostream.h"
258eb8d936SJohn Thompson #include "ModularizeUtilities.h"
26d845baecSJohn Thompson 
279cb79646SJohn Thompson using namespace clang;
28d845baecSJohn Thompson using namespace llvm;
29d845baecSJohn Thompson using namespace Modularize;
30d845baecSJohn Thompson 
31e7103712SBenjamin Kramer namespace {
329cb79646SJohn Thompson // Subclass TargetOptions so we can construct it inline with
339cb79646SJohn Thompson // the minimal option, the triple.
349cb79646SJohn Thompson class ModuleMapTargetOptions : public clang::TargetOptions {
359cb79646SJohn Thompson public:
ModuleMapTargetOptions()369cb79646SJohn Thompson   ModuleMapTargetOptions() { Triple = llvm::sys::getDefaultTargetTriple(); }
379cb79646SJohn Thompson };
38e7103712SBenjamin Kramer } // namespace
399cb79646SJohn Thompson 
40d845baecSJohn Thompson // ModularizeUtilities class implementation.
41d845baecSJohn Thompson 
42d845baecSJohn Thompson // Constructor.
ModularizeUtilities(std::vector<std::string> & InputPaths,llvm::StringRef Prefix,llvm::StringRef ProblemFilesListPath)43d845baecSJohn Thompson ModularizeUtilities::ModularizeUtilities(std::vector<std::string> &InputPaths,
444018c624SJohn Thompson                                          llvm::StringRef Prefix,
454018c624SJohn Thompson                                          llvm::StringRef ProblemFilesListPath)
46201f0f55SDavid Blaikie     : InputFilePaths(InputPaths), HeaderPrefix(Prefix),
47201f0f55SDavid Blaikie       ProblemFilesPath(ProblemFilesListPath), HasModuleMap(false),
4896f5551bSJohn Thompson       MissingHeaderCount(0),
499cb79646SJohn Thompson       // Init clang stuff needed for loading the module map and preprocessing.
509cb79646SJohn Thompson       LangOpts(new LangOptions()), DiagIDs(new DiagnosticIDs()),
519cb79646SJohn Thompson       DiagnosticOpts(new DiagnosticOptions()),
529cb79646SJohn Thompson       DC(llvm::errs(), DiagnosticOpts.get()),
539cb79646SJohn Thompson       Diagnostics(
549cb79646SJohn Thompson           new DiagnosticsEngine(DiagIDs, DiagnosticOpts.get(), &DC, false)),
559cb79646SJohn Thompson       TargetOpts(new ModuleMapTargetOptions()),
569cb79646SJohn Thompson       Target(TargetInfo::CreateTargetInfo(*Diagnostics, TargetOpts)),
579cb79646SJohn Thompson       FileMgr(new FileManager(FileSystemOpts)),
589cb79646SJohn Thompson       SourceMgr(new SourceManager(*Diagnostics, *FileMgr, false)),
59201f0f55SDavid Blaikie       HeaderInfo(new HeaderSearch(std::make_shared<HeaderSearchOptions>(),
60201f0f55SDavid Blaikie                                   *SourceMgr, *Diagnostics, *LangOpts,
61201f0f55SDavid Blaikie                                   Target.get())) {}
62d845baecSJohn Thompson 
63d845baecSJohn Thompson // Create instance of ModularizeUtilities, to simplify setting up
64d845baecSJohn Thompson // subordinate objects.
createModularizeUtilities(std::vector<std::string> & InputPaths,llvm::StringRef Prefix,llvm::StringRef ProblemFilesListPath)65d845baecSJohn Thompson ModularizeUtilities *ModularizeUtilities::createModularizeUtilities(
664018c624SJohn Thompson     std::vector<std::string> &InputPaths, llvm::StringRef Prefix,
674018c624SJohn Thompson     llvm::StringRef ProblemFilesListPath) {
68d845baecSJohn Thompson 
694018c624SJohn Thompson   return new ModularizeUtilities(InputPaths, Prefix, ProblemFilesListPath);
70d845baecSJohn Thompson }
71d845baecSJohn Thompson 
72d845baecSJohn Thompson // Load all header lists and dependencies.
loadAllHeaderListsAndDependencies()73d845baecSJohn Thompson std::error_code ModularizeUtilities::loadAllHeaderListsAndDependencies() {
749cb79646SJohn Thompson   // For each input file.
7508124b11SPiotr Padlewski   for (auto I = InputFilePaths.begin(), E = InputFilePaths.end(); I != E; ++I) {
769cb79646SJohn Thompson     llvm::StringRef InputPath = *I;
779cb79646SJohn Thompson     // If it's a module map.
789cb79646SJohn Thompson     if (InputPath.endswith(".modulemap")) {
799cb79646SJohn Thompson       // Load the module map.
809cb79646SJohn Thompson       if (std::error_code EC = loadModuleMap(InputPath))
819cb79646SJohn Thompson         return EC;
829cb79646SJohn Thompson     }
839cb79646SJohn Thompson     else {
849cb79646SJohn Thompson       // Else we assume it's a header list and load it.
859cb79646SJohn Thompson       if (std::error_code EC = loadSingleHeaderListsAndDependencies(InputPath)) {
869cb79646SJohn Thompson         errs() << "modularize: error: Unable to get header list '" << InputPath
87d845baecSJohn Thompson           << "': " << EC.message() << '\n';
88d845baecSJohn Thompson         return EC;
89d845baecSJohn Thompson       }
90d845baecSJohn Thompson     }
919cb79646SJohn Thompson   }
924018c624SJohn Thompson   // If we have a problem files list.
934018c624SJohn Thompson   if (ProblemFilesPath.size() != 0) {
944018c624SJohn Thompson     // Load problem files list.
954018c624SJohn Thompson     if (std::error_code EC = loadProblemHeaderList(ProblemFilesPath)) {
964018c624SJohn Thompson       errs() << "modularize: error: Unable to get problem header list '" << ProblemFilesPath
974018c624SJohn Thompson         << "': " << EC.message() << '\n';
984018c624SJohn Thompson       return EC;
994018c624SJohn Thompson     }
1004018c624SJohn Thompson   }
101d845baecSJohn Thompson   return std::error_code();
102d845baecSJohn Thompson }
103d845baecSJohn Thompson 
1048eb8d936SJohn Thompson // Do coverage checks.
1058eb8d936SJohn Thompson // For each loaded module map, do header coverage check.
1068eb8d936SJohn Thompson // Starting from the directory of the module.map file,
1078eb8d936SJohn Thompson // Find all header files, optionally looking only at files
1088eb8d936SJohn Thompson // covered by the include path options, and compare against
1098eb8d936SJohn Thompson // the headers referenced by the module.map file.
1108eb8d936SJohn Thompson // Display warnings for unaccounted-for header files.
1118eb8d936SJohn Thompson // Returns 0 if there were no errors or warnings, 1 if there
1128eb8d936SJohn Thompson // were warnings, 2 if any other problem, such as a bad
1138eb8d936SJohn Thompson // module map path argument was specified.
doCoverageCheck(std::vector<std::string> & IncludePaths,llvm::ArrayRef<std::string> CommandLine)1148eb8d936SJohn Thompson std::error_code ModularizeUtilities::doCoverageCheck(
1158eb8d936SJohn Thompson     std::vector<std::string> &IncludePaths,
1168eb8d936SJohn Thompson     llvm::ArrayRef<std::string> CommandLine) {
1178eb8d936SJohn Thompson   int ModuleMapCount = ModuleMaps.size();
1188eb8d936SJohn Thompson   int ModuleMapIndex;
1198eb8d936SJohn Thompson   std::error_code EC;
1208eb8d936SJohn Thompson   for (ModuleMapIndex = 0; ModuleMapIndex < ModuleMapCount; ++ModuleMapIndex) {
1218eb8d936SJohn Thompson     std::unique_ptr<clang::ModuleMap> &ModMap = ModuleMaps[ModuleMapIndex];
122a67cf000SDavid Blaikie     auto Checker = CoverageChecker::createCoverageChecker(
123a67cf000SDavid Blaikie         InputFilePaths[ModuleMapIndex], IncludePaths, CommandLine,
124a67cf000SDavid Blaikie         ModMap.get());
1258eb8d936SJohn Thompson     std::error_code LocalEC = Checker->doChecks();
1268eb8d936SJohn Thompson     if (LocalEC.value() > 0)
1278eb8d936SJohn Thompson       EC = LocalEC;
1288eb8d936SJohn Thompson   }
1298eb8d936SJohn Thompson   return EC;
1308eb8d936SJohn Thompson }
1318eb8d936SJohn Thompson 
132d845baecSJohn Thompson // Load single header list and dependencies.
loadSingleHeaderListsAndDependencies(llvm::StringRef InputPath)133d845baecSJohn Thompson std::error_code ModularizeUtilities::loadSingleHeaderListsAndDependencies(
134d845baecSJohn Thompson     llvm::StringRef InputPath) {
135d845baecSJohn Thompson 
136d845baecSJohn Thompson   // By default, use the path component of the list file name.
137d845baecSJohn Thompson   SmallString<256> HeaderDirectory(InputPath);
138d845baecSJohn Thompson   llvm::sys::path::remove_filename(HeaderDirectory);
139d845baecSJohn Thompson   SmallString<256> CurrentDirectory;
140d845baecSJohn Thompson   llvm::sys::fs::current_path(CurrentDirectory);
141d845baecSJohn Thompson 
142d845baecSJohn Thompson   // Get the prefix if we have one.
143d845baecSJohn Thompson   if (HeaderPrefix.size() != 0)
144d845baecSJohn Thompson     HeaderDirectory = HeaderPrefix;
145d845baecSJohn Thompson 
146d845baecSJohn Thompson   // Read the header list file into a buffer.
147d845baecSJohn Thompson   ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
148d845baecSJohn Thompson     MemoryBuffer::getFile(InputPath);
149d845baecSJohn Thompson   if (std::error_code EC = listBuffer.getError())
150d845baecSJohn Thompson     return EC;
151d845baecSJohn Thompson 
152d845baecSJohn Thompson   // Parse the header list into strings.
153d845baecSJohn Thompson   SmallVector<StringRef, 32> Strings;
154d845baecSJohn Thompson   listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
155d845baecSJohn Thompson 
156d845baecSJohn Thompson   // Collect the header file names from the string list.
157d845baecSJohn Thompson   for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
158d845baecSJohn Thompson     E = Strings.end();
159d845baecSJohn Thompson     I != E; ++I) {
160d845baecSJohn Thompson     StringRef Line = I->trim();
161d845baecSJohn Thompson     // Ignore comments and empty lines.
162d845baecSJohn Thompson     if (Line.empty() || (Line[0] == '#'))
163d845baecSJohn Thompson       continue;
164d845baecSJohn Thompson     std::pair<StringRef, StringRef> TargetAndDependents = Line.split(':');
165d845baecSJohn Thompson     SmallString<256> HeaderFileName;
166d845baecSJohn Thompson     // Prepend header file name prefix if it's not absolute.
167d845baecSJohn Thompson     if (llvm::sys::path::is_absolute(TargetAndDependents.first))
168d845baecSJohn Thompson       llvm::sys::path::native(TargetAndDependents.first, HeaderFileName);
169d845baecSJohn Thompson     else {
170d845baecSJohn Thompson       if (HeaderDirectory.size() != 0)
171d845baecSJohn Thompson         HeaderFileName = HeaderDirectory;
172d845baecSJohn Thompson       else
173d845baecSJohn Thompson         HeaderFileName = CurrentDirectory;
174d845baecSJohn Thompson       llvm::sys::path::append(HeaderFileName, TargetAndDependents.first);
175d845baecSJohn Thompson       llvm::sys::path::native(HeaderFileName);
176d845baecSJohn Thompson     }
177d845baecSJohn Thompson     // Handle optional dependencies.
178d845baecSJohn Thompson     DependentsVector Dependents;
179d845baecSJohn Thompson     SmallVector<StringRef, 4> DependentsList;
180d845baecSJohn Thompson     TargetAndDependents.second.split(DependentsList, " ", -1, false);
181d845baecSJohn Thompson     int Count = DependentsList.size();
182d845baecSJohn Thompson     for (int Index = 0; Index < Count; ++Index) {
183d845baecSJohn Thompson       SmallString<256> Dependent;
184d845baecSJohn Thompson       if (llvm::sys::path::is_absolute(DependentsList[Index]))
185d845baecSJohn Thompson         Dependent = DependentsList[Index];
186d845baecSJohn Thompson       else {
187d845baecSJohn Thompson         if (HeaderDirectory.size() != 0)
188d845baecSJohn Thompson           Dependent = HeaderDirectory;
189d845baecSJohn Thompson         else
190d845baecSJohn Thompson           Dependent = CurrentDirectory;
191d845baecSJohn Thompson         llvm::sys::path::append(Dependent, DependentsList[Index]);
192d845baecSJohn Thompson       }
193d845baecSJohn Thompson       llvm::sys::path::native(Dependent);
1943dcb3934SJohn Thompson       Dependents.push_back(getCanonicalPath(Dependent.str()));
195d845baecSJohn Thompson     }
1963dcb3934SJohn Thompson     // Get canonical form.
1973dcb3934SJohn Thompson     HeaderFileName = getCanonicalPath(HeaderFileName);
198d845baecSJohn Thompson     // Save the resulting header file path and dependencies.
199adcd0268SBenjamin Kramer     HeaderFileNames.push_back(std::string(HeaderFileName.str()));
200d845baecSJohn Thompson     Dependencies[HeaderFileName.str()] = Dependents;
201d845baecSJohn Thompson   }
202d845baecSJohn Thompson   return std::error_code();
203d845baecSJohn Thompson }
2043dcb3934SJohn Thompson 
2054018c624SJohn Thompson // Load problem header list.
loadProblemHeaderList(llvm::StringRef InputPath)2064018c624SJohn Thompson std::error_code ModularizeUtilities::loadProblemHeaderList(
2074018c624SJohn Thompson   llvm::StringRef InputPath) {
2084018c624SJohn Thompson 
2094018c624SJohn Thompson   // By default, use the path component of the list file name.
2104018c624SJohn Thompson   SmallString<256> HeaderDirectory(InputPath);
2114018c624SJohn Thompson   llvm::sys::path::remove_filename(HeaderDirectory);
2124018c624SJohn Thompson   SmallString<256> CurrentDirectory;
2134018c624SJohn Thompson   llvm::sys::fs::current_path(CurrentDirectory);
2144018c624SJohn Thompson 
2154018c624SJohn Thompson   // Get the prefix if we have one.
2164018c624SJohn Thompson   if (HeaderPrefix.size() != 0)
2174018c624SJohn Thompson     HeaderDirectory = HeaderPrefix;
2184018c624SJohn Thompson 
2194018c624SJohn Thompson   // Read the header list file into a buffer.
2204018c624SJohn Thompson   ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
2214018c624SJohn Thompson     MemoryBuffer::getFile(InputPath);
2224018c624SJohn Thompson   if (std::error_code EC = listBuffer.getError())
2234018c624SJohn Thompson     return EC;
2244018c624SJohn Thompson 
2254018c624SJohn Thompson   // Parse the header list into strings.
2264018c624SJohn Thompson   SmallVector<StringRef, 32> Strings;
2274018c624SJohn Thompson   listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
2284018c624SJohn Thompson 
2294018c624SJohn Thompson   // Collect the header file names from the string list.
2304018c624SJohn Thompson   for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
2314018c624SJohn Thompson     E = Strings.end();
2324018c624SJohn Thompson     I != E; ++I) {
2334018c624SJohn Thompson     StringRef Line = I->trim();
2344018c624SJohn Thompson     // Ignore comments and empty lines.
2354018c624SJohn Thompson     if (Line.empty() || (Line[0] == '#'))
2364018c624SJohn Thompson       continue;
2374018c624SJohn Thompson     SmallString<256> HeaderFileName;
2384018c624SJohn Thompson     // Prepend header file name prefix if it's not absolute.
2394018c624SJohn Thompson     if (llvm::sys::path::is_absolute(Line))
2404018c624SJohn Thompson       llvm::sys::path::native(Line, HeaderFileName);
2414018c624SJohn Thompson     else {
2424018c624SJohn Thompson       if (HeaderDirectory.size() != 0)
2434018c624SJohn Thompson         HeaderFileName = HeaderDirectory;
2444018c624SJohn Thompson       else
2454018c624SJohn Thompson         HeaderFileName = CurrentDirectory;
2464018c624SJohn Thompson       llvm::sys::path::append(HeaderFileName, Line);
2474018c624SJohn Thompson       llvm::sys::path::native(HeaderFileName);
2484018c624SJohn Thompson     }
2494018c624SJohn Thompson     // Get canonical form.
2504018c624SJohn Thompson     HeaderFileName = getCanonicalPath(HeaderFileName);
2514018c624SJohn Thompson     // Save the resulting header file path.
252adcd0268SBenjamin Kramer     ProblemFileNames.push_back(std::string(HeaderFileName.str()));
2534018c624SJohn Thompson   }
2544018c624SJohn Thompson   return std::error_code();
2554018c624SJohn Thompson }
2564018c624SJohn Thompson 
2579cb79646SJohn Thompson // Load single module map and extract header file list.
loadModuleMap(llvm::StringRef InputPath)2589cb79646SJohn Thompson std::error_code ModularizeUtilities::loadModuleMap(
2599cb79646SJohn Thompson     llvm::StringRef InputPath) {
2609cb79646SJohn Thompson   // Get file entry for module.modulemap file.
2617799ef71SNico Weber   auto ModuleMapEntryOrErr =
2627799ef71SNico Weber     SourceMgr->getFileManager().getFile(InputPath);
2639cb79646SJohn Thompson 
2649cb79646SJohn Thompson   // return error if not found.
2657799ef71SNico Weber   if (!ModuleMapEntryOrErr) {
2669cb79646SJohn Thompson     llvm::errs() << "error: File \"" << InputPath << "\" not found.\n";
2677799ef71SNico Weber     return ModuleMapEntryOrErr.getError();
2689cb79646SJohn Thompson   }
2697799ef71SNico Weber   const FileEntry *ModuleMapEntry = *ModuleMapEntryOrErr;
2709cb79646SJohn Thompson 
2719cb79646SJohn Thompson   // Because the module map parser uses a ForwardingDiagnosticConsumer,
2729cb79646SJohn Thompson   // which doesn't forward the BeginSourceFile call, we do it explicitly here.
2739cb79646SJohn Thompson   DC.BeginSourceFile(*LangOpts, nullptr);
2749cb79646SJohn Thompson 
2759cb79646SJohn Thompson   // Figure out the home directory for the module map file.
2767799ef71SNico Weber   const DirectoryEntry *Dir = ModuleMapEntry->getDir();
2779cb79646SJohn Thompson   StringRef DirName(Dir->getName());
2789cb79646SJohn Thompson   if (llvm::sys::path::filename(DirName) == "Modules") {
2799cb79646SJohn Thompson     DirName = llvm::sys::path::parent_path(DirName);
280a02f8576SHarlan Haskins     if (DirName.endswith(".framework")) {
281a02f8576SHarlan Haskins       if (auto DirEntry = FileMgr->getDirectory(DirName))
282a02f8576SHarlan Haskins         Dir = *DirEntry;
283a02f8576SHarlan Haskins       else
284a02f8576SHarlan Haskins         Dir = nullptr;
285a02f8576SHarlan Haskins     }
2869cb79646SJohn Thompson     // FIXME: This assert can fail if there's a race between the above check
2879cb79646SJohn Thompson     // and the removal of the directory.
2889cb79646SJohn Thompson     assert(Dir && "parent must exist");
2899cb79646SJohn Thompson   }
2909cb79646SJohn Thompson 
2919cb79646SJohn Thompson   std::unique_ptr<ModuleMap> ModMap;
2929cb79646SJohn Thompson   ModMap.reset(new ModuleMap(*SourceMgr, *Diagnostics, *LangOpts,
2939cb79646SJohn Thompson     Target.get(), *HeaderInfo));
2949cb79646SJohn Thompson 
2959cb79646SJohn Thompson   // Parse module.modulemap file into module map.
2969cb79646SJohn Thompson   if (ModMap->parseModuleMapFile(ModuleMapEntry, false, Dir)) {
2979cb79646SJohn Thompson     return std::error_code(1, std::generic_category());
2989cb79646SJohn Thompson   }
2999cb79646SJohn Thompson 
3009cb79646SJohn Thompson   // Do matching end call.
3019cb79646SJohn Thompson   DC.EndSourceFile();
3029cb79646SJohn Thompson 
30396f5551bSJohn Thompson   // Reset missing header count.
30496f5551bSJohn Thompson   MissingHeaderCount = 0;
30596f5551bSJohn Thompson 
3069cb79646SJohn Thompson   if (!collectModuleMapHeaders(ModMap.get()))
3079cb79646SJohn Thompson     return std::error_code(1, std::generic_category());
3089cb79646SJohn Thompson 
3099cb79646SJohn Thompson   // Save module map.
3109cb79646SJohn Thompson   ModuleMaps.push_back(std::move(ModMap));
3119cb79646SJohn Thompson 
3128eb8d936SJohn Thompson   // Indicate we are using module maps.
3138eb8d936SJohn Thompson   HasModuleMap = true;
3148eb8d936SJohn Thompson 
31596f5551bSJohn Thompson   // Return code of 1 for missing headers.
31696f5551bSJohn Thompson   if (MissingHeaderCount)
31796f5551bSJohn Thompson     return std::error_code(1, std::generic_category());
31896f5551bSJohn Thompson 
3199cb79646SJohn Thompson   return std::error_code();
3209cb79646SJohn Thompson }
3219cb79646SJohn Thompson 
3229cb79646SJohn Thompson // Collect module map headers.
3239cb79646SJohn Thompson // Walks the modules and collects referenced headers into
3243c9fb522SJohn Thompson // HeaderFileNames.
collectModuleMapHeaders(clang::ModuleMap * ModMap)3259cb79646SJohn Thompson bool ModularizeUtilities::collectModuleMapHeaders(clang::ModuleMap *ModMap) {
3269cb79646SJohn Thompson   for (ModuleMap::module_iterator I = ModMap->module_begin(),
3279cb79646SJohn Thompson     E = ModMap->module_end();
3289cb79646SJohn Thompson     I != E; ++I) {
3299cb79646SJohn Thompson     if (!collectModuleHeaders(*I->second))
3309cb79646SJohn Thompson       return false;
3319cb79646SJohn Thompson   }
3329cb79646SJohn Thompson   return true;
3339cb79646SJohn Thompson }
3349cb79646SJohn Thompson 
3359cb79646SJohn Thompson // Collect referenced headers from one module.
3369cb79646SJohn Thompson // Collects the headers referenced in the given module into
3373c9fb522SJohn Thompson // HeaderFileNames.
collectModuleHeaders(const clang::Module & Mod)338f868d0b2SPaul Robinson bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) {
3399cb79646SJohn Thompson 
3409cb79646SJohn Thompson   // Ignore explicit modules because they often have dependencies
3419cb79646SJohn Thompson   // we can't know.
3429cb79646SJohn Thompson   if (Mod.IsExplicit)
3439cb79646SJohn Thompson     return true;
3449cb79646SJohn Thompson 
3459cb79646SJohn Thompson   // Treat headers in umbrella directory as dependencies.
3469cb79646SJohn Thompson   DependentsVector UmbrellaDependents;
3479cb79646SJohn Thompson 
3489cb79646SJohn Thompson   // Recursively do submodules.
34908124b11SPiotr Padlewski   for (auto MI = Mod.submodule_begin(), MIEnd = Mod.submodule_end();
3509cb79646SJohn Thompson        MI != MIEnd; ++MI)
3519cb79646SJohn Thompson     collectModuleHeaders(**MI);
3529cb79646SJohn Thompson 
3539d5ae217SRichard Smith   if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) {
3549cb79646SJohn Thompson     std::string HeaderPath = getCanonicalPath(UmbrellaHeader->getName());
3559cb79646SJohn Thompson     // Collect umbrella header.
3569cb79646SJohn Thompson     HeaderFileNames.push_back(HeaderPath);
3579cb79646SJohn Thompson 
3589cb79646SJohn Thompson     // FUTURE: When needed, umbrella header header collection goes here.
3599cb79646SJohn Thompson   }
3609d5ae217SRichard Smith   else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) {
3619cb79646SJohn Thompson     // If there normal headers, assume these are umbrellas and skip collection.
3629cb79646SJohn Thompson     if (Mod.Headers->size() == 0) {
3639cb79646SJohn Thompson       // Collect headers in umbrella directory.
3649cb79646SJohn Thompson       if (!collectUmbrellaHeaders(UmbrellaDir->getName(), UmbrellaDependents))
3659cb79646SJohn Thompson         return false;
3669cb79646SJohn Thompson     }
3679cb79646SJohn Thompson   }
3689cb79646SJohn Thompson 
3699cb79646SJohn Thompson   // We ignore HK_Private, HK_Textual, HK_PrivateTextual, and HK_Excluded,
3709cb79646SJohn Thompson   // assuming they are marked as such either because of unsuitability for
3719cb79646SJohn Thompson   // modules or because they are meant to be included by another header,
3729cb79646SJohn Thompson   // and thus should be ignored by modularize.
3739cb79646SJohn Thompson 
3749cb79646SJohn Thompson   int NormalHeaderCount = Mod.Headers[clang::Module::HK_Normal].size();
3759cb79646SJohn Thompson 
3769cb79646SJohn Thompson   for (int Index = 0; Index < NormalHeaderCount; ++Index) {
3779cb79646SJohn Thompson     DependentsVector NormalDependents;
3789cb79646SJohn Thompson     // Collect normal header.
3799cb79646SJohn Thompson     const clang::Module::Header &Header(
3809cb79646SJohn Thompson       Mod.Headers[clang::Module::HK_Normal][Index]);
3819cb79646SJohn Thompson     std::string HeaderPath = getCanonicalPath(Header.Entry->getName());
3829cb79646SJohn Thompson     HeaderFileNames.push_back(HeaderPath);
3839cb79646SJohn Thompson   }
3849cb79646SJohn Thompson 
38596f5551bSJohn Thompson   int MissingCountThisModule = Mod.MissingHeaders.size();
38696f5551bSJohn Thompson 
38796f5551bSJohn Thompson   for (int Index = 0; Index < MissingCountThisModule; ++Index) {
38896f5551bSJohn Thompson     std::string MissingFile = Mod.MissingHeaders[Index].FileName;
38996f5551bSJohn Thompson     SourceLocation Loc = Mod.MissingHeaders[Index].FileNameLoc;
39096f5551bSJohn Thompson     errs() << Loc.printToString(*SourceMgr)
39196f5551bSJohn Thompson       << ": error : Header not found: " << MissingFile << "\n";
39296f5551bSJohn Thompson   }
39396f5551bSJohn Thompson 
39496f5551bSJohn Thompson   MissingHeaderCount += MissingCountThisModule;
39596f5551bSJohn Thompson 
3969cb79646SJohn Thompson   return true;
3979cb79646SJohn Thompson }
3989cb79646SJohn Thompson 
3999cb79646SJohn Thompson // Collect headers from an umbrella directory.
collectUmbrellaHeaders(StringRef UmbrellaDirName,DependentsVector & Dependents)4009cb79646SJohn Thompson bool ModularizeUtilities::collectUmbrellaHeaders(StringRef UmbrellaDirName,
4019cb79646SJohn Thompson   DependentsVector &Dependents) {
4029cb79646SJohn Thompson   // Initialize directory name.
4039cb79646SJohn Thompson   SmallString<256> Directory(UmbrellaDirName);
4049cb79646SJohn Thompson   // Walk the directory.
4059cb79646SJohn Thompson   std::error_code EC;
4069cb79646SJohn Thompson   for (llvm::sys::fs::directory_iterator I(Directory.str(), EC), E; I != E;
4079cb79646SJohn Thompson     I.increment(EC)) {
4089cb79646SJohn Thompson     if (EC)
4099cb79646SJohn Thompson       return false;
4109cb79646SJohn Thompson     std::string File(I->path());
4110dfdb447SPeter Collingbourne     llvm::ErrorOr<llvm::sys::fs::basic_file_status> Status = I->status();
4120dfdb447SPeter Collingbourne     if (!Status)
4130dfdb447SPeter Collingbourne       return false;
4140dfdb447SPeter Collingbourne     llvm::sys::fs::file_type Type = Status->type();
4159cb79646SJohn Thompson     // If the file is a directory, ignore the name and recurse.
4169cb79646SJohn Thompson     if (Type == llvm::sys::fs::file_type::directory_file) {
4179cb79646SJohn Thompson       if (!collectUmbrellaHeaders(File, Dependents))
4189cb79646SJohn Thompson         return false;
4199cb79646SJohn Thompson       continue;
4209cb79646SJohn Thompson     }
4219cb79646SJohn Thompson     // If the file does not have a common header extension, ignore it.
4229cb79646SJohn Thompson     if (!isHeader(File))
4239cb79646SJohn Thompson       continue;
4249cb79646SJohn Thompson     // Save header name.
4259cb79646SJohn Thompson     std::string HeaderPath = getCanonicalPath(File);
4269cb79646SJohn Thompson     Dependents.push_back(HeaderPath);
4279cb79646SJohn Thompson   }
4289cb79646SJohn Thompson   return true;
4299cb79646SJohn Thompson }
4309cb79646SJohn Thompson 
43184ced5c1SJohn Thompson // Replace .. embedded in path for purposes of having
43284ced5c1SJohn Thompson // a canonical path.
replaceDotDot(StringRef Path)433e7103712SBenjamin Kramer static std::string replaceDotDot(StringRef Path) {
434b70ecf6eSJohn Thompson   SmallString<128> Buffer;
435b70ecf6eSJohn Thompson   llvm::sys::path::const_iterator B = llvm::sys::path::begin(Path),
436b70ecf6eSJohn Thompson     E = llvm::sys::path::end(Path);
437b70ecf6eSJohn Thompson   while (B != E) {
438b70ecf6eSJohn Thompson     if (B->compare(".") == 0) {
439b70ecf6eSJohn Thompson     }
440b70ecf6eSJohn Thompson     else if (B->compare("..") == 0)
441b70ecf6eSJohn Thompson       llvm::sys::path::remove_filename(Buffer);
442b70ecf6eSJohn Thompson     else
443b70ecf6eSJohn Thompson       llvm::sys::path::append(Buffer, *B);
444b70ecf6eSJohn Thompson     ++B;
445b70ecf6eSJohn Thompson   }
446b70ecf6eSJohn Thompson   if (Path.endswith("/") || Path.endswith("\\"))
447b70ecf6eSJohn Thompson     Buffer.append(1, Path.back());
448b70ecf6eSJohn Thompson   return Buffer.c_str();
449b70ecf6eSJohn Thompson }
450b70ecf6eSJohn Thompson 
4513dcb3934SJohn Thompson // Convert header path to canonical form.
4523dcb3934SJohn Thompson // The canonical form is basically just use forward slashes, and remove "./".
4533dcb3934SJohn Thompson // \param FilePath The file path, relative to the module map directory.
4543dcb3934SJohn Thompson // \returns The file path in canonical form.
getCanonicalPath(StringRef FilePath)4553dcb3934SJohn Thompson std::string ModularizeUtilities::getCanonicalPath(StringRef FilePath) {
45684ced5c1SJohn Thompson   std::string Tmp(replaceDotDot(FilePath));
4573dcb3934SJohn Thompson   std::replace(Tmp.begin(), Tmp.end(), '\\', '/');
4583dcb3934SJohn Thompson   StringRef Tmp2(Tmp);
4593dcb3934SJohn Thompson   if (Tmp2.startswith("./"))
460adcd0268SBenjamin Kramer     Tmp = std::string(Tmp2.substr(2));
4613dcb3934SJohn Thompson   return Tmp;
4623dcb3934SJohn Thompson }
4639cb79646SJohn Thompson 
4649cb79646SJohn Thompson // Check for header file extension.
4659cb79646SJohn Thompson // If the file extension is .h, .inc, or missing, it's
4669cb79646SJohn Thompson // assumed to be a header.
4679cb79646SJohn Thompson // \param FileName The file name.  Must not be a directory.
4689cb79646SJohn Thompson // \returns true if it has a header extension or no extension.
isHeader(StringRef FileName)4699cb79646SJohn Thompson bool ModularizeUtilities::isHeader(StringRef FileName) {
4709cb79646SJohn Thompson   StringRef Extension = llvm::sys::path::extension(FileName);
4719cb79646SJohn Thompson   if (Extension.size() == 0)
472b3eef01eSJohn Thompson     return true;
473*86029e4cSMartin Storsjö   if (Extension.equals_insensitive(".h"))
4749cb79646SJohn Thompson     return true;
475*86029e4cSMartin Storsjö   if (Extension.equals_insensitive(".inc"))
4769cb79646SJohn Thompson     return true;
4779cb79646SJohn Thompson   return false;
4789cb79646SJohn Thompson }
4798eb8d936SJohn Thompson 
4808eb8d936SJohn Thompson // Get directory path component from file path.
4818eb8d936SJohn Thompson // \returns the component of the given path, which will be
4828eb8d936SJohn Thompson // relative if the given path is relative, absolute if the
4838eb8d936SJohn Thompson // given path is absolute, or "." if the path has no leading
4848eb8d936SJohn Thompson // path component.
getDirectoryFromPath(StringRef Path)4858eb8d936SJohn Thompson std::string ModularizeUtilities::getDirectoryFromPath(StringRef Path) {
4868eb8d936SJohn Thompson   SmallString<256> Directory(Path);
4878eb8d936SJohn Thompson   sys::path::remove_filename(Directory);
4888eb8d936SJohn Thompson   if (Directory.size() == 0)
4898eb8d936SJohn Thompson     return ".";
490adcd0268SBenjamin Kramer   return std::string(Directory.str());
4918eb8d936SJohn Thompson }
4924018c624SJohn Thompson 
4934018c624SJohn Thompson // Add unique problem file.
4944018c624SJohn Thompson // Also standardizes the path.
addUniqueProblemFile(std::string FilePath)4954018c624SJohn Thompson void ModularizeUtilities::addUniqueProblemFile(std::string FilePath) {
4964018c624SJohn Thompson   FilePath = getCanonicalPath(FilePath);
4974018c624SJohn Thompson   // Don't add if already present.
4984018c624SJohn Thompson   for(auto &TestFilePath : ProblemFileNames) {
4994018c624SJohn Thompson     if (TestFilePath == FilePath)
5004018c624SJohn Thompson       return;
5014018c624SJohn Thompson   }
5024018c624SJohn Thompson   ProblemFileNames.push_back(FilePath);
5034018c624SJohn Thompson }
5044018c624SJohn Thompson 
5054018c624SJohn Thompson // Add file with no compile errors.
5064018c624SJohn Thompson // Also standardizes the path.
addNoCompileErrorsFile(std::string FilePath)5074018c624SJohn Thompson void ModularizeUtilities::addNoCompileErrorsFile(std::string FilePath) {
5084018c624SJohn Thompson   FilePath = getCanonicalPath(FilePath);
5094018c624SJohn Thompson   GoodFileNames.push_back(FilePath);
5104018c624SJohn Thompson }
5114018c624SJohn Thompson 
5124018c624SJohn Thompson // List problem files.
displayProblemFiles()5134018c624SJohn Thompson void ModularizeUtilities::displayProblemFiles() {
5144018c624SJohn Thompson   errs() << "\nThese are the files with possible errors:\n\n";
5154018c624SJohn Thompson   for (auto &ProblemFile : ProblemFileNames) {
5164018c624SJohn Thompson     errs() << ProblemFile << "\n";
5174018c624SJohn Thompson   }
5184018c624SJohn Thompson }
5194018c624SJohn Thompson 
5204018c624SJohn Thompson // List files with no problems.
displayGoodFiles()5214018c624SJohn Thompson void ModularizeUtilities::displayGoodFiles() {
5224018c624SJohn Thompson   errs() << "\nThese are the files with no detected errors:\n\n";
5234018c624SJohn Thompson   for (auto &GoodFile : HeaderFileNames) {
5244018c624SJohn Thompson     bool Good = true;
5254018c624SJohn Thompson     for (auto &ProblemFile : ProblemFileNames) {
5264018c624SJohn Thompson       if (ProblemFile == GoodFile) {
5274018c624SJohn Thompson         Good = false;
5284018c624SJohn Thompson         break;
5294018c624SJohn Thompson       }
5304018c624SJohn Thompson     }
5314018c624SJohn Thompson     if (Good)
5324018c624SJohn Thompson       errs() << GoodFile << "\n";
5334018c624SJohn Thompson   }
5344018c624SJohn Thompson }
5354018c624SJohn Thompson 
5364018c624SJohn Thompson // List files with problem files commented out.
displayCombinedFiles()5374018c624SJohn Thompson void ModularizeUtilities::displayCombinedFiles() {
5384018c624SJohn Thompson   errs() <<
5394018c624SJohn Thompson     "\nThese are the combined files, with problem files preceded by #:\n\n";
5404018c624SJohn Thompson   for (auto &File : HeaderFileNames) {
5414018c624SJohn Thompson     bool Good = true;
5424018c624SJohn Thompson     for (auto &ProblemFile : ProblemFileNames) {
5434018c624SJohn Thompson       if (ProblemFile == File) {
5444018c624SJohn Thompson         Good = false;
5454018c624SJohn Thompson         break;
5464018c624SJohn Thompson       }
5474018c624SJohn Thompson     }
5484018c624SJohn Thompson     errs() << (Good ? "" : "#") << File << "\n";
5494018c624SJohn Thompson   }
5504018c624SJohn Thompson }
551