Merge "Added header-abi-diff." am: 4f4b426e06 am: 8800a6b8d9 am: 649fbd4ce4
am: eb9d98f889
Change-Id: I1a8054aeb53414f729711de05fea85d113131235
This commit is contained in:
@@ -98,6 +98,7 @@ cc_library_static {
|
||||
|
||||
srcs: [
|
||||
"proto/abi_dump.proto",
|
||||
"proto/abi_diff.proto",
|
||||
],
|
||||
|
||||
proto: {
|
||||
@@ -174,3 +175,35 @@ cc_binary_host {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_binary_host {
|
||||
name: "header-abi-diff",
|
||||
|
||||
defaults: [
|
||||
"header-checker-defaults",
|
||||
"header-abi-linker-lib-defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"header-abi-diff/src/*.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libheader-checker-proto",
|
||||
],
|
||||
|
||||
target: {
|
||||
linux: {
|
||||
host_ldlibs: [
|
||||
"-ldl",
|
||||
"-lpthread",
|
||||
],
|
||||
},
|
||||
darwin: {
|
||||
host_ldlibs: [
|
||||
"-ldl",
|
||||
"-lpthread",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,12 +1,33 @@
|
||||
# VNDK Header Checker
|
||||
# VNDK Header Abi Dumper
|
||||
|
||||
`header-checker` is a tool to check for ABI compliance. First, we can create a
|
||||
reference dump for each header file when we are preparing a formal release.
|
||||
After the release, we can check the ABI compliance by comparing the information
|
||||
in the reference dump and the latest header.
|
||||
`header-abi-dumper` is a tool to dump the abi of a source. The Abi dumped
|
||||
belonging to a source file is filtered by dumping only the Abi contained in a
|
||||
set of header files exposed through the "export_include_dirs" directory(ies).
|
||||
|
||||
## Usage
|
||||
header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I
|
||||
<export-include-dir-2>.. -- <cflags>
|
||||
|
||||
Example 1:
|
||||
# VNDK Header Abi Linker
|
||||
|
||||
`header-abi-linker` is a tool to link abi dumps produced by header-abi-dumper.
|
||||
This tool combines all the abi information present in the dump files passed to
|
||||
it.
|
||||
|
||||
## Usage
|
||||
header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ...
|
||||
|
||||
# VNDK Header Abi Diff
|
||||
|
||||
`header-abi-diff` is a tool which compares two header abi dumps produced by
|
||||
header-abi-dumper. It produces a report outlining all the differences in the
|
||||
abi's exposed by the two dumps.
|
||||
|
||||
# Return Value
|
||||
1: InCompatible
|
||||
0: Compatible or Compatible Extension.
|
||||
|
||||
|
||||
## Usage
|
||||
header-abi-diff -old <old-abi-dump> -new <new-abi-dump> -o <report>
|
||||
|
||||
$ header-checker -g -r example1.ast tests/example1.h -- clang -x c++
|
||||
|
||||
232
vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp
Normal file
232
vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright (C) 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 "abi_diff.h"
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
Status HeaderAbiDiff::GenerateCompatibilityReport() {
|
||||
abi_dump::TranslationUnit old_tu;
|
||||
abi_dump::TranslationUnit new_tu;
|
||||
std::ifstream old_input(old_dump_);
|
||||
std::ifstream new_input(new_dump_);
|
||||
google::protobuf::io::IstreamInputStream text_iso(&old_input);
|
||||
google::protobuf::io::IstreamInputStream text_isn(&new_input);
|
||||
|
||||
if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) ||
|
||||
!google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) {
|
||||
llvm::errs() << "Failed to Parse Input\n";
|
||||
::exit(1);
|
||||
}
|
||||
return CompareTUs(old_tu, new_tu);
|
||||
}
|
||||
|
||||
Status HeaderAbiDiff::CompareTUs(const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu) {
|
||||
abi_diff::TranslationUnitDiff diff_tu;
|
||||
Status record_Status = CollectRecords(&diff_tu, old_tu, new_tu);
|
||||
Status function_Status = CollectFunctions(&diff_tu, old_tu, new_tu);
|
||||
Status enum_Status = CollectEnums(&diff_tu, old_tu, new_tu);
|
||||
|
||||
Status combined_Status = record_Status | function_Status | enum_Status;
|
||||
|
||||
std::ofstream text_output(cr_);
|
||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||
|
||||
if(!google::protobuf::TextFormat::Print(diff_tu, &text_os)) {
|
||||
llvm::errs() << "Unable to dump report\n";
|
||||
::exit(1);
|
||||
}
|
||||
if (combined_Status & INCOMPATIBLE) {
|
||||
return INCOMPATIBLE;
|
||||
}
|
||||
if (combined_Status & EXTENSION) {
|
||||
return EXTENSION;
|
||||
}
|
||||
return COMPATIBLE;
|
||||
}
|
||||
|
||||
Status HeaderAbiDiff::CollectRecords(abi_diff::TranslationUnitDiff *diff_tu,
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu) {
|
||||
AddToMap(&old_dump_records_, old_tu.records());
|
||||
AddToMap(&new_dump_records_, new_tu.records());
|
||||
|
||||
if (!PopulateRemovedElements(diff_tu->mutable_records_removed(),
|
||||
old_dump_records_, new_dump_records_) ||
|
||||
!PopulateRemovedElements(diff_tu->mutable_records_removed(),
|
||||
new_dump_records_, old_dump_records_) ||
|
||||
!PopulateCommonElements(diff_tu->mutable_records_diff(),old_dump_records_,
|
||||
new_dump_records_)) {
|
||||
llvm::errs() << "Populating records in report failed\n";
|
||||
::exit(1);
|
||||
}
|
||||
if (diff_tu->records_diff().size() || diff_tu->records_removed().size()) {
|
||||
return INCOMPATIBLE;
|
||||
}
|
||||
if (diff_tu->records_added().size()) {
|
||||
return EXTENSION;
|
||||
}
|
||||
return COMPATIBLE;
|
||||
}
|
||||
|
||||
Status HeaderAbiDiff::CollectFunctions(
|
||||
abi_diff::TranslationUnitDiff *diff_tu,
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu) {
|
||||
AddToMap(&old_dump_functions_, old_tu.functions());
|
||||
AddToMap(&new_dump_functions_, new_tu.functions());
|
||||
|
||||
if (!PopulateRemovedElements(diff_tu->mutable_functions_removed(),
|
||||
old_dump_functions_, new_dump_functions_) ||
|
||||
!PopulateRemovedElements(diff_tu->mutable_functions_added(),
|
||||
new_dump_functions_, old_dump_functions_)) {
|
||||
llvm::errs() << "Populating functions in report failed\n";
|
||||
::exit(1);
|
||||
}
|
||||
if (diff_tu->functions_removed().size()) {
|
||||
return INCOMPATIBLE;
|
||||
}
|
||||
if (diff_tu->functions_added().size()) {
|
||||
return EXTENSION;
|
||||
}
|
||||
return COMPATIBLE;
|
||||
}
|
||||
|
||||
Status HeaderAbiDiff::CollectEnums(abi_diff::TranslationUnitDiff *diff_tu,
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu) {
|
||||
AddToMap(&old_dump_enums_, old_tu.enums());
|
||||
AddToMap(&new_dump_enums_, new_tu.enums());
|
||||
|
||||
if (!PopulateRemovedElements(diff_tu->mutable_enums_removed(),
|
||||
old_dump_enums_, new_dump_enums_) ||
|
||||
!PopulateRemovedElements(diff_tu->mutable_enums_added(), new_dump_enums_,
|
||||
old_dump_enums_) ||
|
||||
!PopulateCommonElements(diff_tu->mutable_enums_diff(),old_dump_enums_,
|
||||
new_dump_enums_)) {
|
||||
llvm::errs() << "Populating enums in report failed\n";
|
||||
::exit(1);
|
||||
}
|
||||
if (diff_tu->enums_removed().size() || diff_tu->enums_diff().size()) {
|
||||
return INCOMPATIBLE;
|
||||
}
|
||||
if (diff_tu->enums_added().size()) {
|
||||
return EXTENSION;
|
||||
}
|
||||
return COMPATIBLE;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::PopulateRemovedElements(
|
||||
google::protobuf::RepeatedPtrField<T> *dst,
|
||||
const std::map<std::string, const T*> &old_elements_map,
|
||||
const std::map<std::string, const T*> &new_elements_map) const {
|
||||
|
||||
std::vector<const T *> removed_elements;
|
||||
for (auto &&map_element : old_elements_map) {
|
||||
const T *element = map_element.second;
|
||||
auto new_element = new_elements_map.find(element->linker_set_key());
|
||||
if (new_element == new_elements_map.end()) {
|
||||
removed_elements.emplace_back(element);
|
||||
}
|
||||
}
|
||||
if (!DumpLoneElements(dst, removed_elements)) {
|
||||
llvm::errs() << "Dumping added / removed element to report failed\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
bool HeaderAbiDiff::PopulateCommonElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map) const {
|
||||
std::vector<std::pair<const T *, const T *>> common_elements;
|
||||
typename std::map<std::string, const T *>::const_iterator old_element =
|
||||
old_elements_map.begin();
|
||||
typename std::map<std::string, const T *>::const_iterator new_element =
|
||||
new_elements_map.begin();
|
||||
while (old_element != old_elements_map.end() &&
|
||||
new_element != new_elements_map.end()) {
|
||||
if (old_element->first == new_element->first) {
|
||||
common_elements.emplace_back(std::make_pair(
|
||||
old_element->second, new_element->second));
|
||||
old_element++;
|
||||
new_element++;
|
||||
continue;
|
||||
}
|
||||
if (old_element->first < new_element->first) {
|
||||
old_element++;
|
||||
} else {
|
||||
new_element++;
|
||||
}
|
||||
}
|
||||
if (!DumpDiffElements(dst, common_elements)) {
|
||||
llvm::errs() << "Dumping difference in common element to report failed\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::DumpLoneElements(google::protobuf::RepeatedPtrField<T> *dst,
|
||||
std::vector<const T *> &elements) const {
|
||||
for (auto &&element : elements) {
|
||||
T *added_element = dst->Add();
|
||||
if (!added_element) {
|
||||
llvm::errs() << "Adding element diff failed\n";
|
||||
return false;
|
||||
}
|
||||
*added_element = *element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
bool HeaderAbiDiff::DumpDiffElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
std::vector<std::pair<const T *,const T *>> &pairs) const {
|
||||
for (auto &&pair : pairs) {
|
||||
const T *old_element = pair.first;
|
||||
const T *new_element = pair.second;
|
||||
// Not having inheritance from protobuf messages makes this
|
||||
// restrictive code.
|
||||
abi_diff_wrappers::DiffWrapper<T, TDiff> diff_wrapper(old_element,
|
||||
new_element);
|
||||
std::unique_ptr<TDiff> decl_diff_ptr = diff_wrapper.Get();
|
||||
if (!decl_diff_ptr) {
|
||||
continue;
|
||||
}
|
||||
TDiff *added_element_diff = dst->Add();
|
||||
if (!added_element_diff) {
|
||||
llvm::errs() << "Adding element diff failed\n";
|
||||
return false;
|
||||
}
|
||||
*added_element_diff = *decl_diff_ptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
125
vndk/tools/header-checker/header-abi-diff/src/abi_diff.h
Normal file
125
vndk/tools/header-checker/header-abi-diff/src/abi_diff.h
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (C) 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 "abi_diff_wrappers.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class HeaderAbiDiff {
|
||||
public:
|
||||
enum Status {
|
||||
COMPATIBLE = 1 << 0,
|
||||
EXTENSION = 1 << 1,
|
||||
INCOMPATIBLE = 1 << 2,
|
||||
};
|
||||
|
||||
|
||||
HeaderAbiDiff(const std::string &old_dump, const std::string &new_dump,
|
||||
const std::string &compatibility_report)
|
||||
: old_dump_(old_dump), new_dump_(new_dump), cr_(compatibility_report) { }
|
||||
|
||||
Status GenerateCompatibilityReport();
|
||||
|
||||
private:
|
||||
Status CompareTUs(const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu);
|
||||
// Collect* methods fill in the diff_tu.
|
||||
Status CollectRecords(abi_diff::TranslationUnitDiff *abi_diff,
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu);
|
||||
|
||||
Status CollectFunctions(abi_diff::TranslationUnitDiff *abi_diff,
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu);
|
||||
|
||||
Status CollectEnums(abi_diff::TranslationUnitDiff *abi_diff,
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu);
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline void AddToMap(std::map<std::string, const T *> *dst,
|
||||
const google::protobuf::RepeatedPtrField<T> &src);
|
||||
|
||||
template <typename T>
|
||||
bool PopulateRemovedElements(
|
||||
google::protobuf::RepeatedPtrField<T> *dst,
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map) const;
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
bool PopulateCommonElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map) const;
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
bool DumpDiffElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
std::vector<std::pair<const T *, const T *>> &pairs) const;
|
||||
|
||||
template <typename T>
|
||||
bool DumpLoneElements(google::protobuf::RepeatedPtrField<T> *dst,
|
||||
std::vector<const T *> &elements) const;
|
||||
|
||||
private:
|
||||
const std::string &old_dump_;
|
||||
const std::string &new_dump_;
|
||||
const std::string &cr_;
|
||||
|
||||
// HashMaps for the old tu abis
|
||||
std::map<std::string, const abi_dump::RecordDecl *> old_dump_records_;
|
||||
std::map<std::string, const abi_dump::FunctionDecl *> old_dump_functions_;
|
||||
std::map<std::string, const abi_dump::EnumDecl *> old_dump_enums_;
|
||||
|
||||
// HashMaps for the new tu abis
|
||||
std::map<std::string, const abi_dump::RecordDecl *> new_dump_records_;
|
||||
std::map<std::string, const abi_dump::FunctionDecl *> new_dump_functions_;
|
||||
std::map<std::string, const abi_dump::EnumDecl *> new_dump_enums_;
|
||||
};
|
||||
|
||||
typedef HeaderAbiDiff::Status Status;
|
||||
|
||||
template <typename T>
|
||||
inline void HeaderAbiDiff::AddToMap(
|
||||
std::map<std::string, const T *> *dst,
|
||||
const google::protobuf::RepeatedPtrField<T> &src) {
|
||||
for (auto &&element : src) {
|
||||
dst->insert(std::make_pair(element.linker_set_key(), &element));
|
||||
}
|
||||
}
|
||||
|
||||
static inline Status operator|(Status f, Status s) {
|
||||
return static_cast<Status>(
|
||||
static_cast<std::underlying_type<Status>::type>(f) |
|
||||
static_cast<std::underlying_type<Status>::type>(s));
|
||||
}
|
||||
|
||||
static inline Status operator&(Status f, Status s) {
|
||||
return static_cast<Status>(
|
||||
static_cast<std::underlying_type<Status>::type>(f) &
|
||||
static_cast<std::underlying_type<Status>::type>(s));
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright (C) 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 "abi_diff_wrappers.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
using abi_diff::RecordDeclDiff;
|
||||
using abi_diff::FieldDeclDiff;
|
||||
using abi_diff::CXXBaseSpecifierDiff;
|
||||
using abi_diff::EnumDeclDiff;
|
||||
using abi_diff::EnumFieldDeclDiff;
|
||||
using abi_dump::RecordDecl;
|
||||
using abi_dump::EnumDecl;
|
||||
|
||||
namespace abi_diff_wrappers {
|
||||
|
||||
// This function fills in a *Diff Message's repeated field. For eg:
|
||||
// RecordDeclDiff's CXXBaseSpecifierDiff fields and well as FieldDeclDiff
|
||||
// fields.
|
||||
template <typename T, typename TDiff>
|
||||
template <typename Element, typename ElementDiff>
|
||||
bool DiffWrapperBase<T, TDiff>::GetElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
|
||||
bool differs = false;
|
||||
assert (dst != nullptr);
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (i < old_elements.size() && j < new_elements.size()) {
|
||||
const Element &old_element = old_elements.Get(i);
|
||||
const Element &new_element = new_elements.Get(i);
|
||||
if (old_element.linker_set_key() != new_element.linker_set_key()) {
|
||||
ElementDiff *diff = dst->Add();
|
||||
Element *old_elementp = nullptr;
|
||||
Element *new_elementp = nullptr;
|
||||
if (!diff || !(old_elementp = diff->mutable_old()) ||
|
||||
!(new_elementp = diff->mutable_new_())) {
|
||||
llvm::errs() << "Failed to add diff element\n";
|
||||
::exit(1);
|
||||
}
|
||||
*old_elementp = old_element;
|
||||
*new_elementp = new_element;
|
||||
diff->set_index(i);
|
||||
differs = true;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
if (old_elements.size() != new_elements.size()) {
|
||||
GetExtraElementDiffs(dst, i, j, old_elements, new_elements);
|
||||
differs = true;
|
||||
}
|
||||
return differs;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
template <typename Element, typename ElementDiff>
|
||||
void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
|
||||
assert(dst != nullptr);
|
||||
while (i < old_elements.size()) {
|
||||
const Element &old_element = old_elements.Get(i);
|
||||
ElementDiff *diff = dst->Add();
|
||||
Element *old_elementp = nullptr;
|
||||
if (!diff || !(old_elementp = diff->mutable_old())) {
|
||||
llvm::errs() << "Failed to add diff element\n";
|
||||
::exit(1);
|
||||
}
|
||||
*old_elementp = old_element;
|
||||
diff->set_index(i);
|
||||
i++;
|
||||
}
|
||||
while (j < new_elements.size()) {
|
||||
const Element &new_element = new_elements.Get(j);
|
||||
ElementDiff *diff = dst->Add();
|
||||
Element *new_elementp = nullptr;
|
||||
if (!diff || !(new_elementp = diff->mutable_new_())) {
|
||||
llvm::errs() << "Failed to add diff element\n";
|
||||
::exit(1);
|
||||
}
|
||||
*new_elementp = new_element;
|
||||
diff->set_index(j);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<RecordDeclDiff> DiffWrapper<RecordDecl, RecordDeclDiff>::Get() {
|
||||
std::unique_ptr<RecordDeclDiff> record_diff(new RecordDeclDiff());
|
||||
assert(oldp_->fully_qualified_name() == newp_->fully_qualified_name());
|
||||
record_diff->set_name(oldp_->fully_qualified_name());
|
||||
google::protobuf::RepeatedPtrField<FieldDeclDiff> *fdiffs =
|
||||
record_diff->mutable_field_diffs();
|
||||
google::protobuf::RepeatedPtrField<CXXBaseSpecifierDiff> *bdiffs =
|
||||
record_diff->mutable_base_diffs();
|
||||
assert(fdiffs != nullptr && bdiffs != nullptr);
|
||||
// Template Information isn't diffed since the linker_set_key includes the
|
||||
// mangled name which includes template information.
|
||||
if (GetElementDiffs(fdiffs, oldp_->fields(), newp_->fields()) ||
|
||||
GetElementDiffs(bdiffs, oldp_->base_specifiers(),
|
||||
newp_->base_specifiers())) {
|
||||
return record_diff;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<EnumDeclDiff> DiffWrapper<EnumDecl, EnumDeclDiff>::Get() {
|
||||
std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff());
|
||||
assert(oldp_->enum_name() == newp_->enum_name());
|
||||
google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *fdiffs =
|
||||
enum_diff->mutable_field_diffs();
|
||||
assert(fdiffs != nullptr);
|
||||
if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) ||
|
||||
(oldp_->enum_type() != newp_->enum_type())) {
|
||||
return enum_diff;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // abi_diff_wrappers
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 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 clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
namespace abi_diff_wrappers {
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
class DiffWrapperBase {
|
||||
public:
|
||||
virtual std::unique_ptr<TDiff> Get() = 0 ;
|
||||
protected:
|
||||
DiffWrapperBase(const T *oldp, const T *newp)
|
||||
: oldp_(oldp), newp_(newp) { }
|
||||
template <typename Element, typename ElementDiff>
|
||||
bool GetElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements);
|
||||
|
||||
private:
|
||||
template <typename Element, typename ElementDiff>
|
||||
void GetExtraElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements);
|
||||
|
||||
protected:
|
||||
const T *oldp_;
|
||||
const T *newp_;
|
||||
};
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
class DiffWrapper : public DiffWrapperBase<T, TDiff> {
|
||||
public:
|
||||
DiffWrapper(const T *oldp, const T *newp)
|
||||
: DiffWrapperBase<T, TDiff>(oldp, newp) { }
|
||||
|
||||
std::unique_ptr<TDiff> Get() override;
|
||||
};
|
||||
|
||||
} // abi_diff_wrappers
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 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 "abi_diff.h"
|
||||
|
||||
#include <llvm/Support/CommandLine.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
static llvm::cl::OptionCategory header_checker_category(
|
||||
"header-abi-diff options");
|
||||
|
||||
static llvm::cl::opt<std::string> compatibility_report(
|
||||
"o", llvm::cl::desc("<compatibility report>"), llvm::cl::Required,
|
||||
llvm::cl::cat(header_checker_category));
|
||||
|
||||
static llvm::cl::opt<std::string> new_dump(
|
||||
"new", llvm::cl::desc("<new dump>"), llvm::cl::Required,
|
||||
llvm::cl::cat(header_checker_category));
|
||||
|
||||
static llvm::cl::opt<std::string> old_dump(
|
||||
"old", llvm::cl::desc("<old dump>"), llvm::cl::Required,
|
||||
llvm::cl::cat(header_checker_category));
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker");
|
||||
HeaderAbiDiff judge(old_dump, new_dump, compatibility_report);
|
||||
switch (judge.GenerateCompatibilityReport()) {
|
||||
case HeaderAbiDiff::COMPATIBLE:
|
||||
case HeaderAbiDiff::EXTENSION:
|
||||
return 0;
|
||||
default:
|
||||
llvm::errs() << "******************************************************\n"
|
||||
<< "VNDK Abi Compliance breakage:"
|
||||
<< " Please check compatiblity report at : "
|
||||
<< compatibility_report << "\n"
|
||||
<< "*****************************************************\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -225,9 +225,13 @@ bool RecordDeclWrapper::SetupRecordFields(
|
||||
<< " to reference dump\n";
|
||||
return false;
|
||||
}
|
||||
record_fieldp->set_field_name(field->getName());
|
||||
record_fieldp->set_field_type(QualTypeToString(field->getType()));
|
||||
record_fieldp->set_access(AccessToString(field->getAccess()));
|
||||
std::string name = field->getName();
|
||||
std::string type = QualTypeToString(field->getType());
|
||||
std::string access = AccessToString(field->getAccess());
|
||||
record_fieldp->set_field_name(name);
|
||||
record_fieldp->set_field_type(type);
|
||||
record_fieldp->set_access(access);
|
||||
record_fieldp->set_linker_set_key(name + type + access);
|
||||
field++;
|
||||
}
|
||||
return true;
|
||||
@@ -248,11 +252,14 @@ bool RecordDeclWrapper::SetupCXXBases(abi_dump::RecordDecl *cxxp) const {
|
||||
llvm::errs() << " Couldn't add base specifier to reference dump\n";
|
||||
return false;
|
||||
}
|
||||
base_specifierp->set_fully_qualified_name(
|
||||
QualTypeToString(base_class->getType()));
|
||||
base_specifierp->set_is_virtual(base_class->isVirtual());
|
||||
base_specifierp->set_access(
|
||||
AccessToString(base_class->getAccessSpecifier()));
|
||||
std::string name = QualTypeToString(base_class->getType());
|
||||
bool is_virtual = base_class->isVirtual();
|
||||
char is_virtual_c = is_virtual ? 't' : 'f';
|
||||
std::string access = AccessToString(base_class->getAccessSpecifier());
|
||||
base_specifierp->set_fully_qualified_name(name);
|
||||
base_specifierp->set_is_virtual(is_virtual);
|
||||
base_specifierp->set_access(access);
|
||||
base_specifierp->set_linker_set_key(name + is_virtual_c + access);
|
||||
base_class++;
|
||||
}
|
||||
return true;
|
||||
@@ -298,9 +305,11 @@ bool RecordDeclWrapper::SetupTemplateInfo(
|
||||
void RecordDeclWrapper::SetupRecordInfo(abi_dump::RecordDecl *record_declp,
|
||||
const std::string &source_file) const {
|
||||
std::string qualified_name = GetTagDeclQualifiedName(record_decl_);
|
||||
std::string mangled_name = GetMangledNameDecl(record_decl_);
|
||||
std::string linker_key = (mangled_name == "") ? qualified_name : mangled_name;
|
||||
record_declp->set_fully_qualified_name(qualified_name);
|
||||
//TODO: Add Template Information
|
||||
record_declp->set_linker_set_key(qualified_name);
|
||||
record_declp->set_mangled_record_name(mangled_name);
|
||||
record_declp->set_linker_set_key(linker_key);
|
||||
record_declp->set_source_file(source_file);
|
||||
record_declp->set_access(AccessToString(record_decl_->getAccess()));
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class RecordDeclWrapper : public ABIWrapper {
|
||||
void SetupRecordInfo(abi_dump::RecordDecl *record_declp,
|
||||
const std::string &source_file) const;
|
||||
|
||||
bool SetupRecordFields(abi_dump::RecordDecl *recordp,
|
||||
bool SetupRecordFields(abi_dump::RecordDecl *record_declp,
|
||||
const std::string &source_file) const;
|
||||
|
||||
bool SetupCXXBases(abi_dump::RecordDecl *cxxp) const;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <clang/Tooling/Core/QualTypeNames.h>
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -128,11 +129,8 @@ HeaderASTConsumer::HeaderASTConsumer(
|
||||
|
||||
void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
std::ofstream text_output(out_dump_name_ + ".txt");
|
||||
std::fstream binary_output(
|
||||
out_dump_name_,
|
||||
std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
|
||||
std::ofstream text_output(out_dump_name_);
|
||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||
clang::TranslationUnitDecl* translation_unit = ctx.getTranslationUnitDecl();
|
||||
std::unique_ptr<clang::MangleContext> mangle_contextp(
|
||||
ctx.createMangleContext());
|
||||
@@ -141,12 +139,10 @@ void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
|
||||
HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_, file_name_,
|
||||
exported_headers_);
|
||||
if (!v.TraverseDecl(translation_unit) ||
|
||||
!google::protobuf::TextFormat::PrintToString(tu, &str_out) ||
|
||||
!tu.SerializeToOstream(&binary_output)) {
|
||||
!google::protobuf::TextFormat::Print(tu, &text_os)) {
|
||||
llvm::errs() << "Serialization to ostream failed\n";
|
||||
::exit(1);
|
||||
}
|
||||
text_output << str_out;
|
||||
}
|
||||
|
||||
void HeaderASTConsumer::HandleVTable(clang::CXXRecordDecl *crd) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
@@ -78,15 +79,13 @@ class HeaderAbiLinker {
|
||||
|
||||
bool HeaderAbiLinker::LinkAndDump() {
|
||||
abi_dump::TranslationUnit linked_tu;
|
||||
std::string str_out;
|
||||
std::ofstream text_output(out_dump_name_ + ".txt");
|
||||
std::fstream binary_output(
|
||||
out_dump_name_,
|
||||
std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
std::ofstream text_output(out_dump_name_);
|
||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||
for (auto &&i : dump_files_) {
|
||||
abi_dump::TranslationUnit dump_tu;
|
||||
std::fstream input(i, std::ios::binary | std::ios::in);
|
||||
if (!dump_tu.ParseFromIstream(&input) ||
|
||||
std::ifstream input(i);
|
||||
google::protobuf::io::IstreamInputStream text_is(&input);
|
||||
if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) ||
|
||||
!LinkRecords(dump_tu, &linked_tu) ||
|
||||
!LinkFunctions(dump_tu, &linked_tu) ||
|
||||
!LinkEnums(dump_tu, &linked_tu)) {
|
||||
@@ -95,13 +94,10 @@ bool HeaderAbiLinker::LinkAndDump() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!google::protobuf::TextFormat::PrintToString(linked_tu, &str_out) ||
|
||||
!linked_tu.SerializeToOstream(&binary_output)) {
|
||||
if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) {
|
||||
llvm::errs() << "Serialization to ostream failed\n";
|
||||
return false;
|
||||
}
|
||||
text_output << str_out;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
55
vndk/tools/header-checker/proto/abi_diff.proto
Normal file
55
vndk/tools/header-checker/proto/abi_diff.proto
Normal file
@@ -0,0 +1,55 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "development/vndk/tools/header-checker/proto/abi_dump.proto";
|
||||
|
||||
package abi_diff;
|
||||
|
||||
message FieldDeclDiff {
|
||||
optional abi_dump.FieldDecl old = 1;
|
||||
optional abi_dump.FieldDecl new = 2;
|
||||
required uint32 index = 3;
|
||||
}
|
||||
|
||||
message EnumFieldDeclDiff {
|
||||
optional abi_dump.EnumFieldDecl old = 1;
|
||||
optional abi_dump.EnumFieldDecl new = 2;
|
||||
required uint32 index = 3;
|
||||
}
|
||||
|
||||
message CXXBaseSpecifierDiff {
|
||||
optional abi_dump.CXXBaseSpecifier old = 1;
|
||||
optional abi_dump.CXXBaseSpecifier new = 2;
|
||||
required uint32 index = 3;
|
||||
}
|
||||
|
||||
message AccessDiff {
|
||||
required string old = 1;
|
||||
required string new = 2;
|
||||
}
|
||||
|
||||
message RecordDeclDiff {
|
||||
repeated FieldDeclDiff field_diffs = 1;
|
||||
repeated CXXBaseSpecifierDiff base_diffs = 2;
|
||||
required AccessDiff access_diff = 3;
|
||||
required string name = 4;
|
||||
}
|
||||
|
||||
message EnumDeclDiff {
|
||||
repeated EnumFieldDeclDiff field_diffs = 1;
|
||||
optional AccessDiff access_diff = 2;
|
||||
required string name = 3;
|
||||
}
|
||||
|
||||
message TranslationUnitDiff {
|
||||
// Differing Elements.
|
||||
repeated RecordDeclDiff records_diff = 1;
|
||||
repeated EnumDeclDiff enums_diff = 3;
|
||||
// Removed Elements.
|
||||
repeated abi_dump.RecordDecl records_removed = 4;
|
||||
repeated abi_dump.FunctionDecl functions_removed = 5;
|
||||
repeated abi_dump.EnumDecl enums_removed = 6;
|
||||
// Added Elements.
|
||||
repeated abi_dump.RecordDecl records_added = 7;
|
||||
repeated abi_dump.FunctionDecl functions_added = 8;
|
||||
repeated abi_dump.EnumDecl enums_added = 9;
|
||||
}
|
||||
@@ -25,11 +25,13 @@ message FieldDecl {
|
||||
optional string field_type = 2 [default = "VOID"];
|
||||
optional string access = 3 [default = "public"];
|
||||
optional bool default_arg = 4 [default = false];
|
||||
optional string linker_set_key = 5 [default = "NONE"];
|
||||
}
|
||||
|
||||
message EnumFieldDecl {
|
||||
optional string enum_field_name = 1 [default = "NONE"];
|
||||
optional int64 enum_field_value = 2 [default = 0]; // assumption: fits int64
|
||||
optional string linker_set_key = 3 [default = "NONE"];
|
||||
}
|
||||
|
||||
message TemplateInfo {
|
||||
@@ -40,19 +42,19 @@ message CXXBaseSpecifier {
|
||||
optional string fully_qualified_name = 1 [default = "NONE"];
|
||||
optional string access = 2 [default = "public"];
|
||||
optional bool is_virtual = 3 [default = false];
|
||||
optional string linker_set_key = 4 [default = "NONE"];
|
||||
}
|
||||
|
||||
message RecordDecl {
|
||||
repeated FieldDecl fields = 2;
|
||||
repeated string inner_classes = 3;
|
||||
repeated CXXBaseSpecifier base_specifiers = 4;
|
||||
optional string fully_qualified_name = 5 [default = "NONE"];
|
||||
optional int64 id = 6 [default = 0];
|
||||
optional string source_file = 9 [default = "NONE"];
|
||||
optional string template_kind = 11 [default = "NONE"];
|
||||
optional TemplateInfo template_info = 12;
|
||||
optional string access = 13 [default = "public"];
|
||||
optional string linker_set_key = 14 [default = "NONE"];
|
||||
repeated FieldDecl fields = 1;
|
||||
repeated CXXBaseSpecifier base_specifiers = 2;
|
||||
optional string fully_qualified_name = 3 [default = "NONE"];
|
||||
optional string source_file = 4 [default = "NONE"];
|
||||
optional uint32 template_kind = 5 [default = 0];
|
||||
optional TemplateInfo template_info = 6;
|
||||
optional string access = 7 [default = "public"];
|
||||
optional string linker_set_key = 8 [default = "NONE"];
|
||||
optional string mangled_record_name = 9 [default = "NONE"];
|
||||
}
|
||||
|
||||
message EnumDecl {
|
||||
|
||||
Reference in New Issue
Block a user