diff --git a/iwyu.cc b/iwyu.cc index d96fe33..392e1f5 100644 --- a/iwyu.cc +++ b/iwyu.cc @@ -3644,12 +3644,19 @@ class IwyuAstConsumer // We use an ASTFrontendAction to hook up IWYU with Clang. class IwyuAction : public ASTFrontendAction { + public: + IwyuAction(const char* executable_path) + : executable_path_(executable_path) + { + } + protected: virtual ASTConsumer* CreateASTConsumer(CompilerInstance& compiler, // NOLINT llvm::StringRef /* dummy */) { // Do this first thing after getting our hands on a CompilerInstance. InitGlobals(&compiler.getSourceManager(), - &compiler.getPreprocessor().getHeaderSearchInfo()); + &compiler.getPreprocessor().getHeaderSearchInfo(), + executable_path_); IwyuPreprocessorInfo* const preprocessor_consumer = new IwyuPreprocessorInfo(); @@ -3662,6 +3669,9 @@ class IwyuAction : public ASTFrontendAction { compiler.getPreprocessor().addCommentHandler(preprocessor_consumer); return ast_consumer; } + + private: + std::string executable_path_; }; @@ -3704,7 +3714,7 @@ int main(int argc, char **argv) { CHECK_(compiler && "Failed to process argv"); // Create and execute the frontend to generate an LLVM bitcode module. - llvm::OwningPtr action(new IwyuAction); + llvm::OwningPtr action(new IwyuAction(clang_argv[0])); if (!compiler->ExecuteAction(*action)) return 1; diff --git a/iwyu_globals.cc b/iwyu_globals.cc index 1cef24f..c9e567b 100644 --- a/iwyu_globals.cc +++ b/iwyu_globals.cc @@ -49,7 +49,6 @@ static SourceManagerCharacterDataGetter* data_getter = NULL; static FullUseCache* function_calls_full_use_cache = NULL; static FullUseCache* class_members_full_use_cache = NULL; - static void PrintHelp(const char* extra_msg) { printf("USAGE: iwyu [-Xiwyu --iwyu_opt]... \n" "Here are the you can specify:\n" @@ -182,7 +181,9 @@ static vector ComputeHeaderSearchPaths( return NormalizeHeaderSearchPaths(search_path_map); } -void InitGlobals(clang::SourceManager* sm, clang::HeaderSearch* header_search) { +void InitGlobals(clang::SourceManager* sm, + clang::HeaderSearch* header_search, + const std::string& executable_path) { CHECK_(sm && "InitGlobals() needs a non-NULL SourceManager"); source_manager = sm; data_getter = new SourceManagerCharacterDataGetter(*source_manager); @@ -199,6 +200,11 @@ void InitGlobals(clang::SourceManager* sm, clang::HeaderSearch* header_search) { VERRS(6) << "Search path: " << it->path << " (" << path_type_name << ")\n"; } + // Set up mapping file search paths. + include_picker->AddMappingFileSearchPath("."); + include_picker->AddMappingFileSearchPath(GetParentPath(executable_path)); + + // Add mappings. for (Each it(&GlobalFlags().mapping_files); !it.AtEnd(); ++it) { include_picker->AddMappingsFromFile(*it); } diff --git a/iwyu_globals.h b/iwyu_globals.h index 6efa309..3dec3db 100644 --- a/iwyu_globals.h +++ b/iwyu_globals.h @@ -42,7 +42,8 @@ class SourceManagerCharacterDataGetter; int ParseIwyuCommandlineFlags(int argc, char** argv); void InitGlobals(clang::SourceManager* source_manager, - clang::HeaderSearch* header_search); + clang::HeaderSearch* header_search, + const std::string& executable_path); // Can be called by tests -- doesn't need a SourceManager or // argc/argv. Note that GlobalSourceManager() and DefaultDataGetter() diff --git a/iwyu_include_picker.cc b/iwyu_include_picker.cc index c8acfc2..cabd6f9 100644 --- a/iwyu_include_picker.cc +++ b/iwyu_include_picker.cc @@ -26,10 +26,9 @@ #include "iwyu_verrs.h" #include "port.h" // for CHECK_ -#include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Regex.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLParser.h" @@ -279,6 +278,16 @@ void IncludePicker::AddDirectInclude(const string& includer_filepath, } } +void IncludePicker::AddMappingFileSearchPath(const string& path) { + string absolute_path = MakeAbsolutePath(path); + if (std::find(mapping_file_search_path_.begin(), + mapping_file_search_path_.end(), + absolute_path) == mapping_file_search_path_.end()) { + VERRS(6) << "Adding mapping file search path: " << absolute_path << "\n"; + mapping_file_search_path_.push_back(absolute_path); + } +} + void IncludePicker::AddMapping(const string& map_from, const string& map_to) { VERRS(4) << "Adding mapping from " << map_from << " to " << map_to << "\n"; CHECK_(!has_called_finalize_added_include_lines_ && "Can't mutate anymore"); @@ -493,6 +502,33 @@ string IncludePicker::MaybeGetIncludeNameAsWritten( return value ? *value : ""; } +error_code IncludePicker::TryReadMappingFile( + const string& filename, + OwningPtr& buffer) const { + string absolute_path; + if (IsAbsolutePath(filename)) { + VERRS(5) << "Absolute mapping filename: " << filename << ".\n"; + absolute_path = filename; + } else { + VERRS(5) << "Relative mapping filename: " << filename << ". " + << "Scanning search path.\n"; + // Scan search path + for (Each it(&mapping_file_search_path_); !it.AtEnd(); ++it) { + string candidate = MakeAbsolutePath(*it, filename); + if (llvm::sys::fs::exists(candidate)) { + absolute_path = candidate; + VERRS(5) << "Found mapping file: " << candidate << ".\n"; + break; + } + } + } + + error_code error = MemoryBuffer::getFile(absolute_path, buffer); + VERRS(5) << "Opened mapping file: " << filename << "? " + << error.message() << "\n"; + return error; +} + vector IncludePicker::GetCandidateHeadersForSymbol( const string& symbol) const { CHECK_(has_called_finalize_added_include_lines_ && "Must finalize includes"); @@ -579,13 +615,11 @@ bool IncludePicker::HasMapping(const string& map_from_filepath, // We use this to maintain mappings externally, to make it easier // to update/adjust to local circumstances. void IncludePicker::AddMappingsFromFile(const string& filename) { - // TODO(kimgr): Resolve absolute mapping path along some search path - // e.g. look also next to IWYU binary. OwningPtr buffer; - llvm::error_code result = MemoryBuffer::getFile(filename, buffer); - if (result != 0) { + error_code error = TryReadMappingFile(filename, buffer); + if (error) { errs() << "Cannot open mapping file '" << filename << "': " - << result.message() << ".\n"; + << error.message() << ".\n"; return; } @@ -680,7 +714,7 @@ void IncludePicker::AddMappingsFromFile(const string& filename) { mapping[2], to_visibility); } else if (directive == "ref") { - // Mapping ref, recurse. + // Mapping ref. string ref_file = GetScalarValue(it->getValue()); if (ref_file.empty()) { errs() << MappingDiag(source_manager, filename, current_node, @@ -688,6 +722,11 @@ void IncludePicker::AddMappingsFromFile(const string& filename) { return; } + // Add the path of the file we're currently processing + // to the search path. Allows refs to be relative to referrer. + AddMappingFileSearchPath(GetParentPath(filename)); + + // Recurse. AddMappingsFromFile(ref_file); } else { errs() << MappingDiag(source_manager, filename, current_node, diff --git a/iwyu_include_picker.h b/iwyu_include_picker.h index b7ae8a1..2215421 100644 --- a/iwyu_include_picker.h +++ b/iwyu_include_picker.h @@ -50,6 +50,13 @@ #include // for pair #include // for vector +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace llvm { + class error_code; +} + namespace include_what_you_use { using std::map; @@ -59,6 +66,10 @@ using std::string; using std::vector; +using llvm::OwningPtr; +using llvm::MemoryBuffer; +using llvm::error_code; + class IncludePicker { public: enum Visibility { kUnusedVisibility, kPublic, kPrivate }; @@ -76,6 +87,9 @@ class IncludePicker { const string& includee_filepath, const string& quoted_include_as_written); + // Add a mapping file search path. + void AddMappingFileSearchPath(const string& path); + // Add this to say "map_to re-exports everything in file map_from". // Both map_to and map_from should be quoted includes. void AddMapping(const string& map_from, const string& map_to); @@ -182,6 +196,11 @@ class IncludePicker { string MaybeGetIncludeNameAsWritten(const string& includer_filepath, const string& includee_filepath) const; + // Scan the search paths for filename. If it exists, put file contents + // in buffer. If not, return the error code. + error_code TryReadMappingFile(const string& filename, + OwningPtr& buffer) const; + // From symbols to includes. IncludeMap symbol_include_map_; @@ -217,6 +236,8 @@ class IncludePicker { // contents of friend_to_headers_map_["@\"foo/bar/.*\""]. map > friend_to_headers_map_; + vector mapping_file_search_path_; + // Make sure we don't do any non-const operations after finalizing. bool has_called_finalize_added_include_lines_; }; // class IncludePicker diff --git a/iwyu_path_util.cc b/iwyu_path_util.cc index a4acd0e..53f82ba 100644 --- a/iwyu_path_util.cc +++ b/iwyu_path_util.cc @@ -13,6 +13,12 @@ #include "iwyu_stl_util.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/PathV2.h" +#include "llvm/Support/system_error.h" + namespace include_what_you_use { namespace { @@ -123,6 +129,30 @@ string NormalizeFilePath(const string& path) { return result; } +bool IsAbsolutePath(const string& path) { + return llvm::sys::path::is_absolute(path); +} + +string MakeAbsolutePath(const string& path) { + llvm::SmallString<128> absolute_path(path.c_str()); + llvm::error_code error = llvm::sys::fs::make_absolute(absolute_path); + CHECK_(!error); + + return absolute_path.str(); +} + +string MakeAbsolutePath(const string& base_path, const string& relative_path) { + llvm::SmallString<128> absolute_path(base_path.c_str()); + llvm::sys::path::append(absolute_path, relative_path); + + return absolute_path.str(); +} + +string GetParentPath(const string& path) { + llvm::StringRef parent = llvm::sys::path::parent_path(path); + return parent.str(); +} + // Converts a file-path, such as /usr/include/stdio.h, to a // quoted include, such as . string ConvertToQuotedInclude(const string& filepath) { diff --git a/iwyu_path_util.h b/iwyu_path_util.h index c4ec715..ce4cf9d 100644 --- a/iwyu_path_util.h +++ b/iwyu_path_util.h @@ -62,11 +62,20 @@ string CanonicalizeFilePath(const string& path); // platforms. string GetCanonicalName(string file_path); - // "Canonicals" the name on Microsoft platforms, then recursively // removes all "./" prefixes. string NormalizeFilePath(const string& path); +// Is path absolute? +bool IsAbsolutePath(const string& path); + +// Get absolute version of path. +string MakeAbsolutePath(const string& path); +string MakeAbsolutePath(const string& base_path, const string& relative_path); + +// Get the parent of path. +string GetParentPath(const string& path); + // Below, we talk 'quoted' includes. A quoted include is something // that would be written on an #include line, complete with the <> or // "". In the line '#include ', "" is the quoted