Merge "Add initial header checker."

This commit is contained in:
Treehugger Robot
2017-01-13 17:34:29 +00:00
committed by Gerrit Code Review
10 changed files with 552 additions and 0 deletions

View File

@@ -16,4 +16,5 @@
subdirs = [
"abides",
"header-checker",
]

View File

@@ -0,0 +1,120 @@
//
// 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.
//
cc_defaults {
name: "header-checker-defaults",
defaults: [
"clang-defaults",
],
cflags: [
"-Wall",
"-Werror",
"-std=c++11",
],
}
cc_defaults {
name: "header-checker-lib-defaults",
shared_libs: [
"libclang",
"libLLVM",
],
}
cc_defaults {
name: "header-checker-lib-debug-defaults",
static_libs: [
"libclangTooling",
"libclangFrontendTool",
"libclangFrontend",
"libclangDriver",
"libclangSerialization",
"libclangCodeGen",
"libclangRewriteFrontend",
"libclangRewrite",
"libclangParse",
"libclangSema",
"libclangStaticAnalyzerFrontend",
"libclangStaticAnalyzerCheckers",
"libclangStaticAnalyzerMPIChecker",
"libclangStaticAnalyzerCore",
"libclangAnalysis",
"libclangEdit",
"libclangAST",
"libclangLex",
"libclangBasic",
"libLLVMIRReader",
"libLLVMAsmParser",
"libLLVMAsmPrinter",
"libLLVMBitReader",
"libLLVMBitWriter",
"libLLVMMC",
"libLLVMMCParser",
"libLLVMCore",
"libLLVMOption",
"libLLVMProfileData",
"libLLVMObject",
"libLLVMMCDisassembler",
"libLLVMSupport",
],
}
cc_binary_host {
name: "header-checker",
defaults: [
"header-checker-defaults",
"header-checker-lib-debug-defaults",
],
srcs: ["src/*.cpp"],
target: {
windows: {
host_ldlibs: [
"-limagehlp",
"-lole32",
"-lversion",
],
cflags: [
// Skip missing-field-initializer warnings for mingw.
"-Wno-error=missing-field-initializers",
],
},
linux: {
host_ldlibs: [
"-ldl",
"-lpthread",
],
},
darwin: {
host_ldlibs: [
"-ldl",
"-lpthread",
],
},
},
product_variables: {
unbundled_build: {
enabled: false,
},
},
}

View File

@@ -0,0 +1,12 @@
# VNDK Header Checker
`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.
## Usage
Example 1:
$ header-checker -g -r example1.ast tests/example1.h -- clang -x c++

View File

@@ -0,0 +1,160 @@
// 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 "frontend_action.h"
#include <clang/AST/AST.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/MultiplexConsumer.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/Token.h>
#include <clang/Serialization/ASTWriter.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/Support/raw_ostream.h>
#include <memory>
#include <string>
static constexpr bool kLoadRefAsImplicitPCH = false;
class HeaderCheckVisitor
: public clang::RecursiveASTVisitor<HeaderCheckVisitor> {
public:
bool VisitRecordDecl(const clang::RecordDecl *decl) {
llvm::errs() << "struct: " << decl->getName() << "\n";
return true;
}
bool VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) {
llvm::errs() << "class: " << decl->getName() << "\n";
return true;
}
bool VisitFunctionDecl(const clang::FunctionDecl *decl) {
llvm::errs() << "func: " << decl->getName() << "\n";
return true;
}
};
class HeaderCheckerConsumer : public clang::ASTConsumer {
public:
void HandleTranslationUnit(clang::ASTContext &ctx) override {
llvm::errs() << "HandleTranslationUnit ------------------------------\n";
clang::TranslationUnitDecl* translation_unit = ctx.getTranslationUnitDecl();
HeaderCheckVisitor v;
v.TraverseDecl(translation_unit);
}
void HandleVTable(clang::CXXRecordDecl *crd) override {
llvm::errs() << "HandleVTable: " << crd->getName() << "\n";
}
};
class HeaderCheckerPPCallbacks : public clang::PPCallbacks {
private:
llvm::StringRef ToString(const clang::Token &tok) {
return tok.getIdentifierInfo()->getName();
}
public:
void MacroDefined(const clang::Token &macro_name_tok,
const clang::MacroDirective *) override {
assert(macro_name_tok.isAnyIdentifier());
llvm::errs() << "defines: " << ToString(macro_name_tok) << "\n";
}
};
HeaderCheckerFrontendAction::HeaderCheckerFrontendAction(
const std::string &ref_dump_name, bool should_generate_ref_dump)
: ref_dump_name_(ref_dump_name),
should_generate_ref_dump_(should_generate_ref_dump) { }
static bool VisitRefDumpDecls(void *ctx, const clang::Decl *decl) {
HeaderCheckVisitor v;
v.TraverseDecl(const_cast<clang::Decl *>(decl));
return true;
}
bool HeaderCheckerFrontendAction::BeginSourceFileAction(
clang::CompilerInstance &ci, llvm::StringRef header_file) {
// Load reference dump file.
if (llvm::sys::fs::exists(ref_dump_name_)) {
if (kLoadRefAsImplicitPCH) {
ci.getPreprocessorOpts().ImplicitPCHInclude = ref_dump_name_;
} else {
clang::DiagnosticsEngine &diag = ci.getDiagnostics();
diag.getClient()->BeginSourceFile(ci.getLangOpts(),
&ci.getPreprocessor());
// FIXME: Must replace getPCHContainerReader() with other ASTReader.
ref_dump_ = clang::ASTUnit::LoadFromASTFile(
ref_dump_name_, ci.getPCHContainerReader(), &diag,
ci.getFileSystemOpts(), ci.getCodeGenOpts().DebugTypeExtRefs);
diag.getClient()->EndSourceFile();
if (ref_dump_) {
llvm::errs() << "Loaded: " << ref_dump_name_ << " : "
<< ref_dump_->top_level_size() << "\n";
ref_dump_->visitLocalTopLevelDecls(nullptr, VisitRefDumpDecls);
llvm::errs() << "----------------------------------------\n";
}
}
}
return true;
}
void HeaderCheckerFrontendAction::EndSourceFileAction() {
ref_dump_.reset();
}
std::unique_ptr<clang::ASTConsumer>
HeaderCheckerFrontendAction::CreateASTConsumer(clang::CompilerInstance &ci,
llvm::StringRef header_file) {
// Add preprocessor callbacks.
clang::Preprocessor &pp = ci.getPreprocessor();
pp.addPPCallbacks(llvm::make_unique<HeaderCheckerPPCallbacks>());
// Create AST consumers.
std::vector<std::unique_ptr<clang::ASTConsumer>> consumers;
consumers.push_back(llvm::make_unique<HeaderCheckerConsumer>());
if (should_generate_ref_dump_) {
std::string sysroot;
llvm::raw_pwrite_stream *ref_dump_os = ci.createOutputFile(
ref_dump_name_, true, false, header_file, "", true);
if (!ref_dump_os) {
llvm::errs() << "ERROR: Failed to create reference dump file: "
<< ref_dump_name_ << "\n";
return nullptr;
}
auto buffer = std::make_shared<clang::PCHBuffer>();
consumers.push_back(
llvm::make_unique<clang::PCHGenerator>(
ci.getPreprocessor(), ref_dump_name_, nullptr, "", buffer,
ci.getFrontendOpts().ModuleFileExtensions, false, false));
consumers.push_back(
ci.getPCHContainerWriter().CreatePCHContainerGenerator(
ci, header_file, ref_dump_name_, ref_dump_os, buffer));
}
return llvm::make_unique<clang::MultiplexConsumer>(std::move(consumers));
}

View File

@@ -0,0 +1,49 @@
// 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.
#ifndef FRONTEND_ACTION_H_
#define FRONTEND_ACTION_H_
#include <clang/Frontend/FrontendAction.h>
#include <llvm/ADT/StringRef.h>
#include <memory>
#include <string>
namespace clang {
class ASTConsumer;
class CompilerInstance;
} // namespace clang
class HeaderCheckerFrontendAction : public clang::ASTFrontendAction {
private:
std::string ref_dump_name_;
std::unique_ptr<clang::ASTUnit> ref_dump_;
bool should_generate_ref_dump_;
public:
HeaderCheckerFrontendAction(const std::string &ref_dump_name,
bool should_generate_ref_dump);
protected:
bool BeginSourceFileAction(clang::CompilerInstance &ci,
llvm::StringRef header_file) override;
void EndSourceFileAction() override;
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &ci, llvm::StringRef header_file) override;
};
#endif // FRONTEND_ACTION_H_

View File

@@ -0,0 +1,29 @@
// 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 "frontend_action_factory.h"
#include "frontend_action.h"
#include <clang/Frontend/FrontendActions.h>
HeaderCheckerFrontendActionFactory::HeaderCheckerFrontendActionFactory(
const std::string &ref_dump_name, bool should_generate_ref_dump)
: ref_dump_name_(ref_dump_name),
should_generate_ref_dump_(should_generate_ref_dump) { }
clang::FrontendAction *HeaderCheckerFrontendActionFactory::create() {
return new HeaderCheckerFrontendAction(ref_dump_name_,
should_generate_ref_dump_);
}

View File

@@ -0,0 +1,33 @@
// 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.
#ifndef FRONTEND_ACTION_FACTORY_H_
#define FRONTEND_ACTION_FACTORY_H_
#include <clang/Tooling/Tooling.h>
class HeaderCheckerFrontendActionFactory
: public clang::tooling::FrontendActionFactory {
private:
std::string ref_dump_name_;
bool should_generate_ref_dump_;
public:
HeaderCheckerFrontendActionFactory(const std::string &ref_dump_name,
bool should_generate_ref_dump);
clang::FrontendAction *create() override;
};
#endif // FRONTEND_ACTION_FACTORY_H_

View File

@@ -0,0 +1,98 @@
// 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 "frontend_action_factory.h"
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/CompilationDatabase.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <memory>
#include <string>
#include <stdlib.h>
static llvm::cl::OptionCategory header_checker_category(
"header-checker options");
static llvm::cl::opt<std::string> header_file(
llvm::cl::Positional, llvm::cl::desc("<header>"), llvm::cl::Required,
llvm::cl::cat(header_checker_category));
static llvm::cl::opt<std::string> ref_dump(
"r", llvm::cl::value_desc("refdump"), llvm::cl::Required,
llvm::cl::desc("Specify the reference dump file name"),
llvm::cl::cat(header_checker_category));
static llvm::cl::opt<bool> gen_ref_dump(
"g", llvm::cl::init(false),
llvm::cl::desc("Generate reference dump for header file"),
llvm::cl::cat(header_checker_category));
// Hide irrelevant command line options defined in LLVM libraries.
static void HideIrrelevantCommandLineOptions() {
llvm::StringMap<llvm::cl::Option *> &map = llvm::cl::getRegisteredOptions();
for (llvm::StringMapEntry<llvm::cl::Option *> &p : map) {
if (p.second->Category == &header_checker_category) {
continue;
}
if (p.first().startswith("help")) {
continue;
}
p.second->setHiddenFlag(llvm::cl::Hidden);
}
}
int main(int argc, const char **argv) {
HideIrrelevantCommandLineOptions();
// Create compilation database from command line arguments after "--".
std::unique_ptr<clang::tooling::CompilationDatabase> compilations(
clang::tooling::FixedCompilationDatabase::loadFromCommandLine(
argc, argv));
// Parse the command line options.
llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker");
// Check the availability of input header file and reference dump file.
if (!llvm::sys::fs::exists(header_file)) {
llvm::errs() << "ERROR: Header file \"" << header_file << "\" not found\n";
::exit(1);
}
if (!gen_ref_dump && !llvm::sys::fs::exists(ref_dump)) {
llvm::errs() << "ERROR: Reference file \"" << ref_dump << "\" not found\n";
::exit(1);
}
// Check the availability of clang compilation options.
if (!compilations) {
llvm::errs() << "ERROR: Clang compilation options not specified.\n";
::exit(1);
}
// Initialize clang tools and run front-end action.
std::vector<std::string> header_files{ header_file };
clang::tooling::ClangTool tool(*compilations, header_files);
std::unique_ptr<clang::tooling::FrontendActionFactory> factory(
new HeaderCheckerFrontendActionFactory(ref_dump, gen_ref_dump));
return tool.run(factory.get());
}

View File

@@ -0,0 +1,50 @@
#ifndef EXAMPLE1_H_
#define EXAMPLE1_H_
#if defined(__cplusplus)
extern "C" {
#endif
struct Hello {
int foo;
int bar;
};
#if defined(__cplusplus)
} // extern "C"
#endif
template<typename T>
struct StackNode {
public:
T value_;
StackNode<T>* next_;
public:
StackNode(T t, StackNode* next = nullptr)
: value_(static_cast<T&&>(t)),
next_(next) { }
};
template<typename T>
class Stack {
private:
StackNode<T>* head_;
public:
Stack() : head_(nullptr) { }
void push(T t) {
head_ = new StackNode<T>(static_cast<T&&>(t), head_);
}
T pop() {
StackNode<T>* cur = head_;
head_ = cur->next_;
T res = static_cast<T&&>(cur->value_);
delete cur;
return res;
}
};
#endif // EXAMPLE1_H_