diff options
author | Logan Chien <loganchien@google.com> | 2019-05-09 12:12:07 -0700 |
---|---|---|
committer | Logan Chien <loganchien@google.com> | 2019-05-14 13:31:23 -0700 |
commit | a2ea9eb478d18c1f5ba8ca24b2f961e0e272404b (patch) | |
tree | 267398e16868359e9d524d723947a6e704847985 | |
parent | d02e81f6a44469486351cfe210f333900aeb376c (diff) | |
download | development-a2ea9eb478d18c1f5ba8ca24b2f961e0e272404b.tar.gz |
header-checker: Add config file support
This commit add config file support to header-abi-diff so that we can
have per-branch configuration.
Bug: 112760591
Test: ./tests/test.py
Test: ${ANDROID_HOST_OUT}/nativetest64/header-checker-unittests/\
header-checker-unittests
Change-Id: I1fbd76c93991a2b36121a5f4397c62eb72d486ec
Merged-In: I1fbd76c93991a2b36121a5f4397c62eb72d486ec
-rw-r--r-- | vndk/tools/header-checker/Android.bp | 2 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/diff/header_abi_diff.cpp | 50 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/utils/config_file.cpp | 87 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/utils/config_file.h | 193 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/utils/config_file_test.cpp | 111 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/utils/string_utils.cpp | 9 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/utils/string_utils.h | 2 | ||||
-rw-r--r-- | vndk/tools/header-checker/src/utils/string_utils_test.cpp | 14 |
8 files changed, 464 insertions, 4 deletions
diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp index d83bd477f..aff2d0554 100644 --- a/vndk/tools/header-checker/Android.bp +++ b/vndk/tools/header-checker/Android.bp @@ -165,6 +165,7 @@ cc_library_host_static { "src/repr/symbol/so_file_parser.cpp", "src/repr/symbol/version_script_parser.cpp", "src/utils/api_level.cpp", + "src/utils/config_file.cpp", "src/utils/collect_exported_headers.cpp", "src/utils/string_utils.cpp", ], @@ -198,6 +199,7 @@ cc_test_host { "src/repr/symbol/exported_symbol_set_test.cpp", "src/repr/symbol/version_script_parser_test.cpp", "src/utils/api_level_test.cpp", + "src/utils/config_file_test.cpp", "src/utils/string_utils_test.cpp", ], diff --git a/vndk/tools/header-checker/src/diff/header_abi_diff.cpp b/vndk/tools/header-checker/src/diff/header_abi_diff.cpp index c3c2b7188..e0ead7423 100644 --- a/vndk/tools/header-checker/src/diff/header_abi_diff.cpp +++ b/vndk/tools/header-checker/src/diff/header_abi_diff.cpp @@ -14,8 +14,13 @@ #include "diff/abi_diff.h" +#include "utils/config_file.h" +#include "utils/string_utils.h" + +#include <llvm/ADT/SmallString.h> #include <llvm/Support/CommandLine.h> #include <llvm/Support/FileSystem.h> +#include <llvm/Support/Path.h> #include <llvm/Support/raw_ostream.h> #include <fstream> @@ -25,6 +30,9 @@ using header_checker::diff::HeaderAbiDiff; using header_checker::repr::CompatibilityStatusIR; using header_checker::repr::DiffPolicyOptions; using header_checker::repr::TextFormatIR; +using header_checker::utils::ConfigFile; +using header_checker::utils::ConfigParser; +using header_checker::utils::ParseBool; static llvm::cl::OptionCategory header_checker_category( @@ -70,10 +78,6 @@ static llvm::cl::opt<bool> check_all_apis( " the dynsym table of a shared library are checked"), llvm::cl::Optional, llvm::cl::cat(header_checker_category)); -static llvm::cl::opt<bool> suppress_local_warnings( - "suppress_local_warnings", llvm::cl::desc("suppress local warnings"), - llvm::cl::Optional, llvm::cl::cat(header_checker_category)); - static llvm::cl::opt<bool> allow_extensions( "allow-extensions", llvm::cl::desc("Do not return a non zero status on extensions"), @@ -142,6 +146,39 @@ static std::set<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) { return ignored_symbols; } +static std::string GetConfigFilePath(const std::string &dump_file_path) { + llvm::SmallString<128> config_file_path(dump_file_path); + llvm::sys::path::remove_filename(config_file_path); + llvm::sys::path::append(config_file_path, "config.ini"); + return config_file_path.str(); +} + +static void ReadConfigFile(const std::string &config_file_path) { + ConfigFile cfg = ConfigParser::ParseFile(config_file_path); + if (cfg.HasSection("global")) { + for (auto &&[key, value] : cfg.GetSection("global")) { + bool value_bool = ParseBool(value); + if (key == "allow_adding_removing_weak_symbols") { + allow_adding_removing_weak_symbols = value_bool; + } else if (key == "advice_only") { + advice_only = value_bool; + } else if (key == "elf_unreferenced_symbol_errors") { + elf_unreferenced_symbol_errors = value_bool; + } else if (key == "check_all_apis") { + check_all_apis = value_bool; + } else if (key == "allow_extensions") { + allow_extensions = value_bool; + } else if (key == "allow_unreferenced_elf_symbol_changes") { + allow_unreferenced_elf_symbol_changes = value_bool; + } else if (key == "allow_unreferenced_changes") { + allow_unreferenced_changes = value_bool; + } else if (key == "consider_opaque_types_different") { + consider_opaque_types_different = value_bool; + } + } + } +} + static const char kWarn[] = "\033[36;1mwarning: \033[0m"; static const char kError[] = "\033[31;1merror: \033[0m"; @@ -157,11 +194,16 @@ bool ShouldEmitWarningMessage(CompatibilityStatusIR status) { int main(int argc, const char **argv) { llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker"); + + ReadConfigFile(GetConfigFilePath(old_dump)); + std::set<std::string> ignored_symbols; if (llvm::sys::fs::exists(ignore_symbol_list)) { ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list); } + DiffPolicyOptions diff_policy_options(consider_opaque_types_different); + HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report, ignored_symbols, allow_adding_removing_weak_symbols, diff_policy_options, check_all_apis, text_format_old, diff --git a/vndk/tools/header-checker/src/utils/config_file.cpp b/vndk/tools/header-checker/src/utils/config_file.cpp new file mode 100644 index 000000000..e4861c62a --- /dev/null +++ b/vndk/tools/header-checker/src/utils/config_file.cpp @@ -0,0 +1,87 @@ +// Copyright (C) 2019 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. + +#include "utils/config_file.h" + +#include "utils/string_utils.h" + +#include <fstream> +#include <map> +#include <string> + + +namespace header_checker { +namespace utils { + + +ConfigFile ConfigParser::ParseFile(std::istream &istream) { + ConfigParser parser(istream); + return parser.ParseFile(); +} + + +ConfigFile ConfigParser::ParseFile(const std::string &path) { + std::ifstream stream(path, std::ios_base::in); + return ParseFile(stream); +} + + +ConfigFile ConfigParser::ParseFile() { + size_t line_no = 0; + std::string line; + while (std::getline(stream_, line)) { + ParseLine(++line_no, line); + } + return std::move(cfg_); +} + + +void ConfigParser::ParseLine(size_t line_no, std::string_view line) { + if (line.empty() || line[0] == ';' || line[0] == '#') { + // Skip empty or comment line. + return; + } + + // Parse section name line. + if (line[0] == '[') { + std::string::size_type pos = line.rfind(']'); + if (pos == std::string::npos) { + ReportError(line_no, "bad section name line"); + return; + } + std::string_view section_name = line.substr(1, pos - 1); + section_ = &cfg_.map_[std::string(section_name)]; + return; + } + + // Parse key-value line. + std::string::size_type pos = line.find('='); + if (pos == std::string::npos) { + ReportError(line_no, "bad key-value line"); + return; + } + + // Add key-value entry to current section. + std::string_view key = Trim(line.substr(0, pos)); + std::string_view value = Trim(line.substr(pos + 1)); + + if (!section_) { + section_ = &cfg_.map_[""]; + } + section_->map_[std::string(key)] = std::string(value); +} + + +} // namespace utils +} // namespace header_checker diff --git a/vndk/tools/header-checker/src/utils/config_file.h b/vndk/tools/header-checker/src/utils/config_file.h new file mode 100644 index 000000000..cf8c6d905 --- /dev/null +++ b/vndk/tools/header-checker/src/utils/config_file.h @@ -0,0 +1,193 @@ +// Copyright (C) 2019 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 CONFIG_FILE_H_ +#define CONFIG_FILE_H_ + +#include <cassert> +#include <iosfwd> +#include <map> +#include <string> + + +namespace header_checker { +namespace utils { + + +class ConfigParser; + + +class ConfigSection { + public: + using MapType = std::map<std::string, std::string>; + using const_iterator = MapType::const_iterator; + + + public: + ConfigSection() = default; + ConfigSection(ConfigSection &&) = default; + ConfigSection &operator=(ConfigSection &&) = default; + + bool HasProperty(const std::string &name) const { + return map_.find(name) != map_.end(); + } + + std::string GetProperty(const std::string &name) const { + auto &&it = map_.find(name); + if (it == map_.end()) { + return ""; + } + return it->second; + } + + std::string operator[](const std::string &name) const { + return GetProperty(name); + } + + const_iterator begin() const { + return map_.begin(); + } + + const_iterator end() const { + return map_.end(); + } + + + private: + ConfigSection(const ConfigSection &) = delete; + ConfigSection &operator=(const ConfigSection &) = delete; + + + private: + std::map<std::string, std::string> map_; + + friend class ConfigParser; +}; + + +class ConfigFile { + public: + using MapType = std::map<std::string, ConfigSection>; + using const_iterator = MapType::const_iterator; + + + public: + ConfigFile() = default; + ConfigFile(ConfigFile &&) = default; + ConfigFile &operator=(ConfigFile &&) = default; + + bool HasSection(const std::string §ion_name) const { + return map_.find(section_name) != map_.end(); + } + + const ConfigSection &GetSection(const std::string §ion_name) const { + auto &&it = map_.find(section_name); + assert(it != map_.end()); + return it->second; + } + + const ConfigSection &operator[](const std::string §ion_name) const { + return GetSection(section_name); + } + + bool HasProperty(const std::string §ion_name, + const std::string &property_name) const { + auto &&it = map_.find(section_name); + if (it == map_.end()) { + return false; + } + return it->second.HasProperty(property_name); + } + + std::string GetProperty(const std::string §ion_name, + const std::string &property_name) const { + auto &&it = map_.find(section_name); + if (it == map_.end()) { + return ""; + } + return it->second.GetProperty(property_name); + } + + const_iterator begin() const { + return map_.begin(); + } + + const_iterator end() const { + return map_.end(); + } + + + private: + ConfigFile(const ConfigFile &) = delete; + ConfigFile &operator=(const ConfigFile &) = delete; + + + private: + std::map<std::string, ConfigSection> map_; + + friend class ConfigParser; +}; + + +class ConfigParser { + public: + using ErrorListener = std::function<void (size_t, const char *)>; + + + public: + ConfigParser(std::istream &stream) + : stream_(stream), section_(nullptr) { } + + ConfigFile ParseFile(); + + static ConfigFile ParseFile(std::istream &istream); + + static ConfigFile ParseFile(const std::string &path); + + void SetErrorListener(ErrorListener listener) { + error_listener_ = std::move(listener); + } + + + private: + void ParseLine(size_t line_no, std::string_view line); + + void ReportError(size_t line_no, const char *cause) { + if (error_listener_) { + error_listener_(line_no, cause); + } + } + + + private: + ConfigParser(const ConfigParser &) = delete; + ConfigParser &operator=(const ConfigParser &) = delete; + + + private: + std::istream &stream_; + + ErrorListener error_listener_; + + ConfigSection *section_; + + ConfigFile cfg_; +}; + + +} // namespace utils +} // namespace header_checker + + +#endif // CONFIG_FILE_H_ diff --git a/vndk/tools/header-checker/src/utils/config_file_test.cpp b/vndk/tools/header-checker/src/utils/config_file_test.cpp new file mode 100644 index 000000000..28ead1040 --- /dev/null +++ b/vndk/tools/header-checker/src/utils/config_file_test.cpp @@ -0,0 +1,111 @@ +// Copyright (C) 2019 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. + +#include "utils/config_file.h" + +#include <sstream> + +#include <gtest/gtest.h> + + +namespace header_checker { +namespace utils { + + +TEST(ConfigParserTest, Parse) { + std::stringstream stream(R"( +# Comment line starts with hash symbol +; Comment line starts with semicolon + +[section1] +key1 = value1 +key2 = value2 + +[section2] +key1 = true +key2 = false +)"); + + auto &&cfg = ConfigParser::ParseFile(stream); + EXPECT_TRUE(cfg.HasSection("section1")); + EXPECT_TRUE(cfg.HasSection("section2")); + EXPECT_FALSE(cfg.HasSection("section3")); + + auto &§ion1 = cfg.GetSection("section1"); + EXPECT_TRUE(section1.HasProperty("key1")); + EXPECT_EQ("value1", section1.GetProperty("key1")); + EXPECT_TRUE(section1.HasProperty("key2")); + EXPECT_EQ("value2", section1.GetProperty("key2")); + + EXPECT_FALSE(section1.HasProperty("key3")); + EXPECT_EQ("", section1.GetProperty("key3")); + + auto &§ion2 = cfg.GetSection("section2"); + EXPECT_TRUE(section2.HasProperty("key1")); + EXPECT_EQ("true", section2.GetProperty("key1")); + EXPECT_TRUE(section2.HasProperty("key2")); + EXPECT_EQ("false", section2.GetProperty("key2")); + + EXPECT_EQ("value1", cfg.GetProperty("section1", "key1")); + EXPECT_EQ("value2", cfg.GetProperty("section1", "key2")); + + EXPECT_EQ("value1", cfg["section1"]["key1"]); + EXPECT_EQ("value2", cfg["section1"]["key2"]); +} + + +TEST(ConfigParserTest, BadSectionNameLine) { + std::stringstream stream(R"( +[section1 +key1 = value1 +)"); + + size_t num_errors = 0; + + ConfigParser parser(stream); + parser.SetErrorListener( + [&num_errors](size_t line_no, const char *cause) { + ++num_errors; + EXPECT_EQ(2, line_no); + EXPECT_STREQ("bad section name line", cause); + }); + parser.ParseFile(); + + EXPECT_EQ(1, num_errors); +} + + +TEST(ConfigParserTest, BadKeyValueLine) { + std::stringstream stream(R"( +[section1] +key1 +)"); + + size_t num_errors = 0; + + ConfigParser parser(stream); + parser.SetErrorListener( + [&num_errors](size_t line_no, const char *cause) { + ++num_errors; + EXPECT_EQ(3, line_no); + EXPECT_STREQ("bad key-value line", cause); + }); + parser.ParseFile(); + + EXPECT_EQ(1, num_errors); +} + + +} // namespace utils +} // namespace header_checker diff --git a/vndk/tools/header-checker/src/utils/string_utils.cpp b/vndk/tools/header-checker/src/utils/string_utils.cpp index ced07f754..2dd97aded 100644 --- a/vndk/tools/header-checker/src/utils/string_utils.cpp +++ b/vndk/tools/header-checker/src/utils/string_utils.cpp @@ -14,6 +14,8 @@ #include "utils/string_utils.h" +#include <algorithm> +#include <cctype> #include <cstdlib> #include <string> #include <utility> @@ -86,6 +88,13 @@ std::optional<int> ParseInt(const std::string &s) { } +bool ParseBool(const std::string &s) { + std::string value(s); + std::transform(value.begin(), value.end(), value.begin(), std::tolower); + return (value == "true" || value == "on" || value == "1"); +} + + bool IsGlobPattern(std::string_view s) { return s.find_first_of("*?[") != std::string::npos; } diff --git a/vndk/tools/header-checker/src/utils/string_utils.h b/vndk/tools/header-checker/src/utils/string_utils.h index 8d546f400..bcb840504 100644 --- a/vndk/tools/header-checker/src/utils/string_utils.h +++ b/vndk/tools/header-checker/src/utils/string_utils.h @@ -35,6 +35,8 @@ std::vector<std::string_view> Split(std::string_view s, std::optional<int> ParseInt(const std::string &s); +bool ParseBool(const std::string &s); + bool IsGlobPattern(std::string_view s); diff --git a/vndk/tools/header-checker/src/utils/string_utils_test.cpp b/vndk/tools/header-checker/src/utils/string_utils_test.cpp index 6c5a874a8..4ad007440 100644 --- a/vndk/tools/header-checker/src/utils/string_utils_test.cpp +++ b/vndk/tools/header-checker/src/utils/string_utils_test.cpp @@ -94,6 +94,20 @@ TEST(StringUtilsTest, ParseInt) { } +TEST(StringUtilsTest, ParseBool) { + EXPECT_FALSE(ParseBool("")); + EXPECT_FALSE(ParseBool("false")); + EXPECT_FALSE(ParseBool("off")); + EXPECT_FALSE(ParseBool("0")); + + EXPECT_TRUE(ParseBool("TRUE")); + EXPECT_TRUE(ParseBool("True")); + EXPECT_TRUE(ParseBool("true")); + EXPECT_TRUE(ParseBool("ON")); + EXPECT_TRUE(ParseBool("1")); +} + + TEST(StringUtilsTest, IsGlobPattern) { EXPECT_TRUE(IsGlobPattern("*.so")); EXPECT_TRUE(IsGlobPattern("[ab].txt")); |