diff --git a/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp b/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp index 388f44c14..2ff038538 100644 --- a/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp +++ b/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp @@ -69,7 +69,7 @@ ABIWrapper::ABIWrapper( std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl, const clang::CompilerInstance *cip, - const std::string &root_dir) { + const utils::RootDirs &root_dirs) { clang::SourceManager &sm = cip->getSourceManager(); clang::SourceLocation location = decl->getLocation(); // We need to use the expansion location to identify whether we should recurse @@ -80,7 +80,7 @@ std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl, // belonging to the library. clang::SourceLocation expansion_location = sm.getExpansionLoc(location); return utils::NormalizePath(sm.getFilename(expansion_location).str(), - root_dir); + root_dirs); } std::string ABIWrapper::GetCachedDeclSourceFile( @@ -88,7 +88,7 @@ std::string ABIWrapper::GetCachedDeclSourceFile( assert(decl != nullptr); auto result = ast_caches_->decl_to_source_file_cache_.find(decl); if (result == ast_caches_->decl_to_source_file_cache_.end()) { - return GetDeclSourceFile(decl, cip, ast_caches_->root_dir_); + return GetDeclSourceFile(decl, cip, ast_caches_->root_dirs_); } return result->second; } diff --git a/vndk/tools/header-checker/src/dumper/abi_wrappers.h b/vndk/tools/header-checker/src/dumper/abi_wrappers.h index 92e101988..b2a039ef1 100644 --- a/vndk/tools/header-checker/src/dumper/abi_wrappers.h +++ b/vndk/tools/header-checker/src/dumper/abi_wrappers.h @@ -49,7 +49,7 @@ class ABIWrapper { public: static std::string GetDeclSourceFile(const clang::Decl *decl, const clang::CompilerInstance *cip, - const std::string &root_dir); + const utils::RootDirs &root_dirs); protected: std::string GetCachedDeclSourceFile(const clang::Decl *decl, diff --git a/vndk/tools/header-checker/src/dumper/ast_processing.cpp b/vndk/tools/header-checker/src/dumper/ast_processing.cpp index de97da635..12dcbb30d 100644 --- a/vndk/tools/header-checker/src/dumper/ast_processing.cpp +++ b/vndk/tools/header-checker/src/dumper/ast_processing.cpp @@ -16,7 +16,6 @@ #include "dumper/abi_wrappers.h" #include "repr/ir_dumper.h" -#include "utils/header_abi_util.h" #include #include @@ -33,14 +32,15 @@ namespace dumper { class PrintNormalizedPath : public clang::PrintingCallbacks { public: - PrintNormalizedPath(const std::string root_dir) : root_dir_(root_dir) {} + PrintNormalizedPath(const utils::RootDirs &root_dirs) + : root_dirs_(root_dirs) {} std::string remapPath(llvm::StringRef path) const { - return utils::NormalizePath(path.str(), root_dir_); + return utils::NormalizePath(path.str(), root_dirs_); } private: - const std::string root_dir_; + const utils::RootDirs &root_dirs_; }; HeaderASTVisitor::HeaderASTVisitor( @@ -106,7 +106,7 @@ bool HeaderASTVisitor::ShouldSkipFunctionDecl(const clang::FunctionDecl *decl) { if (!decl->getDefinition()) { if (!options_.dump_function_declarations_ || options_.source_file_ != - ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dir_)) { + ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dirs_)) { return true; } } @@ -177,7 +177,7 @@ bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) { return true; } std::string source_file = - ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dir_); + ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dirs_); ast_caches_->decl_to_source_file_cache_.insert( std::make_pair(decl, source_file)); // If no exported headers are specified we assume the whole AST is exported. @@ -208,15 +208,15 @@ void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { // names to avoid inconsistency between C and C++ (for C++ files, this is true // by default) policy.SuppressTagKeyword = true; - PrintNormalizedPath callbacks(options_.root_dir_); + PrintNormalizedPath callbacks(options_.root_dirs_); policy.Callbacks = &callbacks; ctx.setPrintingPolicy(policy); clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl(); std::unique_ptr mangle_contextp( ctx.createMangleContext()); - ASTCaches ast_caches( - ABIWrapper::GetDeclSourceFile(translation_unit, cip_, options_.root_dir_), - options_.root_dir_); + ASTCaches ast_caches(ABIWrapper::GetDeclSourceFile(translation_unit, cip_, + options_.root_dirs_), + options_.root_dirs_); std::unique_ptr module( new repr::ModuleIR(nullptr /*FIXME*/)); diff --git a/vndk/tools/header-checker/src/dumper/ast_util.h b/vndk/tools/header-checker/src/dumper/ast_util.h index a4872e781..9960f0b6e 100644 --- a/vndk/tools/header-checker/src/dumper/ast_util.h +++ b/vndk/tools/header-checker/src/dumper/ast_util.h @@ -15,6 +15,8 @@ #ifndef AST_UTIL_H_ #define AST_UTIL_H_ +#include "utils/source_path_utils.h" + #include #include @@ -30,12 +32,12 @@ namespace dumper { struct ASTCaches { ASTCaches(const std::string &translation_unit_source, - const std::string &root_dir) - : translation_unit_source_(translation_unit_source), root_dir_(root_dir) { - } + const utils::RootDirs &root_dirs) + : translation_unit_source_(translation_unit_source), + root_dirs_(root_dirs) {} std::string translation_unit_source_; - const std::string root_dir_; + const utils::RootDirs &root_dirs_; std::map decl_to_source_file_cache_; llvm::DenseSet converted_qual_types_; diff --git a/vndk/tools/header-checker/src/dumper/header_checker.cpp b/vndk/tools/header-checker/src/dumper/header_checker.cpp index 7832ce813..209aebb5f 100644 --- a/vndk/tools/header-checker/src/dumper/header_checker.cpp +++ b/vndk/tools/header-checker/src/dumper/header_checker.cpp @@ -42,9 +42,11 @@ using header_checker::dumper::HeaderCheckerFrontendActionFactory; using header_checker::dumper::HeaderCheckerOptions; using header_checker::repr::TextFormatIR; using header_checker::utils::CollectAllExportedHeaders; -using header_checker::utils::GetCwd; using header_checker::utils::HideIrrelevantCommandLineOptions; using header_checker::utils::NormalizePath; +using header_checker::utils::ParseRootDirs; +using header_checker::utils::RootDir; +using header_checker::utils::RootDirs; static llvm::cl::OptionCategory header_checker_category( @@ -63,11 +65,13 @@ static llvm::cl::list exported_header_dirs( "I", llvm::cl::desc(""), llvm::cl::Prefix, llvm::cl::ZeroOrMore, llvm::cl::cat(header_checker_category)); -static llvm::cl::opt root_dir( +static llvm::cl::list root_dirs( "root-dir", - llvm::cl::desc("Specify the directory that the paths in the dump file are " - "relative to. Default to current working directory"), - llvm::cl::Optional, llvm::cl::cat(header_checker_category)); + llvm::cl::desc("Specify the directory that the paths in the dump files " + "are relative to. The format is : or " + ". If this option is not specified, it defaults to " + "current working directory."), + llvm::cl::ZeroOrMore, llvm::cl::cat(header_checker_category)); static llvm::cl::opt no_filter( "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional, @@ -172,17 +176,17 @@ int main(int argc, const char **argv) { ::exit(1); } - const std::string root_dir_or_cwd = (root_dir.empty() ? GetCwd() : root_dir); + RootDirs parsed_root_dirs = ParseRootDirs(root_dirs); bool dump_exported_only = (!no_filter && !exported_header_dirs.empty()); std::set exported_headers = - CollectAllExportedHeaders(exported_header_dirs, root_dir_or_cwd); + CollectAllExportedHeaders(exported_header_dirs, parsed_root_dirs); // Initialize clang tools and run front-end action. std::vector header_files{ header_file }; HeaderCheckerOptions options( - NormalizePath(header_file, root_dir_or_cwd), out_dump, - std::move(exported_headers), root_dir_or_cwd, output_format, + NormalizePath(header_file, parsed_root_dirs), out_dump, + std::move(exported_headers), std::move(parsed_root_dirs), output_format, dump_exported_only, dump_function_declarations, suppress_errors); clang::tooling::ClangTool tool(*compilations, header_files); diff --git a/vndk/tools/header-checker/src/dumper/header_checker.h b/vndk/tools/header-checker/src/dumper/header_checker.h index 53c917a26..7a7506de7 100644 --- a/vndk/tools/header-checker/src/dumper/header_checker.h +++ b/vndk/tools/header-checker/src/dumper/header_checker.h @@ -16,6 +16,7 @@ #define HEADER_CHECKER_H_ #include "repr/ir_representation.h" +#include "utils/source_path_utils.h" #include #include @@ -30,7 +31,7 @@ class HeaderCheckerOptions { std::string source_file_; std::string dump_name_; const std::set exported_headers_; - const std::string root_dir_; + const utils::RootDirs root_dirs_; repr::TextFormatIR text_format_; const bool dump_exported_only_; bool dump_function_declarations_; @@ -39,12 +40,12 @@ class HeaderCheckerOptions { public: HeaderCheckerOptions(std::string source_file, std::string dump_name, std::set exported_headers, - std::string root_dir, repr::TextFormatIR text_format, - bool dump_exported_only, + utils::RootDirs root_dirs, + repr::TextFormatIR text_format, bool dump_exported_only, bool dump_function_declarations, bool suppress_errors) : source_file_(std::move(source_file)), dump_name_(std::move(dump_name)), exported_headers_(std::move(exported_headers)), - root_dir_(std::move(root_dir)), text_format_(text_format), + root_dirs_(std::move(root_dirs)), text_format_(text_format), dump_exported_only_(dump_exported_only), dump_function_declarations_(dump_function_declarations), suppress_errors_(suppress_errors) {} diff --git a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp index f8570813d..e877150b5 100644 --- a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp +++ b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp @@ -19,7 +19,7 @@ #include "repr/symbol/so_file_parser.h" #include "repr/symbol/version_script_parser.h" #include "utils/command_line_utils.h" -#include "utils/header_abi_util.h" +#include "utils/source_path_utils.h" #include #include @@ -39,8 +39,9 @@ using namespace header_checker; using header_checker::repr::TextFormatIR; using header_checker::utils::CollectAllExportedHeaders; -using header_checker::utils::GetCwd; using header_checker::utils::HideIrrelevantCommandLineOptions; +using header_checker::utils::ParseRootDirs; +using header_checker::utils::RootDir; static llvm::cl::OptionCategory header_linker_category( @@ -58,11 +59,13 @@ static llvm::cl::list exported_header_dirs( "I", llvm::cl::desc(""), llvm::cl::Prefix, llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category)); -static llvm::cl::opt root_dir( +static llvm::cl::list root_dirs( "root-dir", - llvm::cl::desc("Specify the directory that the paths in the dump files are " - "relative to. Default to current working directory"), - llvm::cl::Optional, llvm::cl::cat(header_linker_category)); + llvm::cl::desc("Specify the directory that the paths in the dump files " + "are relative to. The format is : or " + ". If this option is not specified, it defaults to " + "current working directory."), + llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category)); static llvm::cl::opt version_script( "v", llvm::cl::desc(""), llvm::cl::Optional, @@ -255,8 +258,8 @@ bool HeaderAbiLinker::LinkAndDump() { } // Construct the list of exported headers for source location filtering. - exported_headers_ = CollectAllExportedHeaders( - exported_header_dirs_, root_dir.empty() ? GetCwd() : root_dir); + exported_headers_ = CollectAllExportedHeaders(exported_header_dirs_, + ParseRootDirs(root_dirs)); // Read all input ABI dumps. auto merger = ReadInputDumpFiles(); diff --git a/vndk/tools/header-checker/src/utils/header_abi_util.h b/vndk/tools/header-checker/src/utils/header_abi_util.h index c9c6b1d1f..9915c5203 100644 --- a/vndk/tools/header-checker/src/utils/header_abi_util.h +++ b/vndk/tools/header-checker/src/utils/header_abi_util.h @@ -26,16 +26,6 @@ namespace header_checker { namespace utils { -std::string GetCwd(); - -// Resolve '..' and '.'; if the path starts with root_dir, remove the prefix; -// don't resolve symbolic links. -std::string NormalizePath(const std::string &path, const std::string &root_dir); - -std::set -CollectAllExportedHeaders(const std::vector &exported_header_dirs, - const std::string &root_dir); - inline std::string FindAndReplace(const std::string &candidate_str, const std::string &find_str, const std::string &replace_str) { diff --git a/vndk/tools/header-checker/src/utils/source_path_utils.cpp b/vndk/tools/header-checker/src/utils/source_path_utils.cpp index bc69809f1..5e6daf429 100644 --- a/vndk/tools/header-checker/src/utils/source_path_utils.cpp +++ b/vndk/tools/header-checker/src/utils/source_path_utils.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "utils/header_abi_util.h" +#include "utils/source_path_utils.h" #include #include @@ -37,33 +37,87 @@ static bool ShouldSkipFile(llvm::StringRef &file_name) { file_name.endswith(".cc") || file_name.endswith(".c")); } -std::string GetCwd() { +static std::string GetCwd() { llvm::SmallString<256> cwd; if (llvm::sys::fs::current_path(cwd)) { llvm::errs() << "ERROR: Failed to get current working directory\n"; ::exit(1); } - return cwd.c_str(); + return std::string(cwd); } -std::string NormalizePath(const std::string &path, - const std::string &root_dir) { - llvm::SmallString<256> norm_path(path); +RootDirs ParseRootDirs(const std::vector &args) { + RootDirs root_dirs; + for (const std::string_view arg : args) { + std::string_view path; + std::string_view replacement; + size_t colon_index = arg.find(":"); + if (colon_index != std::string_view::npos) { + path = arg.substr(0, colon_index); + replacement = arg.substr(colon_index + 1); + } else { + path = arg; + replacement = ""; + } + llvm::SmallString<256> norm_replacement(replacement.begin(), + replacement.end()); + llvm::sys::path::remove_dots(norm_replacement, /* remove_dot_dot = */ true); + root_dirs.emplace_back(NormalizePath(path, {}), + std::string(norm_replacement)); + } + if (root_dirs.empty()) { + root_dirs.emplace_back(GetCwd(), ""); + } + // Sort by length in descending order so that NormalizePath finds the longest + // matching root dir. + std::sort(root_dirs.begin(), root_dirs.end(), + [](RootDir &first, RootDir &second) { + return first.path.size() > second.path.size(); + }); + for (size_t index = 1; index < root_dirs.size(); index++) { + if (root_dirs[index - 1].path == root_dirs[index].path) { + llvm::errs() << "Duplicate root dir: " << root_dirs[index].path << "\n"; + ::exit(1); + } + } + return root_dirs; +} + +std::string NormalizePath(std::string_view path, const RootDirs &root_dirs) { + llvm::SmallString<256> norm_path(path.begin(), path.end()); if (llvm::sys::fs::make_absolute(norm_path)) { return ""; } llvm::sys::path::remove_dots(norm_path, /* remove_dot_dot = */ true); - // Convert /cwd/path to /path. - if (llvm::sys::path::replace_path_prefix(norm_path, root_dir, "")) { - // Convert /path to path. - return llvm::sys::path::relative_path(norm_path.str()).str(); + llvm::StringRef separator = llvm::sys::path::get_separator(); + // Convert /root/dir/path to path. + for (const RootDir &root_dir : root_dirs) { + // llvm::sys::path::replace_path_prefix("AB", "A", "") returns "B", so do + // not use it. + if (!norm_path.startswith(root_dir.path)) { + continue; + } + if (norm_path.size() == root_dir.path.size()) { + return root_dir.replacement; + } + llvm::StringRef suffix = norm_path.substr(root_dir.path.size()); + if (suffix.startswith(separator)) { + if (root_dir.replacement.empty()) { + return suffix.substr(separator.size()).str(); + } + // replacement == "/" + if (llvm::StringRef(root_dir.replacement).endswith(separator)) { + return root_dir.replacement + suffix.substr(separator.size()).str(); + } + return root_dir.replacement + suffix.str(); + } } return std::string(norm_path); } static bool CollectExportedHeaderSet(const std::string &dir_name, std::set *exported_headers, - const std::string &root_dir) { + const RootDirs &root_dirs) { std::error_code ec; llvm::sys::fs::recursive_directory_iterator walker(dir_name, ec); // Default construction - end of directory. @@ -98,17 +152,17 @@ static bool CollectExportedHeaderSet(const std::string &dir_name, continue; } - exported_headers->insert(NormalizePath(file_path, root_dir)); + exported_headers->insert(NormalizePath(file_path, root_dirs)); } return true; } std::set CollectAllExportedHeaders(const std::vector &exported_header_dirs, - const std::string &root_dir) { + const RootDirs &root_dirs) { std::set exported_headers; for (auto &&dir : exported_header_dirs) { - if (!CollectExportedHeaderSet(dir, &exported_headers, root_dir)) { + if (!CollectExportedHeaderSet(dir, &exported_headers, root_dirs)) { llvm::errs() << "Couldn't collect exported headers\n"; ::exit(1); } diff --git a/vndk/tools/header-checker/src/utils/source_path_utils.h b/vndk/tools/header-checker/src/utils/source_path_utils.h new file mode 100644 index 000000000..efd927cc5 --- /dev/null +++ b/vndk/tools/header-checker/src/utils/source_path_utils.h @@ -0,0 +1,52 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_PATH_UTILS_H_ +#define SOURCE_PATH_UTILS_H_ + +#include +#include +#include + + +namespace header_checker { +namespace utils { + + +struct RootDir { + std::string path; + std::string replacement; + + RootDir(std::string p, std::string r) + : path(std::move(p)), replacement(std::move(r)) {} +}; + +typedef std::vector RootDirs; + +RootDirs ParseRootDirs(const std::vector &args); + +// Resolve '..' and '.'; if the path starts with any of root_dirs, replace the +// prefix; don't resolve symbolic links. +std::string NormalizePath(std::string_view path, const RootDirs &root_dirs); + +std::set +CollectAllExportedHeaders(const std::vector &exported_header_dirs, + const RootDirs &root_dirs); + + +} // namespace utils +} // namespace header_checker + + +#endif // SOURCE_PATH_UTILS_H_ diff --git a/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp b/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp index 4ef1c6509..2b74eba16 100644 --- a/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp +++ b/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "utils/header_abi_util.h" +#include "utils/source_path_utils.h" #include @@ -21,25 +21,59 @@ namespace header_checker { namespace utils { -TEST(CollectExportedHeadersTest, NormalizeAbsolutePaths) { - const std::string root = "/root/dir"; - EXPECT_EQ("", NormalizePath(root, root)); - EXPECT_EQ("/unit/test", NormalizePath("/unit/test", root)); - EXPECT_EQ("/root/unit/test", NormalizePath(root + "/../unit/test", root)); +TEST(SourcePathUtilsTest, NormalizeAbsolutePaths) { + const std::vector args{"/root/dir"}; + const RootDirs root_dirs = ParseRootDirs(args); + ASSERT_EQ(1, root_dirs.size()); + ASSERT_EQ("/root/dir", root_dirs[0].path); + ASSERT_EQ("", root_dirs[0].replacement); + + EXPECT_EQ("", NormalizePath("/root/dir", root_dirs)); + EXPECT_EQ("test", NormalizePath("/root/dir/test", root_dirs)); + EXPECT_EQ("/root/unit/test", + NormalizePath("/root/dir/../unit/test", root_dirs)); } -TEST(CollectExportedHeadersTest, NormalizeCwdPaths) { - const std::string cwd = GetCwd(); - ASSERT_NE("", cwd); +TEST(SourcePathUtilsTest, NormalizeCwdPaths) { + const RootDirs cwd = ParseRootDirs(std::vector()); + ASSERT_EQ(1, cwd.size()); + ASSERT_NE("", cwd[0].path); + ASSERT_EQ("", cwd[0].replacement); + EXPECT_EQ("", NormalizePath("", cwd)); EXPECT_EQ("unit/test", NormalizePath("./unit/test/.", cwd)); EXPECT_EQ("unit/test", NormalizePath("unit//test//", cwd)); EXPECT_EQ("test", NormalizePath("unit/../test", cwd)); - EXPECT_EQ("unit/test", NormalizePath(cwd + "/unit/test", cwd)); + EXPECT_EQ("unit/test", NormalizePath(cwd[0].path + "/unit/test", cwd)); EXPECT_EQ('/', NormalizePath("../unit/test", cwd)[0]); } +TEST(SourcePathUtilsTest, NormalizePathsWithMultipleRootDirs) { + const std::vector args{"/before:/", "/before/dir:after"}; + const RootDirs root_dirs = ParseRootDirs(args); + ASSERT_EQ(2, root_dirs.size()); + ASSERT_EQ("/before/dir", root_dirs[0].path); + ASSERT_EQ("after", root_dirs[0].replacement); + ASSERT_EQ("/before", root_dirs[1].path); + ASSERT_EQ("/", root_dirs[1].replacement); + + EXPECT_EQ("/directory", NormalizePath("/before/directory", root_dirs)); + EXPECT_EQ("after", NormalizePath("/before/dir", root_dirs)); +} + + +TEST(SourcePathUtilsTest, NormalizeRelativePaths) { + const std::vector args{"../before/.:..//after/."}; + const RootDirs root_dirs = ParseRootDirs(args); + ASSERT_EQ(1, root_dirs.size()); + ASSERT_EQ('/', root_dirs[0].path[0]); + ASSERT_EQ("../after", root_dirs[0].replacement); + + EXPECT_EQ("../after", NormalizePath("../before", root_dirs)); +} + + } // namespace utils } // namespace header_checker