| //===--- iwyu_path_util.cc - file-path utilities for include-what-you-use -===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "iwyu_path_util.h" |
| |
| #include <stddef.h> |
| |
| #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 { |
| vector<HeaderSearchPath>* header_search_paths; |
| } // namespace |
| |
| void SetHeaderSearchPaths(const vector<HeaderSearchPath>& search_paths) { |
| if (header_search_paths != NULL) { |
| delete header_search_paths; |
| } |
| header_search_paths = new vector<HeaderSearchPath>(search_paths); |
| } |
| |
| const vector<HeaderSearchPath>& HeaderSearchPaths() { |
| if (header_search_paths == NULL) { |
| header_search_paths = new vector<HeaderSearchPath>(); |
| } |
| return *header_search_paths; |
| } |
| |
| namespace { |
| |
| } // namespace |
| |
| bool IsHeaderFile(string path) { |
| if (EndsWith(path, "\"") || EndsWith(path, ">")) |
| path = path.substr(0, path.length() - 1); |
| |
| // Some headers don't have an extension (e.g. <string>), or have an |
| // unusual one (the compiler doesn't care), so it's safer to |
| // enumerate non-header extensions instead. |
| if (EndsWith(path, ".cc") || EndsWith(path, ".c") || |
| EndsWith(path, ".cxx") || EndsWith(path, ".cpp")) |
| return false; |
| return true; |
| } |
| |
| string GetCWD() { |
| char cwd[PATH_MAX]; |
| if (getcwd(cwd, sizeof(cwd))) |
| return cwd; |
| return ""; |
| } |
| |
| string Basename(const string& path) { |
| string::size_type last_slash = path.rfind('/'); |
| if (last_slash != string::npos) { |
| return path.substr(last_slash + 1); |
| } |
| return path; |
| } |
| |
| string CanonicalizeFilePath(const string& path) { |
| string result = path; |
| |
| #ifdef _WIN32 |
| // canonicalise directory separators (forward slashes considered canonical) |
| for (size_t i = 0; i < result.size(); ++i) { |
| if (result[i] == '\\') |
| result[i] = '/'; |
| } |
| #endif |
| |
| // We may also want to collapse ../ here. |
| |
| return result; |
| } |
| |
| string GetCanonicalName(string file_path) { |
| // Get rid of any <> and "" in case file_path is really an #include line. |
| StripLeft(&file_path, "\"") || StripLeft(&file_path, "<"); |
| StripRight(&file_path, "\"") || StripRight(&file_path, ">"); |
| |
| file_path = CanonicalizeFilePath(file_path); |
| |
| StripRight(&file_path, ".h") |
| || StripRight(&file_path, ".hpp") |
| || StripRight(&file_path, ".hxx") |
| || StripRight(&file_path, ".hh") |
| || StripRight(&file_path, ".inl") |
| || StripRight(&file_path, ".cxx") |
| || StripRight(&file_path, ".cpp") |
| || StripRight(&file_path, ".cc") |
| || StripRight(&file_path, ".c"); |
| StripRight(&file_path, "_unittest") |
| || StripRight(&file_path, "_regtest") |
| || StripRight(&file_path, "_test") |
| || StripLeft(&file_path, "test_headercompile_"); |
| StripRight(&file_path, "-inl"); |
| // .h files in /public/ match .cc files in /internal/ |
| const string::size_type internal_pos = file_path.find("/internal/"); |
| if (internal_pos != string::npos) |
| file_path = (file_path.substr(0, internal_pos) + "/public/" + |
| file_path.substr(internal_pos + strlen("/internal/"))); |
| |
| // .h files in /include/ match .cc files in /src/ |
| const string::size_type include_pos = file_path.find("/include/"); |
| if (include_pos != string::npos) |
| file_path = (file_path.substr(0, include_pos) + "/src/" + |
| file_path.substr(include_pos + strlen("/include/"))); |
| return file_path; |
| } |
| |
| string NormalizeFilePath(const string& path) { |
| string result = CanonicalizeFilePath(path); |
| while (StripLeft(&result, "./")) { |
| } |
| 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 <stdio.h>. |
| string ConvertToQuotedInclude(const string& filepath) { |
| // First, get rid of leading ./'s and the like. |
| string path = NormalizeFilePath(filepath); |
| |
| // Case 1: Uses an explicit entry on the search path (-I) list. |
| const vector<HeaderSearchPath>& search_paths = HeaderSearchPaths(); |
| // HeaderSearchPaths is sorted to be longest-first, so this |
| // loop will prefer the longest prefix: /usr/include/c++/4.4/foo |
| // will be mapped to <foo>, not <c++/4.4/foo>. |
| for (Each<HeaderSearchPath> it(&search_paths); !it.AtEnd(); ++it) { |
| if (StripLeft(&path, it->path)) { |
| StripLeft(&path, "/"); |
| if (it->path_type == HeaderSearchPath::kSystemPath) |
| return "<" + path + ">"; |
| else |
| return "\"" + path + "\""; |
| } |
| } |
| |
| |
| // Case 2: Uses the implicit "-I." entry on the search path. Always local. |
| return "\"" + path + "\""; |
| } |
| |
| bool IsQuotedInclude(const string& s) { |
| if (s.size() < 2) |
| return false; |
| return ((StartsWith(s, "<") && EndsWith(s, ">")) || |
| (StartsWith(s, "\"") && EndsWith(s, "\""))); |
| } |
| |
| // Returns whether this is a system (as opposed to user) include file, |
| // based on where it lives. |
| bool IsSystemIncludeFile(const string& filepath) { |
| return ConvertToQuotedInclude(filepath)[0] == '<'; |
| } |
| |
| // Returns true if the given file is third-party. Google-authored |
| // code living in third_party/ is not considered third-party. |
| bool IsThirdPartyFile(string quoted_path) { |
| if (!StripLeft("ed_path, "\"third_party/")) |
| return false; |
| |
| // These are Google-authored libraries living in third_party/ |
| // because of old licensing constraints. |
| if (StartsWith(quoted_path, "car/") || |
| StartsWith(quoted_path, "gtest/") || |
| StartsWith(quoted_path, "gmock/")) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace include_what_you_use |