Merge "Extend header-abi-dumper/linker --root-dir" am: ece19b2c70

Original change: https://android-review.googlesource.com/c/platform/development/+/1761252

Change-Id: I62671415596fd0ce386650c9390e63959bc94ef9
This commit is contained in:
Hsin-Yi Chen
2021-07-16 10:33:43 +00:00
committed by Automerger Merge Worker
11 changed files with 213 additions and 73 deletions

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -16,7 +16,6 @@
#include "dumper/abi_wrappers.h"
#include "repr/ir_dumper.h"
#include "utils/header_abi_util.h"
#include <clang/AST/PrettyPrinter.h>
#include <clang/AST/QualTypeNames.h>
@@ -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<clang::MangleContext> 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<repr::ModuleIR> module(
new repr::ModuleIR(nullptr /*FIXME*/));

View File

@@ -15,6 +15,8 @@
#ifndef AST_UTIL_H_
#define AST_UTIL_H_
#include "utils/source_path_utils.h"
#include <clang/AST/AST.h>
#include <clang/AST/Type.h>
@@ -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<const clang::Decl *, std::string> decl_to_source_file_cache_;
llvm::DenseSet<clang::QualType> converted_qual_types_;

View File

@@ -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<std::string> exported_header_dirs(
"I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
llvm::cl::ZeroOrMore, llvm::cl::cat(header_checker_category));
static llvm::cl::opt<std::string> root_dir(
static llvm::cl::list<std::string> 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 <path>:<replacement> or "
"<path>. 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<bool> 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<std::string> 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<std::string> 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);

View File

@@ -16,6 +16,7 @@
#define HEADER_CHECKER_H_
#include "repr/ir_representation.h"
#include "utils/source_path_utils.h"
#include <set>
#include <string>
@@ -30,7 +31,7 @@ class HeaderCheckerOptions {
std::string source_file_;
std::string dump_name_;
const std::set<std::string> 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<std::string> 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) {}

View File

@@ -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 <llvm/ADT/Optional.h>
#include <llvm/Support/CommandLine.h>
@@ -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<std::string> exported_header_dirs(
"I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
static llvm::cl::opt<std::string> root_dir(
static llvm::cl::list<std::string> 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 <path>:<replacement> or "
"<path>. 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<std::string> version_script(
"v", llvm::cl::desc("<version_script>"), 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();

View File

@@ -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<std::string>
CollectAllExportedHeaders(const std::vector<std::string> &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) {

View File

@@ -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 <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
@@ -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<std::string> &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<std::string> *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<std::string>
CollectAllExportedHeaders(const std::vector<std::string> &exported_header_dirs,
const std::string &root_dir) {
const RootDirs &root_dirs) {
std::set<std::string> 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);
}

View File

@@ -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 <set>
#include <string>
#include <vector>
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<RootDir> RootDirs;
RootDirs ParseRootDirs(const std::vector<std::string> &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<std::string>
CollectAllExportedHeaders(const std::vector<std::string> &exported_header_dirs,
const RootDirs &root_dirs);
} // namespace utils
} // namespace header_checker
#endif // SOURCE_PATH_UTILS_H_

View File

@@ -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 <gtest/gtest.h>
@@ -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<std::string> 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<std::string>());
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<std::string> 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<std::string> 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