versioner: replace availability attributes with annotate.
Major refactor to use __attribute__((annotate)) to be able to keep
track of the semantic differences between __INTRODUCED_IN(x) and
__INTRODUCED_IN_X86(x), for use in the upcoming preprocessor.
Bug: http://b/30170081
Change-Id: I6496a8c40ba7f4553de9a2be0bbddcf37c813937
diff --git a/tools/versioner/Android.mk b/tools/versioner/Android.mk
index 3036300..4cf30e2 100644
--- a/tools/versioner/Android.mk
+++ b/tools/versioner/Android.mk
@@ -14,6 +14,7 @@
LOCAL_SRC_FILES := \
src/versioner.cpp \
+ src/Arch.cpp \
src/DeclarationDatabase.cpp \
src/SymbolDatabase.cpp \
src/Utils.cpp
diff --git a/tools/versioner/src/Arch.cpp b/tools/versioner/src/Arch.cpp
new file mode 100644
index 0000000..49dad8a
--- /dev/null
+++ b/tools/versioner/src/Arch.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 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 "Arch.h"
+
+#include <err.h>
+
+#include <string>
+
+std::string to_string(const Arch& arch) {
+ switch (arch) {
+ case Arch::arm:
+ return "arm";
+
+ case Arch::arm64:
+ return "arm64";
+
+ case Arch::mips:
+ return "mips";
+
+ case Arch::mips64:
+ return "mips64";
+
+ case Arch::x86:
+ return "x86";
+
+ case Arch::x86_64:
+ return "x86_64";
+ }
+
+ errx(1, "unknown arch '%zu'", size_t(arch));
+}
+
+Arch arch_from_string(const std::string& name) {
+ if (name == "arm") {
+ return Arch::arm;
+ } else if (name == "arm64") {
+ return Arch::arm64;
+ } else if (name == "mips") {
+ return Arch::mips;
+ } else if (name == "mips64") {
+ return Arch::mips64;
+ } else if (name == "x86") {
+ return Arch::x86;
+ } else if (name == "x86_64") {
+ return Arch::x86_64;
+ }
+
+ errx(1, "unknown architecture '%s'", name.c_str());
+}
diff --git a/tools/versioner/src/Arch.h b/tools/versioner/src/Arch.h
new file mode 100644
index 0000000..7fdd17d
--- /dev/null
+++ b/tools/versioner/src/Arch.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include <array>
+#include <initializer_list>
+#include <set>
+#include <string>
+
+enum class Arch : size_t {
+ arm = 0,
+ arm64,
+ mips,
+ mips64,
+ x86,
+ x86_64,
+};
+
+std::string to_string(const Arch& arch);
+Arch arch_from_string(const std::string& name);
+
+template <typename T>
+class ArchMapIterator;
+
+template <typename T>
+class ArchMap {
+ public:
+ ArchMap() {
+ }
+
+ ArchMap(std::initializer_list<std::pair<Arch, T>> initializer) {
+ for (auto& pair : initializer) {
+ this->operator[](pair.first) = pair.second;
+ }
+ }
+
+ T& operator[](Arch arch) {
+ return data_[size_t(arch)];
+ }
+
+ const T& operator[](Arch arch) const {
+ return data_[size_t(arch)];
+ }
+
+ bool operator==(const ArchMap& other) const {
+ for (size_t i = 0; i < data_.size(); ++i) {
+ if (data_[i] != other.data_[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ArchMapIterator<T> begin() const {
+ return ArchMapIterator<T>(*this, Arch::arm);
+ }
+
+ ArchMapIterator<T> end() const {
+ return ArchMapIterator<T>(*this, Arch(size_t(Arch::x86_64) + 1));
+ }
+
+ private:
+ std::array<T, size_t(Arch::x86_64) + 1> data_;
+};
+
+template <typename T>
+class ArchMapIterator {
+ const ArchMap<T>& map_;
+ Arch arch_ = Arch::arm;
+
+ public:
+ ArchMapIterator() = delete;
+
+ ArchMapIterator(const ArchMap<T>& map, Arch arch) : map_(map), arch_(arch) {
+ }
+
+ bool operator==(const ArchMapIterator<T>& rhs) const {
+ return map_ == rhs.map_ && arch_ == rhs.arch_;
+ }
+
+ bool operator!=(const ArchMapIterator<T>& rhs) const {
+ return !(*this == rhs);
+ }
+
+ ArchMapIterator& operator++() {
+ arch_ = Arch(size_t(arch_) + 1);
+ return *this;
+ }
+
+ ArchMapIterator operator++(int) {
+ ArchMapIterator result = *this;
+ ++*this;
+ return result;
+ }
+
+ std::pair<const Arch&, const T&> operator*() const {
+ return std::tie(arch_, map_[arch_]);
+ }
+
+ std::pair<const Arch&, const T&> operator->() const {
+ return std::tie(arch_, map_[arch_]);
+ }
+};
+
+static const std::set<Arch> supported_archs = {
+ Arch::arm,
+ Arch::arm64,
+ Arch::mips,
+ Arch::mips64,
+ Arch::x86,
+ Arch::x86_64,
+};
+
+static ArchMap<std::string> arch_targets = {
+ { Arch::arm, "arm-linux-androideabi" },
+ { Arch::arm64, "aarch64-linux-android" },
+ { Arch::mips, "mipsel-linux-android" },
+ { Arch::mips64, "mips64el-linux-android" },
+ { Arch::x86, "i686-linux-android" },
+ { Arch::x86_64, "x86_64-linux-android" },
+};
+
+static const std::set<int> supported_levels = { 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 24 };
+
+static const ArchMap<int> arch_min_api = {
+ { Arch::arm, 9 },
+ { Arch::arm64, 21 },
+ { Arch::mips, 9 },
+ { Arch::mips64, 21 },
+ { Arch::x86, 9 },
+ { Arch::x86_64, 21 },
+};
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index 9f02588..85ad9ba 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -16,10 +16,15 @@
#include "DeclarationDatabase.h"
+#include <err.h>
+
#include <iostream>
#include <map>
+#include <mutex>
#include <set>
+#include <sstream>
#include <string>
+#include <utility>
#include <clang/AST/AST.h>
#include <clang/AST/Attr.h>
@@ -32,12 +37,13 @@
class Visitor : public RecursiveASTVisitor<Visitor> {
HeaderDatabase& database;
+ CompilationType type;
SourceManager& src_manager;
std::unique_ptr<MangleContext> mangler;
public:
- Visitor(HeaderDatabase& database, ASTContext& ctx)
- : database(database), src_manager(ctx.getSourceManager()) {
+ Visitor(HeaderDatabase& database, CompilationType type, ASTContext& ctx)
+ : database(database), type(type), src_manager(ctx.getSourceManager()) {
mangler.reset(ItaniumMangleContext::create(ctx, ctx.getDiagnostics()));
}
@@ -114,66 +120,237 @@
return true;
}
- // Look for availability annotations.
+ auto start_loc = src_manager.getPresumedLoc(decl->getLocStart());
+ auto end_loc = src_manager.getPresumedLoc(decl->getLocEnd());
+
+ Location location = {
+ .filename = start_loc.getFilename(),
+ .start = {
+ .line = start_loc.getLine(),
+ .column = start_loc.getColumn(),
+ },
+ .end = {
+ .line = end_loc.getLine(),
+ .column = end_loc.getColumn(),
+ }
+ };
+
DeclarationAvailability availability;
- for (const AvailabilityAttr* attr : decl->specific_attrs<AvailabilityAttr>()) {
- if (attr->getPlatform()->getName() != "android") {
- fprintf(stderr, "skipping non-android platform %s\n",
- attr->getPlatform()->getName().str().c_str());
- continue;
+
+ // Find and parse __ANDROID_AVAILABILITY_DUMP__ annotations.
+ for (const AnnotateAttr* attr : decl->specific_attrs<AnnotateAttr>()) {
+ llvm::StringRef annotation = attr->getAnnotation();
+ if (annotation == "introduced_in_future") {
+ // Tag the compiled-for arch, since this can vary across archs.
+ availability.arch_availability[type.arch].future = true;
+ } else {
+ llvm::SmallVector<llvm::StringRef, 2> fragments;
+ annotation.split(fragments, "=");
+ if (fragments.size() != 2) {
+ continue;
+ }
+
+ auto& global_availability = availability.global_availability;
+ auto& arch_availability = availability.arch_availability;
+ std::map<std::string, std::vector<int*>> prefix_map = {
+ { "introduced_in", { &global_availability.introduced } },
+ { "deprecated_in", { &global_availability.deprecated } },
+ { "obsoleted_in", { &global_availability.obsoleted } },
+ { "introduced_in_arm", { &arch_availability[Arch::arm].introduced } },
+ { "introduced_in_mips", { &arch_availability[Arch::mips].introduced } },
+ { "introduced_in_x86", { &arch_availability[Arch::x86].introduced } },
+ { "introduced_in_32",
+ { &arch_availability[Arch::arm].introduced,
+ &arch_availability[Arch::mips].introduced,
+ &arch_availability[Arch::x86].introduced } },
+ { "introduced_in_64",
+ { &arch_availability[Arch::arm64].introduced,
+ &arch_availability[Arch::mips64].introduced,
+ &arch_availability[Arch::x86_64].introduced } },
+ };
+
+ auto it = prefix_map.find(fragments[0]);
+ if (it == prefix_map.end()) {
+ continue;
+ }
+ int value;
+ if (fragments[1].getAsInteger(10, value)) {
+ errx(1, "invalid __ANDROID_AVAILABILITY_DUMP__ annotation: '%s'",
+ annotation.str().c_str());
+ }
+
+ for (int* ptr : it->second) {
+ *ptr = value;
+ }
}
- if (attr->getIntroduced().getMajor() != 0) {
- availability.introduced = attr->getIntroduced().getMajor();
- }
- if (attr->getDeprecated().getMajor() != 0) {
- availability.deprecated = attr->getDeprecated().getMajor();
- }
- if (attr->getObsoleted().getMajor() != 0) {
- availability.obsoleted = attr->getObsoleted().getMajor();
- }
+ }
+
+ auto symbol_it = database.symbols.find(declaration_name);
+ if (symbol_it == database.symbols.end()) {
+ Symbol symbol = {.name = declaration_name };
+ bool dummy;
+ std::tie(symbol_it, dummy) = database.symbols.insert({ declaration_name, symbol });
}
// Find or insert an entry for the declaration.
- auto declaration_it = database.declarations.find(declaration_name);
- if (declaration_it == database.declarations.end()) {
- Declaration declaration = {.name = declaration_name };
- bool inserted;
- std::tie(declaration_it, inserted) =
- database.declarations.insert({ declaration_name, declaration });
- }
-
- auto& declaration_locations = declaration_it->second.locations;
- auto presumed_loc = src_manager.getPresumedLoc(decl->getLocation());
- DeclarationLocation location = {
- .filename = presumed_loc.getFilename(),
- .line_number = presumed_loc.getLine(),
- .column = presumed_loc.getColumn(),
- .type = declaration_type,
- .is_extern = is_extern,
- .is_definition = is_definition,
- .availability = availability,
- };
-
- // It's fine if the location is already there, we'll get an iterator to the existing element.
- auto location_it = declaration_locations.begin();
- bool inserted = false;
- std::tie(location_it, inserted) = declaration_locations.insert(location);
-
- // If we didn't insert, check to see if the availability attributes are identical.
- if (!inserted) {
- if (location_it->availability != availability) {
- fprintf(stderr, "ERROR: availability attribute mismatch\n");
- decl->dump();
- abort();
+ auto declaration_it = symbol_it->second.declarations.find(location);
+ if (declaration_it == symbol_it->second.declarations.end()) {
+ Declaration declaration;
+ declaration.location = location;
+ declaration.is_extern = is_extern;
+ declaration.is_definition = is_definition;
+ declaration.availability.insert(std::make_pair(type, availability));
+ symbol_it->second.declarations.insert(std::make_pair(location, declaration));
+ } else {
+ if (declaration_it->second.is_extern != is_extern ||
+ declaration_it->second.is_definition != is_definition) {
+ errx(1, "varying declaration of '%s' at %s:%u:%u", declaration_name.c_str(),
+ location.filename.c_str(), location.start.line, location.start.column);
}
+ declaration_it->second.availability.insert(std::make_pair(type, availability));
}
return true;
}
};
-void HeaderDatabase::parseAST(ASTUnit* ast) {
+bool DeclarationAvailability::merge(const DeclarationAvailability& other) {
+#define check_avail(expr) error |= (!this->expr.empty() && this->expr != other.expr);
+ bool error = false;
+
+ if (!other.global_availability.empty()) {
+ check_avail(global_availability);
+ this->global_availability = other.global_availability;
+ }
+
+ for (Arch arch : supported_archs) {
+ if (!other.arch_availability[arch].empty()) {
+ check_avail(arch_availability[arch]);
+ this->arch_availability[arch] = other.arch_availability[arch];
+ }
+ }
+#undef check_avail
+
+ return !error;
+}
+
+bool Declaration::calculateAvailability(DeclarationAvailability* output) const {
+ DeclarationAvailability avail;
+ for (const auto& it : this->availability) {
+ if (!avail.merge(it.second)) {
+ return false;
+ }
+ }
+ *output = avail;
+ return true;
+}
+
+bool Symbol::calculateAvailability(DeclarationAvailability* output) const {
+ DeclarationAvailability avail;
+ for (const auto& it : this->declarations) {
+ // Don't merge availability for inline functions (because they shouldn't have any).
+ if (it.second.is_definition) {
+ continue;
+ }
+
+ DeclarationAvailability decl_availability;
+ if (!it.second.calculateAvailability(&decl_availability)) {
+ return false;
+ abort();
+ }
+
+ if (!avail.merge(decl_availability)) {
+ return false;
+ }
+ }
+ *output = avail;
+ return true;
+}
+
+bool Symbol::hasDeclaration(const CompilationType& type) const {
+ for (const auto& decl_it : this->declarations) {
+ for (const auto& compilation_it : decl_it.second.availability) {
+ if (compilation_it.first == type) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void HeaderDatabase::parseAST(CompilationType type, ASTUnit* ast) {
+ std::unique_lock<std::mutex> lock(this->mutex);
ASTContext& ctx = ast->getASTContext();
- Visitor visitor(*this, ctx);
+ Visitor visitor(*this, type, ctx);
visitor.TraverseDecl(ctx.getTranslationUnitDecl());
}
+
+std::string to_string(const CompilationType& type) {
+ std::stringstream ss;
+ ss << to_string(type.arch) << "-" << type.api_level;
+ return ss.str();
+}
+
+std::string to_string(const AvailabilityValues& av) {
+ std::stringstream ss;
+
+ if (av.future) {
+ ss << "future, ";
+ }
+
+ if (av.introduced != 0) {
+ ss << "introduced = " << av.introduced << ", ";
+ }
+
+ if (av.deprecated != 0) {
+ ss << "deprecated = " << av.deprecated << ", ";
+ }
+
+ if (av.obsoleted != 0) {
+ ss << "obsoleted = " << av.obsoleted << ", ";
+ }
+
+ std::string result = ss.str();
+ if (!result.empty()) {
+ result = result.substr(0, result.length() - 2);
+ }
+ return result;
+}
+
+std::string to_string(const DeclarationType& type) {
+ switch (type) {
+ case DeclarationType::function:
+ return "function";
+ case DeclarationType::variable:
+ return "variable";
+ case DeclarationType::inconsistent:
+ return "inconsistent";
+ }
+ abort();
+}
+
+std::string to_string(const DeclarationAvailability& decl_av) {
+ std::stringstream ss;
+ if (!decl_av.global_availability.empty()) {
+ ss << to_string(decl_av.global_availability) << ", ";
+ }
+
+ for (auto it : decl_av.arch_availability) {
+ if (!it.second.empty()) {
+ ss << to_string(it.first) << ": " << to_string(it.second) << ", ";
+ }
+ }
+
+ std::string result = ss.str();
+ if (result.size() == 0) {
+ return "no availability";
+ }
+
+ return result.substr(0, result.length() - 2);
+}
+
+std::string to_string(const Location& loc) {
+ std::stringstream ss;
+ ss << loc.filename << ":" << loc.start.line << ":" << loc.start.column;
+ return ss.str();
+}
diff --git a/tools/versioner/src/DeclarationDatabase.h b/tools/versioner/src/DeclarationDatabase.h
index 2b462bd..ac5de27 100644
--- a/tools/versioner/src/DeclarationDatabase.h
+++ b/tools/versioner/src/DeclarationDatabase.h
@@ -18,6 +18,7 @@
#include <iostream>
#include <map>
+#include <mutex>
#include <set>
#include <sstream>
#include <string>
@@ -25,27 +26,22 @@
#include <llvm/ADT/StringRef.h>
+#include "Arch.h"
#include "Utils.h"
+namespace clang {
+class ASTUnit;
+class Decl;
+}
+
enum class DeclarationType {
function,
variable,
inconsistent,
};
-static const char* declarationTypeName(DeclarationType type) {
- switch (type) {
- case DeclarationType::function:
- return "function";
- case DeclarationType::variable:
- return "variable";
- case DeclarationType::inconsistent:
- return "inconsistent";
- }
-}
-
struct CompilationType {
- std::string arch;
+ Arch arch;
int api_level;
private:
@@ -61,155 +57,181 @@
bool operator==(const CompilationType& other) const {
return tie() == other.tie();
}
-
- std::string describe() const {
- return arch + "-" + std::to_string(api_level);
- }
};
-struct DeclarationAvailability {
+std::string to_string(const CompilationType& type);
+
+struct AvailabilityValues {
+ bool future = false;
int introduced = 0;
int deprecated = 0;
int obsoleted = 0;
- void dump(std::ostream& out = std::cout) const {
- out << describe();
+ bool empty() const {
+ return !(future || introduced || deprecated || obsoleted);
}
+ bool operator==(const AvailabilityValues& rhs) const {
+ return std::tie(introduced, deprecated, obsoleted) ==
+ std::tie(rhs.introduced, rhs.deprecated, rhs.obsoleted);
+ }
+
+ bool operator!=(const AvailabilityValues& rhs) const {
+ return !(*this == rhs);
+ }
+};
+
+std::string to_string(const AvailabilityValues& av);
+
+struct DeclarationAvailability {
+ AvailabilityValues global_availability;
+ ArchMap<AvailabilityValues> arch_availability;
+
bool empty() const {
- return !(introduced || deprecated || obsoleted);
- }
+ if (!global_availability.empty()) {
+ return false;
+ }
- auto tie() const {
- return std::tie(introduced, deprecated, obsoleted);
+ for (auto it : arch_availability) {
+ if (!it.second.empty()) {
+ return false;
+ }
+ }
+
+ return true;
}
bool operator==(const DeclarationAvailability& rhs) const {
- return this->tie() == rhs.tie();
+ return std::tie(global_availability, arch_availability) ==
+ std::tie(rhs.global_availability, rhs.arch_availability);
}
bool operator!=(const DeclarationAvailability& rhs) const {
return !(*this == rhs);
}
- std::string describe() const {
- if (!(introduced || deprecated || obsoleted)) {
- return "no availability";
- }
-
- std::stringstream out;
- bool need_comma = false;
- auto comma = [&out, &need_comma]() {
- if (!need_comma) {
- need_comma = true;
- return;
- }
- out << ", ";
- };
-
- if (introduced != 0) {
- comma();
- out << "introduced = " << introduced;
- }
- if (deprecated != 0) {
- comma();
- out << "deprecated = " << deprecated;
- }
- if (obsoleted != 0) {
- comma();
- out << "obsoleted = " << obsoleted;
- }
-
- return out.str();
- }
+ // Returns false if the availability declarations conflict.
+ bool merge(const DeclarationAvailability& other);
};
-struct DeclarationLocation {
- std::string filename;
- unsigned line_number;
+std::string to_string(const DeclarationAvailability& decl_av);
+
+struct FileLocation {
+ unsigned line;
unsigned column;
- DeclarationType type;
- bool is_extern;
- bool is_definition;
- DeclarationAvailability availability;
- auto tie() const {
- return std::tie(filename, line_number, column, type, is_extern, is_definition);
+ bool operator<(const FileLocation& rhs) const {
+ return std::tie(line, column) < std::tie(rhs.line, rhs.column);
}
- bool operator<(const DeclarationLocation& other) const {
- return tie() < other.tie();
- }
-
- bool operator==(const DeclarationLocation& other) const {
- return tie() == other.tie();
- }
-
- void dump(const std::string& base_path = "", std::ostream& out = std::cout) const {
- const char* var_type = declarationTypeName(type);
- const char* declaration_type = is_definition ? "definition" : "declaration";
- const char* linkage = is_extern ? "extern" : "static";
-
- std::string stripped_path;
- if (llvm::StringRef(filename).startswith(base_path)) {
- stripped_path = filename.substr(base_path.size());
- } else {
- stripped_path = filename;
- }
-
- out << " " << linkage << " " << var_type << " " << declaration_type << " @ "
- << stripped_path << ":" << line_number << ":" << column;
-
- out << "\t[";
- availability.dump(out);
- out << "]\n";
+ bool operator==(const FileLocation& rhs) const {
+ return std::tie(line, column) == std::tie(rhs.line, rhs.column);
}
};
+struct Location {
+ std::string filename;
+ FileLocation start;
+ FileLocation end;
+
+ bool operator<(const Location& rhs) const {
+ return std::tie(filename, start, end) < std::tie(rhs.filename, rhs.start, rhs.end);
+ }
+};
+
+std::string to_string(const Location& loc);
+
struct Declaration {
- std::string name;
- std::set<DeclarationLocation> locations;
+ Location location;
- bool hasDefinition() const {
- for (const auto& location : locations) {
- if (location.is_definition) {
- return true;
- }
- }
- return false;
+ bool is_extern;
+ bool is_definition;
+ std::map<CompilationType, DeclarationAvailability> availability;
+
+ bool calculateAvailability(DeclarationAvailability* output) const;
+ bool operator<(const Declaration& rhs) const {
+ return location < rhs.location;
}
- DeclarationType type() const {
- DeclarationType result = locations.begin()->type;
- for (const DeclarationLocation& location : locations) {
- if (location.type != result) {
- result = DeclarationType::inconsistent;
- }
- }
- return result;
- }
+ void dump(const std::string& base_path = "", std::ostream& out = std::cout,
+ unsigned indent = 0) const {
+ std::string indent_str(indent, ' ');
+ out << indent_str;
- void dump(const std::string& base_path = "", std::ostream& out = std::cout) const {
- out << " " << name << " declared in " << locations.size() << " locations:\n";
- for (const DeclarationLocation& location : locations) {
- location.dump(base_path, out);
+ if (is_extern) {
+ out << "extern";
+ } else {
+ out << "static";
+ }
+
+ if (is_definition) {
+ out << " definition";
+ } else {
+ out << " declaration";
+ }
+
+ out << " @ " << StripPrefix(location.filename, base_path).str() << ":" << location.start.line
+ << ":" << location.start.column;
+
+ if (!availability.empty()) {
+ DeclarationAvailability avail;
+
+ out << "\n" << indent_str << " ";
+ if (!calculateAvailability(&avail)) {
+ out << "invalid availability";
+ } else {
+ out << to_string(avail);
+ }
}
}
};
-namespace clang {
-class ASTUnit;
-}
+struct Symbol {
+ std::string name;
+ std::map<Location, Declaration> declarations;
-class HeaderDatabase {
- public:
- std::map<std::string, Declaration> declarations;
+ bool calculateAvailability(DeclarationAvailability* output) const;
+ bool hasDeclaration(const CompilationType& type) const;
- void parseAST(clang::ASTUnit* ast);
+ bool operator<(const Symbol& rhs) const {
+ return name < rhs.name;
+ }
+
+ bool operator==(const Symbol& rhs) const {
+ return name == rhs.name;
+ }
void dump(const std::string& base_path = "", std::ostream& out = std::cout) const {
- out << "HeaderDatabase contains " << declarations.size() << " declarations:\n";
- for (const auto& pair : declarations) {
+ DeclarationAvailability availability;
+ bool valid_availability = calculateAvailability(&availability);
+ out << " " << name << ": ";
+
+ if (valid_availability) {
+ out << to_string(availability);
+ } else {
+ out << "invalid";
+ }
+
+ out << "\n";
+
+ for (auto& it : declarations) {
+ it.second.dump(base_path, out, 4);
+ out << "\n";
+ }
+ }
+};
+
+class HeaderDatabase {
+ std::mutex mutex;
+
+ public:
+ std::map<std::string, Symbol> symbols;
+
+ void parseAST(CompilationType type, clang::ASTUnit* ast);
+
+ void dump(const std::string& base_path = "", std::ostream& out = std::cout) const {
+ out << "HeaderDatabase contains " << symbols.size() << " symbols:\n";
+ for (const auto& pair : symbols) {
pair.second.dump(base_path, out);
}
}
diff --git a/tools/versioner/src/SymbolDatabase.cpp b/tools/versioner/src/SymbolDatabase.cpp
index a35f045..b5d2abb 100644
--- a/tools/versioner/src/SymbolDatabase.cpp
+++ b/tools/versioner/src/SymbolDatabase.cpp
@@ -75,7 +75,7 @@
}
std::string path = std::string(platform_dir) + "/android-" + std::to_string(api_level) +
- "/arch-" + type.arch + "/symbols/" + filename;
+ "/arch-" + to_string(type.arch) + "/symbols/" + filename;
stream = std::ifstream(path);
if (stream) {
@@ -86,7 +86,7 @@
}
if (required) {
- errx(1, "failed to find platform file '%s' for %s", filename.c_str(), type.describe().c_str());
+ errx(1, "failed to find platform file '%s' for %s", filename.c_str(), to_string(type).c_str());
}
return std::string();
diff --git a/tools/versioner/src/Utils.cpp b/tools/versioner/src/Utils.cpp
index 92cc9de..8dcadd1 100644
--- a/tools/versioner/src/Utils.cpp
+++ b/tools/versioner/src/Utils.cpp
@@ -21,9 +21,12 @@
#include <string.h>
#include <unistd.h>
+#include <sstream>
#include <string>
#include <vector>
+#include "DeclarationDatabase.h"
+
std::string getWorkingDir() {
char buf[PATH_MAX];
if (!getcwd(buf, sizeof(buf))) {
@@ -54,3 +57,10 @@
fts_close(fts);
return files;
}
+
+llvm::StringRef StripPrefix(llvm::StringRef string, llvm::StringRef prefix) {
+ if (string.startswith(prefix)) {
+ return string.drop_front(prefix.size());
+ }
+ return string;
+}
diff --git a/tools/versioner/src/Utils.h b/tools/versioner/src/Utils.h
index f4845b8..c79c97e 100644
--- a/tools/versioner/src/Utils.h
+++ b/tools/versioner/src/Utils.h
@@ -19,10 +19,11 @@
#include <string>
#include <vector>
+#include <llvm/ADT/StringRef.h>
+
std::string getWorkingDir();
std::vector<std::string> collectFiles(const std::string& directory);
-namespace std {
static __attribute__((unused)) std::string to_string(const char* c) {
return c;
}
@@ -30,13 +31,13 @@
static __attribute__((unused)) const std::string& to_string(const std::string& str) {
return str;
}
-}
template <typename Collection>
static std::string Join(Collection c, const std::string& delimiter = ", ") {
std::string result;
for (const auto& item : c) {
- result.append(std::to_string(item));
+ using std::to_string;
+ result.append(to_string(item));
result.append(delimiter);
}
if (!result.empty()) {
@@ -44,3 +45,5 @@
}
return result;
}
+
+llvm::StringRef StripPrefix(llvm::StringRef string, llvm::StringRef prefix);
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 7238a8c..3ae0a7b 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -37,6 +37,7 @@
#include <clang/Tooling/Tooling.h>
#include <llvm/ADT/StringRef.h>
+#include "Arch.h"
#include "DeclarationDatabase.h"
#include "SymbolDatabase.h"
#include "Utils.h"
@@ -47,6 +48,7 @@
using namespace clang::tooling;
bool verbose;
+static bool add_include;
class HeaderCompilationDatabase : public CompilationDatabase {
CompilationType type;
@@ -75,9 +77,17 @@
command.push_back("-D_FORTIFY_SOURCE=2");
command.push_back("-D_GNU_SOURCE");
command.push_back("-Wno-unknown-attributes");
+ command.push_back("-Wno-pragma-once-outside-header");
command.push_back("-target");
command.push_back(arch_targets[type.arch]);
+ if (add_include) {
+ const char* top = getenv("ANDROID_BUILD_TOP");
+ std::string header_path = to_string(top) + "/bionic/libc/include/android/versioning.h";
+ command.push_back("-include");
+ command.push_back(std::move(header_path));
+ }
+
return CompileCommand(cwd, filename, command);
}
@@ -105,8 +115,7 @@
std::vector<std::string> dependencies;
};
-static CompilationRequirements collectRequirements(const std::string& arch,
- const std::string& header_dir,
+static CompilationRequirements collectRequirements(const Arch& arch, const std::string& header_dir,
const std::string& dependency_dir) {
std::vector<std::string> headers = collectFiles(header_dir);
@@ -143,7 +152,7 @@
};
collect_children(dependency_dir + "/common");
- collect_children(dependency_dir + "/" + arch);
+ collect_children(dependency_dir + "/" + to_string(arch));
}
auto new_end = std::remove_if(headers.begin(), headers.end(), [&arch](llvm::StringRef header) {
@@ -165,10 +174,10 @@
return result;
}
-static std::set<CompilationType> generateCompilationTypes(
- const std::set<std::string> selected_architectures, const std::set<int>& selected_levels) {
+static std::set<CompilationType> generateCompilationTypes(const std::set<Arch> selected_architectures,
+ const std::set<int>& selected_levels) {
std::set<CompilationType> result;
- for (const std::string& arch : selected_architectures) {
+ for (const auto& arch : selected_architectures) {
int min_api = arch_min_api[arch];
for (int api_level : selected_levels) {
if (api_level < min_api) {
@@ -181,31 +190,17 @@
return result;
}
-using DeclarationDatabase = std::map<std::string, std::map<CompilationType, Declaration>>;
-
-static DeclarationDatabase transposeHeaderDatabases(
- const std::map<CompilationType, HeaderDatabase>& original) {
- DeclarationDatabase result;
- for (const auto& outer : original) {
- const CompilationType& type = outer.first;
- for (const auto& inner : outer.second.declarations) {
- const std::string& symbol_name = inner.first;
- result[symbol_name][type] = inner.second;
- }
- }
- return result;
-}
-
-static DeclarationDatabase compileHeaders(const std::set<CompilationType>& types,
- const std::string& header_dir,
- const std::string& dependency_dir, bool* failed) {
+static std::unique_ptr<HeaderDatabase> compileHeaders(const std::set<CompilationType>& types,
+ const std::string& header_dir,
+ const std::string& dependency_dir,
+ bool* failed) {
constexpr size_t thread_count = 8;
size_t threads_created = 0;
std::mutex mutex;
std::vector<std::thread> threads(thread_count);
std::map<CompilationType, HeaderDatabase> header_databases;
- std::unordered_map<std::string, CompilationRequirements> requirements;
+ std::unordered_map<Arch, CompilationRequirements> requirements;
std::string cwd = getWorkingDir();
bool errors = false;
@@ -216,6 +211,7 @@
}
}
+ auto result = std::make_unique<HeaderDatabase>();
for (const auto& type : types) {
size_t thread_id = threads_created++;
if (thread_id >= thread_count) {
@@ -227,7 +223,6 @@
[&](CompilationType type) {
const auto& req = requirements[type.arch];
- HeaderDatabase database;
HeaderCompilationDatabase compilation_database(type, cwd, req.headers, req.dependencies);
ClangTool tool(compilation_database, req.headers);
@@ -240,15 +235,12 @@
if (diagnostics_engine.getNumWarnings() || diagnostics_engine.hasErrorOccurred()) {
std::unique_lock<std::mutex> l(mutex);
errors = true;
- printf("versioner: compilation failure for %s in %s\n", type.describe().c_str(),
+ printf("versioner: compilation failure for %s in %s\n", to_string(type).c_str(),
ast->getOriginalSourceFileName().str().c_str());
}
- database.parseAST(ast.get());
+ result->parseAST(type, ast.get());
}
-
- std::unique_lock<std::mutex> l(mutex);
- header_databases[type] = database;
},
type);
}
@@ -266,83 +258,66 @@
*failed = errors;
}
- return transposeHeaderDatabases(header_databases);
+ return result;
}
-static bool sanityCheck(const std::set<CompilationType>& types,
- const DeclarationDatabase& database) {
+// Perform a sanity check on a symbol's declarations, enforcing the following invariants:
+// 1. At most one inline definition of the function exists.
+// 2. All of the availability declarations for a symbol are compatible.
+// If a function is declared as an inline before a certain version, the inline definition
+// should have no version tag.
+// 3. Each availability type must only be present globally or on a per-arch basis.
+// (e.g. __INTRODUCED_IN_ARM(9) __INTRODUCED_IN_X86(10) __DEPRECATED_IN(11) is fine,
+// but not __INTRODUCED_IN(9) __INTRODUCED_IN_X86(10))
+static bool checkSymbol(const Symbol& symbol) {
+ std::string cwd = getWorkingDir() + "/";
+
+ const Declaration* inline_definition = nullptr;
+ for (const auto& decl_it : symbol.declarations) {
+ const Declaration* decl = &decl_it.second;
+ if (decl->is_definition) {
+ if (inline_definition) {
+ fprintf(stderr, "versioner: multiple definitions of symbol %s\n", symbol.name.c_str());
+ symbol.dump(cwd);
+ inline_definition->dump(cwd);
+ return false;
+ }
+
+ inline_definition = decl;
+ }
+
+ DeclarationAvailability availability;
+ if (!decl->calculateAvailability(&availability)) {
+ fprintf(stderr, "versioner: failed to calculate availability for declaration:\n");
+ decl->dump(cwd, std::cout, 2);
+ return false;
+ }
+
+ if (decl->is_definition && !availability.empty()) {
+ fprintf(stderr, "versioner: inline definition has non-empty versioning information:\n");
+ decl->dump(cwd, std::cout, 2);
+ return false;
+ }
+ }
+
+ DeclarationAvailability availability;
+ if (!symbol.calculateAvailability(&availability)) {
+ fprintf(stderr, "versioner: inconsistent availability for symbol '%s'\n", symbol.name.c_str());
+ symbol.dump(cwd);
+ return false;
+ }
+
+ // TODO: Check invariant #3.
+ return true;
+}
+
+static bool sanityCheck(const HeaderDatabase* database) {
bool error = false;
std::string cwd = getWorkingDir() + "/";
- for (auto outer : database) {
- const std::string& symbol_name = outer.first;
- CompilationType last_type;
- DeclarationAvailability last_availability;
-
- // Rely on std::set being sorted to loop through the types by architecture.
- for (const CompilationType& type : types) {
- auto inner = outer.second.find(type);
- if (inner == outer.second.end()) {
- // TODO: Check for holes.
- continue;
- }
-
- const Declaration& declaration = inner->second;
- bool found_availability = false;
- bool availability_mismatch = false;
- DeclarationAvailability current_availability;
-
- // Ensure that all of the availability declarations for this symbol match.
- for (const DeclarationLocation& location : declaration.locations) {
- if (!found_availability) {
- found_availability = true;
- current_availability = location.availability;
- continue;
- }
-
- if (current_availability != location.availability) {
- availability_mismatch = true;
- error = true;
- }
- }
-
- if (availability_mismatch) {
- printf("%s: availability mismatch for %s\n", symbol_name.c_str(), type.describe().c_str());
- declaration.dump(cwd);
- }
-
- if (type.arch != last_type.arch) {
- last_type = type;
- last_availability = current_availability;
- continue;
- }
-
- // Ensure that availability declarations are consistent across API levels for a given arch.
- if (last_availability != current_availability) {
- error = true;
- printf("%s: availability mismatch between %s and %s: [%s] before, [%s] after\n",
- symbol_name.c_str(), last_type.describe().c_str(), type.describe().c_str(),
- last_availability.describe().c_str(), current_availability.describe().c_str());
- }
-
- // Ensure that at most one inline definition of a function exists.
- std::set<DeclarationLocation> inline_definitions;
-
- for (const DeclarationLocation& location : declaration.locations) {
- if (location.is_definition) {
- inline_definitions.insert(location);
- }
- }
-
- if (inline_definitions.size() > 1) {
- error = true;
- printf("%s: multiple inline definitions found:\n", symbol_name.c_str());
- for (const DeclarationLocation& location : declaration.locations) {
- location.dump(cwd);
- }
- }
-
- last_type = type;
+ for (const auto& symbol_it : database->symbols) {
+ if (!checkSymbol(symbol_it.second)) {
+ error = true;
}
}
return !error;
@@ -351,172 +326,109 @@
// Check that our symbol availability declarations match the actual NDK
// platform symbol availability.
static bool checkVersions(const std::set<CompilationType>& types,
- const DeclarationDatabase& declaration_database,
+ const HeaderDatabase* header_database,
const NdkSymbolDatabase& symbol_database) {
+ std::string cwd = getWorkingDir() + "/";
bool failed = false;
- std::map<std::string, std::set<CompilationType>> arch_types;
+ std::map<Arch, std::set<CompilationType>> arch_types;
for (const CompilationType& type : types) {
arch_types[type.arch].insert(type);
}
std::set<std::string> completely_unavailable;
+ std::map<std::string, std::set<CompilationType>> missing_availability;
+ std::map<std::string, std::set<CompilationType>> extra_availability;
- for (const auto& outer : declaration_database) {
- const std::string& symbol_name = outer.first;
- const auto& compilations = outer.second;
+ for (const auto& symbol_it : header_database->symbols) {
+ const auto& symbol_name = symbol_it.first;
+ DeclarationAvailability symbol_availability;
- auto platform_availability_it = symbol_database.find(symbol_name);
+ if (!symbol_it.second.calculateAvailability(&symbol_availability)) {
+ errx(1, "failed to calculate symbol availability");
+ }
+
+ const auto platform_availability_it = symbol_database.find(symbol_name);
if (platform_availability_it == symbol_database.end()) {
completely_unavailable.insert(symbol_name);
continue;
}
const auto& platform_availability = platform_availability_it->second;
- std::set<CompilationType> missing_symbol;
- std::set<CompilationType> missing_decl;
for (const CompilationType& type : types) {
- auto it = compilations.find(type);
- if (it == compilations.end()) {
- missing_decl.insert(type);
+ bool should_be_available = true;
+ const auto& global_availability = symbol_availability.global_availability;
+ const auto& arch_availability = symbol_availability.arch_availability[type.arch];
+ if (global_availability.introduced != 0 && global_availability.introduced > type.api_level) {
+ should_be_available = false;
+ }
+
+ if (arch_availability.introduced != 0 && arch_availability.introduced > type.api_level) {
+ should_be_available = false;
+ }
+
+ if (global_availability.obsoleted != 0 && global_availability.obsoleted <= type.api_level) {
+ should_be_available = false;
+ }
+
+ if (arch_availability.obsoleted != 0 && arch_availability.obsoleted <= type.api_level) {
+ should_be_available = false;
+ }
+
+ if (arch_availability.future) {
continue;
}
- const Declaration& declaration = it->second;
+ // The function declaration might be (validly) missing for the given CompilationType.
+ if (!symbol_it.second.hasDeclaration(type)) {
+ should_be_available = false;
+ }
- // sanityCheck ensured that the availability declarations for a given arch match.
- DeclarationAvailability availability = declaration.locations.begin()->availability;
- int api_level = type.api_level;
+ bool is_available = platform_availability.count(type);
- int introduced = std::max(0, availability.introduced);
- int obsoleted = availability.obsoleted == 0 ? INT_MAX : availability.obsoleted;
- bool decl_available = api_level >= introduced && api_level < obsoleted;
-
- auto symbol_availability_it = platform_availability.find(type);
- bool symbol_available = symbol_availability_it != platform_availability.end();
- if (decl_available) {
- if (!symbol_available) {
- // Ensure that either it exists in the platform, or an inline definition is visible.
- if (!declaration.hasDefinition()) {
- missing_symbol.insert(type);
- continue;
- }
+ if (should_be_available != is_available) {
+ if (is_available) {
+ extra_availability[symbol_name].insert(type);
} else {
- // Ensure that symbols declared as functions/variables actually are.
- switch (declaration.type()) {
- case DeclarationType::inconsistent:
- printf("%s: inconsistent declaration type\n", symbol_name.c_str());
- declaration.dump();
- exit(1);
-
- case DeclarationType::variable:
- if (symbol_availability_it->second != NdkSymbolType::variable) {
- printf("%s: declared as variable, exists in platform as function\n",
- symbol_name.c_str());
- failed = true;
- }
- break;
-
- case DeclarationType::function:
- if (symbol_availability_it->second != NdkSymbolType::function) {
- printf("%s: declared as function, exists in platform as variable\n",
- symbol_name.c_str());
- failed = true;
- }
- break;
- }
- }
- } else {
- // Ensure that it's not available in the platform.
- if (symbol_availability_it != platform_availability.end()) {
- printf("%s: symbol should be unavailable in %s (declared with availability %s)\n",
- symbol_name.c_str(), type.describe().c_str(), availability.describe().c_str());
- failed = true;
+ missing_availability[symbol_name].insert(type);
}
}
}
+ }
- // Allow declarations to be missing from an entire architecture.
- for (const auto& arch_type : arch_types) {
- const std::string& arch = arch_type.first;
- bool found_all = true;
- for (const auto& type : arch_type.second) {
- if (missing_decl.find(type) == missing_decl.end()) {
- found_all = false;
- break;
- }
- }
+ for (const auto& it : symbol_database) {
+ const std::string& symbol_name = it.first;
- if (!found_all) {
- continue;
- }
-
- for (auto it = missing_decl.begin(); it != missing_decl.end();) {
- if (it->arch == arch) {
- it = missing_decl.erase(it);
- } else {
- ++it;
- }
- }
- }
-
- auto types_to_string = [](const std::set<CompilationType>& types) {
- std::string result;
- for (const CompilationType& type : types) {
- result += type.describe();
- result += ", ";
- }
- result.resize(result.length() - 2);
- return result;
- };
-
- if (!missing_decl.empty()) {
- printf("%s: declaration missing in %s\n", symbol_name.c_str(),
- types_to_string(missing_decl).c_str());
- failed = true;
- }
-
- if (!missing_symbol.empty()) {
+ bool symbol_error = false;
+ auto missing_it = missing_availability.find(symbol_name);
+ if (missing_it != missing_availability.end()) {
printf("%s: declaration marked available but symbol missing in [%s]\n", symbol_name.c_str(),
- types_to_string(missing_symbol).c_str());
+ Join(missing_it->second, ", ").c_str());
+ symbol_error = true;
failed = true;
}
- }
- for (const std::string& symbol_name : completely_unavailable) {
- bool found_inline_definition = false;
- bool future = false;
-
- auto symbol_it = declaration_database.find(symbol_name);
-
- // Ignore inline functions and functions that are tagged as __INTRODUCED_IN_FUTURE.
- // Ensure that all of the declarations of that function satisfy that.
- for (const auto& declaration_pair : symbol_it->second) {
- const Declaration& declaration = declaration_pair.second;
- DeclarationAvailability availability = declaration.locations.begin()->availability;
-
- if (availability.introduced >= 10000) {
- future = true;
- }
-
- if (declaration.hasDefinition()) {
- found_inline_definition = true;
+ if (verbose) {
+ auto extra_it = extra_availability.find(symbol_name);
+ if (extra_it != extra_availability.end()) {
+ printf("%s: declaration marked unavailable but symbol available in [%s]\n",
+ symbol_name.c_str(), Join(extra_it->second, ", ").c_str());
+ symbol_error = true;
+ failed = true;
}
}
- if (future || found_inline_definition) {
- continue;
+ if (symbol_error) {
+ auto symbol_it = header_database->symbols.find(symbol_name);
+ if (symbol_it == header_database->symbols.end()) {
+ errx(1, "failed to find symbol in header database");
+ }
+ symbol_it->second.dump(cwd);
}
-
- if (missing_symbol_whitelist.count(symbol_name) != 0) {
- continue;
- }
-
- printf("%s: not available in any platform\n", symbol_name.c_str());
- failed = true;
}
+ // TODO: Verify that function/variable declarations are actually function/variable symbols.
return !failed;
}
@@ -540,6 +452,7 @@
fprintf(stderr, " -v\t\tenable verbose warnings\n");
fprintf(stderr, "\n");
fprintf(stderr, "Miscellaneous:\n");
+ fprintf(stderr, " -d\t\tdump function availability\n");
fprintf(stderr, " -h\t\tdisplay this message\n");
exit(0);
}
@@ -549,11 +462,12 @@
std::string cwd = getWorkingDir() + "/";
bool default_args = true;
std::string platform_dir;
- std::set<std::string> selected_architectures;
+ std::set<Arch> selected_architectures;
std::set<int> selected_levels;
+ bool dump = false;
int c;
- while ((c = getopt(argc, argv, "a:r:p:vh")) != -1) {
+ while ((c = getopt(argc, argv, "a:r:p:vdhi")) != -1) {
default_args = false;
switch (c) {
case 'a': {
@@ -572,10 +486,8 @@
}
case 'r': {
- if (supported_archs.count(optarg) == 0) {
- errx(1, "unsupported architecture: %s", optarg);
- }
- selected_architectures.insert(optarg);
+ Arch arch = arch_from_string(optarg);
+ selected_architectures.insert(arch);
break;
}
@@ -600,10 +512,19 @@
verbose = true;
break;
+ case 'd':
+ dump = true;
+ break;
+
case 'h':
usage(true);
break;
+ case 'i':
+ // Secret option for tests to -include <android/versioning.h>.
+ add_include = true;
+ break;
+
default:
usage();
break;
@@ -617,15 +538,15 @@
std::string header_dir;
std::string dependency_dir;
+ const char* top = getenv("ANDROID_BUILD_TOP");
+ if (!top && (optind == argc || add_include)) {
+ fprintf(stderr, "versioner: failed to autodetect bionic paths. Is ANDROID_BUILD_TOP set?\n");
+ usage();
+ }
+
if (optind == argc) {
// Neither HEADER_PATH nor DEPS_PATH were specified, so try to figure them out.
- const char* top = getenv("ANDROID_BUILD_TOP");
- if (!top) {
- fprintf(stderr, "versioner: failed to autodetect bionic paths. Is ANDROID_BUILD_TOP set?\n");
- usage();
- }
-
- std::string versioner_dir = std::to_string(top) + "/bionic/tools/versioner";
+ std::string versioner_dir = to_string(top) + "/bionic/tools/versioner";
header_dir = versioner_dir + "/current";
dependency_dir = versioner_dir + "/dependencies";
if (platform_dir.empty()) {
@@ -656,7 +577,6 @@
}
std::set<CompilationType> compilation_types;
- DeclarationDatabase declaration_database;
NdkSymbolDatabase symbol_database;
compilation_types = generateCompilationTypes(selected_architectures, selected_levels);
@@ -668,19 +588,23 @@
}
bool failed = false;
- declaration_database = compileHeaders(compilation_types, header_dir, dependency_dir, &failed);
+ std::unique_ptr<HeaderDatabase> declaration_database =
+ compileHeaders(compilation_types, header_dir, dependency_dir, &failed);
- if (!sanityCheck(compilation_types, declaration_database)) {
- printf("versioner: sanity check failed\n");
- failed = true;
- }
-
- if (!platform_dir.empty()) {
- if (!checkVersions(compilation_types, declaration_database, symbol_database)) {
- printf("versioner: version check failed\n");
+ if (dump) {
+ declaration_database->dump(header_dir + "/");
+ } else {
+ if (!sanityCheck(declaration_database.get())) {
+ printf("versioner: sanity check failed\n");
failed = true;
}
- }
+ if (!platform_dir.empty()) {
+ if (!checkVersions(compilation_types, declaration_database.get(), symbol_database)) {
+ printf("versioner: version check failed\n");
+ failed = true;
+ }
+ }
+ }
return failed;
}
diff --git a/tools/versioner/src/versioner.h b/tools/versioner/src/versioner.h
index ced9b79..33d8084 100644
--- a/tools/versioner/src/versioner.h
+++ b/tools/versioner/src/versioner.h
@@ -24,37 +24,12 @@
extern bool verbose;
-static const std::set<std::string> supported_archs = {
- "arm", "arm64", "mips", "mips64", "x86", "x86_64",
-};
-
-static std::unordered_map<std::string, std::string> arch_targets = {
- { "arm", "arm-linux-androideabi" },
- { "arm64", "aarch64-linux-android" },
- { "mips", "mipsel-linux-android" },
- { "mips64", "mips64el-linux-android" },
- { "x86", "i686-linux-android" },
- { "x86_64", "x86_64-linux-android" },
-};
-
-static const std::set<int> supported_levels = { 9, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 24 };
-
-// Non-const for the convenience of being able to index with operator[].
-static std::map<std::string, int> arch_min_api = {
- { "arm", 9 },
- { "arm64", 21 },
- { "mips", 9 },
- { "mips64", 21 },
- { "x86", 9 },
- { "x86_64", 21 },
-};
-
-static const std::unordered_map<std::string, std::set<std::string>> header_blacklist = {
+static const std::unordered_map<std::string, std::set<Arch>> header_blacklist = {
// Internal header.
{ "sys/_system_properties.h", supported_archs },
// time64.h #errors when included on LP64 archs.
- { "time64.h", { "arm64", "mips64", "x86_64" } },
+ { "time64.h", { Arch::arm64, Arch::mips64, Arch::x86_64 } },
};
static const std::unordered_set<std::string> missing_symbol_whitelist = {
diff --git a/tools/versioner/tests/arch_specific/run.sh b/tools/versioner/tests/arch_specific/run.sh
index 6d97fb0..f0d95ae 100644
--- a/tools/versioner/tests/arch_specific/run.sh
+++ b/tools/versioner/tests/arch_specific/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -r x86 -a 9
+versioner headers -p platforms -r arm -r x86 -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/compilation_error/run.sh b/tools/versioner/tests/compilation_error/run.sh
index 8babb73..a34fda8 100644
--- a/tools/versioner/tests/compilation_error/run.sh
+++ b/tools/versioner/tests/compilation_error/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9
+versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/dependencies/run.sh b/tools/versioner/tests/dependencies/run.sh
index 3a3dda8..0c17907 100644
--- a/tools/versioner/tests/dependencies/run.sh
+++ b/tools/versioner/tests/dependencies/run.sh
@@ -1 +1 @@
-versioner headers dependencies -p platforms -r arm -r x86 -a 9
+versioner headers dependencies -p platforms -r arm -r x86 -a 9
\ No newline at end of file
diff --git a/tools/versioner/tests/errordecl/headers/foo.h b/tools/versioner/tests/errordecl/headers/foo.h
deleted file mode 100644
index c466420..0000000
--- a/tools/versioner/tests/errordecl/headers/foo.h
+++ /dev/null
@@ -1 +0,0 @@
-int foo() __attribute__((unavailable));
diff --git a/tools/versioner/tests/errordecl/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/errordecl/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/errordecl/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/errordecl/run.sh b/tools/versioner/tests/errordecl/run.sh
deleted file mode 100644
index 0dea98f..0000000
--- a/tools/versioner/tests/errordecl/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-versioner -v headers -p platforms -r arm -a 9
diff --git a/tools/versioner/tests/future/headers/foo.h b/tools/versioner/tests/future/headers/foo.h
index b5113f4..54e8f0c 100644
--- a/tools/versioner/tests/future/headers/foo.h
+++ b/tools/versioner/tests/future/headers/foo.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, introduced = 10000)));
+int foo() __INTRODUCED_IN_FUTURE;
diff --git a/tools/versioner/tests/future/run.sh b/tools/versioner/tests/future/run.sh
index 0dea98f..041b047 100644
--- a/tools/versioner/tests/future/run.sh
+++ b/tools/versioner/tests/future/run.sh
@@ -1 +1 @@
-versioner -v headers -p platforms -r arm -a 9
+versioner -v headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/future_arch/headers/foo.h b/tools/versioner/tests/future_arch/headers/foo.h
index 6740975..9dd976e 100644
--- a/tools/versioner/tests/future_arch/headers/foo.h
+++ b/tools/versioner/tests/future_arch/headers/foo.h
@@ -1,5 +1,5 @@
#if defined(__arm__)
-int foo() __attribute__((availability(android, introduced = 9)));
+int foo() __INTRODUCED_IN(9);
#else
-int foo() __attribute__((availability(android, introduced = 10000)));
+int foo() __INTRODUCED_IN_FUTURE;
#endif
diff --git a/tools/versioner/tests/future_arch/run.sh b/tools/versioner/tests/future_arch/run.sh
index 36846da..ad8f430 100644
--- a/tools/versioner/tests/future_arch/run.sh
+++ b/tools/versioner/tests/future_arch/run.sh
@@ -1 +1 @@
-versioner -v headers -p platforms -r arm -r x86 -a 9
+versioner -v headers -p platforms -r arm -r x86 -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/inline/headers/foo.h b/tools/versioner/tests/inline/headers/foo.h
index 7a48a72..a61b386 100644
--- a/tools/versioner/tests/inline/headers/foo.h
+++ b/tools/versioner/tests/inline/headers/foo.h
@@ -1,7 +1,7 @@
-#if __ANDROID_API__ <= 9
-static int foo() __attribute__((availability(android, introduced = 9))) {
+#if __ANDROID_API__ < 12
+static int foo() {
return 0;
}
#else
-int foo() __attribute__((availability(android, introduced = 9)));
+int foo() __INTRODUCED_IN(12);
#endif
diff --git a/tools/versioner/tests/inline/run.sh b/tools/versioner/tests/inline/run.sh
index 914c55d..9bfbe6d 100644
--- a/tools/versioner/tests/inline/run.sh
+++ b/tools/versioner/tests/inline/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9 -a 12
+versioner headers -p platforms -r arm -a 9 -a 12 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/inline_unavailable/headers/foo.h b/tools/versioner/tests/inline_unavailable/headers/foo.h
deleted file mode 100644
index 6800dd0..0000000
--- a/tools/versioner/tests/inline_unavailable/headers/foo.h
+++ /dev/null
@@ -1,3 +0,0 @@
-static int foo() __attribute__((availability(android, introduced = 9))) {
- return 0;
-}
diff --git a/tools/versioner/tests/inline_unavailable/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/inline_unavailable/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/inline_unavailable/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/inline_unavailable/run.sh b/tools/versioner/tests/inline_unavailable/run.sh
deleted file mode 100644
index 0dea98f..0000000
--- a/tools/versioner/tests/inline_unavailable/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-versioner -v headers -p platforms -r arm -a 9
diff --git a/tools/versioner/tests/inline_version_mismatch/expected_fail b/tools/versioner/tests/inline_version_mismatch/expected_fail
deleted file mode 100644
index 7f0709c..0000000
--- a/tools/versioner/tests/inline_version_mismatch/expected_fail
+++ /dev/null
@@ -1,2 +0,0 @@
-foo: availability mismatch between arm-9 and arm-12: [introduced = 9] before, [introduced = 10] after
-versioner: sanity check failed
diff --git a/tools/versioner/tests/inline_version_mismatch/headers/foo.h b/tools/versioner/tests/inline_version_mismatch/headers/foo.h
deleted file mode 100644
index be7cc2c..0000000
--- a/tools/versioner/tests/inline_version_mismatch/headers/foo.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#if __ANDROID_API__ <= 9
-static int foo() __attribute__((availability(android, introduced = 9))) {
- return 0;
-}
-#else
-int foo() __attribute__((availability(android, introduced = 10)));
-#endif
diff --git a/tools/versioner/tests/inline_version_mismatch/platforms/android-12/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/inline_version_mismatch/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/inline_version_mismatch/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/inline_version_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/inline_version_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/inline_version_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/inline_version_mismatch/run.sh b/tools/versioner/tests/inline_version_mismatch/run.sh
deleted file mode 100644
index 914c55d..0000000
--- a/tools/versioner/tests/inline_version_mismatch/run.sh
+++ /dev/null
@@ -1 +0,0 @@
-versioner headers -p platforms -r arm -a 9 -a 12
diff --git a/tools/versioner/tests/missing_api/expected_fail b/tools/versioner/tests/missing_api/expected_fail
index 85c07f5..65a25f2 100644
--- a/tools/versioner/tests/missing_api/expected_fail
+++ b/tools/versioner/tests/missing_api/expected_fail
@@ -1,2 +1,5 @@
foo: declaration marked available but symbol missing in [arm-12]
+ foo: introduced = 9
+ extern declaration @ headers/foo.h:1:1
+ introduced = 9
versioner: version check failed
diff --git a/tools/versioner/tests/missing_api/headers/foo.h b/tools/versioner/tests/missing_api/headers/foo.h
index 2998c8e..3ff3ff7 100644
--- a/tools/versioner/tests/missing_api/headers/foo.h
+++ b/tools/versioner/tests/missing_api/headers/foo.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, introduced = 9)));
+int foo() __INTRODUCED_IN(9);
\ No newline at end of file
diff --git a/tools/versioner/tests/missing_api/run.sh b/tools/versioner/tests/missing_api/run.sh
index 914c55d..9bfbe6d 100644
--- a/tools/versioner/tests/missing_api/run.sh
+++ b/tools/versioner/tests/missing_api/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9 -a 12
+versioner headers -p platforms -r arm -a 9 -a 12 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/missing_arch/expected_fail b/tools/versioner/tests/missing_arch/expected_fail
index 56de9d9..ed8ab79 100644
--- a/tools/versioner/tests/missing_arch/expected_fail
+++ b/tools/versioner/tests/missing_arch/expected_fail
@@ -1,2 +1,5 @@
foo: declaration marked available but symbol missing in [x86-9]
+ foo: no availability
+ extern declaration @ headers/foo.h:1:1
+ no availability
versioner: version check failed
diff --git a/tools/versioner/tests/missing_arch/headers/foo.h b/tools/versioner/tests/missing_arch/headers/foo.h
index 5d5f8f0..176e7a3 100644
--- a/tools/versioner/tests/missing_arch/headers/foo.h
+++ b/tools/versioner/tests/missing_arch/headers/foo.h
@@ -1 +1 @@
-int foo();
+int foo();
\ No newline at end of file
diff --git a/tools/versioner/tests/missing_arch/run.sh b/tools/versioner/tests/missing_arch/run.sh
index 6d97fb0..f0d95ae 100644
--- a/tools/versioner/tests/missing_arch/run.sh
+++ b/tools/versioner/tests/missing_arch/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -r x86 -a 9
+versioner headers -p platforms -r arm -r x86 -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_decl/headers/bar.h b/tools/versioner/tests/multiple_decl/headers/bar.h
index 95f0174..1d3a28c 100644
--- a/tools/versioner/tests/multiple_decl/headers/bar.h
+++ b/tools/versioner/tests/multiple_decl/headers/bar.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, obsoleted = 12)));
+int foo() __REMOVED_IN(12);
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_decl/headers/foo.h b/tools/versioner/tests/multiple_decl/headers/foo.h
index 95f0174..1d3a28c 100644
--- a/tools/versioner/tests/multiple_decl/headers/foo.h
+++ b/tools/versioner/tests/multiple_decl/headers/foo.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, obsoleted = 12)));
+int foo() __REMOVED_IN(12);
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_decl/run.sh b/tools/versioner/tests/multiple_decl/run.sh
index 8babb73..a34fda8 100644
--- a/tools/versioner/tests/multiple_decl/run.sh
+++ b/tools/versioner/tests/multiple_decl/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9
+versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_decl_mismatch/expected_fail b/tools/versioner/tests/multiple_decl_mismatch/expected_fail
index 6d0d209..8e8c846 100644
--- a/tools/versioner/tests/multiple_decl_mismatch/expected_fail
+++ b/tools/versioner/tests/multiple_decl_mismatch/expected_fail
@@ -1,5 +1,8 @@
-foo: availability mismatch for arm-9
- foo declared in 2 locations:
- extern function declaration @ headers/bar.h:1:5 [obsoleted = 12]
- extern function declaration @ headers/foo.h:1:5 [obsoleted = 9]
+versioner: inconsistent availability for symbol 'foo'
+versioner: failed to calculate symbol availability
+ foo: invalid
+ extern declaration @ headers/bar.h:1:1
+ obsoleted = 12
+ extern declaration @ headers/foo.h:1:1
+ obsoleted = 9
versioner: sanity check failed
diff --git a/tools/versioner/tests/multiple_decl_mismatch/headers/bar.h b/tools/versioner/tests/multiple_decl_mismatch/headers/bar.h
index 95f0174..1d3a28c 100644
--- a/tools/versioner/tests/multiple_decl_mismatch/headers/bar.h
+++ b/tools/versioner/tests/multiple_decl_mismatch/headers/bar.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, obsoleted = 12)));
+int foo() __REMOVED_IN(12);
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_decl_mismatch/headers/foo.h b/tools/versioner/tests/multiple_decl_mismatch/headers/foo.h
index 9c81a89..49a73ec 100644
--- a/tools/versioner/tests/multiple_decl_mismatch/headers/foo.h
+++ b/tools/versioner/tests/multiple_decl_mismatch/headers/foo.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, obsoleted = 9)));
+int foo() __REMOVED_IN(9);
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_decl_mismatch/run.sh b/tools/versioner/tests/multiple_decl_mismatch/run.sh
index 8babb73..a34fda8 100644
--- a/tools/versioner/tests/multiple_decl_mismatch/run.sh
+++ b/tools/versioner/tests/multiple_decl_mismatch/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9
+versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/obsoleted/headers/foo.h b/tools/versioner/tests/obsoleted/headers/foo.h
index c190629..68f3d43 100644
--- a/tools/versioner/tests/obsoleted/headers/foo.h
+++ b/tools/versioner/tests/obsoleted/headers/foo.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, introduced = 9, obsoleted = 11)));
+int foo() __INTRODUCED_IN(9) __REMOVED_IN(11);
\ No newline at end of file
diff --git a/tools/versioner/tests/obsoleted/run.sh b/tools/versioner/tests/obsoleted/run.sh
index 914c55d..9bfbe6d 100644
--- a/tools/versioner/tests/obsoleted/run.sh
+++ b/tools/versioner/tests/obsoleted/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9 -a 12
+versioner headers -p platforms -r arm -a 9 -a 12 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/smoke/headers/foo.h b/tools/versioner/tests/smoke/headers/foo.h
index 2998c8e..3ff3ff7 100644
--- a/tools/versioner/tests/smoke/headers/foo.h
+++ b/tools/versioner/tests/smoke/headers/foo.h
@@ -1 +1 @@
-int foo() __attribute__((availability(android, introduced = 9)));
+int foo() __INTRODUCED_IN(9);
\ No newline at end of file
diff --git a/tools/versioner/tests/smoke/run.sh b/tools/versioner/tests/smoke/run.sh
index 8babb73..a34fda8 100644
--- a/tools/versioner/tests/smoke/run.sh
+++ b/tools/versioner/tests/smoke/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9
+versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/version_mismatch/expected_fail b/tools/versioner/tests/version_mismatch/expected_fail
index 7f0709c..f83f71c 100644
--- a/tools/versioner/tests/version_mismatch/expected_fail
+++ b/tools/versioner/tests/version_mismatch/expected_fail
@@ -1,2 +1,8 @@
-foo: availability mismatch between arm-9 and arm-12: [introduced = 9] before, [introduced = 10] after
+versioner: inconsistent availability for symbol 'foo'
+versioner: failed to calculate symbol availability
+ foo: invalid
+ extern declaration @ headers/foo.h:2:1
+ introduced = 9
+ extern declaration @ headers/foo.h:4:1
+ introduced = 10
versioner: sanity check failed
diff --git a/tools/versioner/tests/version_mismatch/headers/foo.h b/tools/versioner/tests/version_mismatch/headers/foo.h
index 4d23417..4604092 100644
--- a/tools/versioner/tests/version_mismatch/headers/foo.h
+++ b/tools/versioner/tests/version_mismatch/headers/foo.h
@@ -1,5 +1,5 @@
#if __ANDROID_API__ <= 9
-int foo() __attribute__((availability(android, introduced = 9)));
+int foo() __INTRODUCED_IN(9);
#else
-int foo() __attribute__((availability(android, introduced = 10)));
+int foo() __INTRODUCED_IN(10);
#endif
diff --git a/tools/versioner/tests/version_mismatch/run.sh b/tools/versioner/tests/version_mismatch/run.sh
index 914c55d..9bfbe6d 100644
--- a/tools/versioner/tests/version_mismatch/run.sh
+++ b/tools/versioner/tests/version_mismatch/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9 -a 12
+versioner headers -p platforms -r arm -a 9 -a 12 -i
\ No newline at end of file