summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLogan Chien <loganchien@google.com>2019-05-09 12:12:07 -0700
committerLogan Chien <loganchien@google.com>2019-05-14 13:31:23 -0700
commita2ea9eb478d18c1f5ba8ca24b2f961e0e272404b (patch)
tree267398e16868359e9d524d723947a6e704847985
parentd02e81f6a44469486351cfe210f333900aeb376c (diff)
downloaddevelopment-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.bp2
-rw-r--r--vndk/tools/header-checker/src/diff/header_abi_diff.cpp50
-rw-r--r--vndk/tools/header-checker/src/utils/config_file.cpp87
-rw-r--r--vndk/tools/header-checker/src/utils/config_file.h193
-rw-r--r--vndk/tools/header-checker/src/utils/config_file_test.cpp111
-rw-r--r--vndk/tools/header-checker/src/utils/string_utils.cpp9
-rw-r--r--vndk/tools/header-checker/src/utils/string_utils.h2
-rw-r--r--vndk/tools/header-checker/src/utils/string_utils_test.cpp14
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 &section_name) const {
+ return map_.find(section_name) != map_.end();
+ }
+
+ const ConfigSection &GetSection(const std::string &section_name) const {
+ auto &&it = map_.find(section_name);
+ assert(it != map_.end());
+ return it->second;
+ }
+
+ const ConfigSection &operator[](const std::string &section_name) const {
+ return GetSection(section_name);
+ }
+
+ bool HasProperty(const std::string &section_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 &section_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 &&section1 = 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 &&section2 = 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"));