From 03ac4a73467edcc27a23016f68242bf33c7e3cd9 Mon Sep 17 00:00:00 2001 From: Logan Chien Date: Mon, 19 Dec 2016 17:34:35 +0800 Subject: [PATCH] Add initial header checker. Test: mm -j && header-checker file.h -- clang file.h Change-Id: I2d743c931d4736da40288991c79e03ebd2b3e8b0 --- vndk/tools/Android.bp | 1 + vndk/tools/header-checker/Android.bp | 120 +++++++++++++ .../header-checker/MODULE_LICENSE_APACHE2 | 0 vndk/tools/header-checker/README.md | 12 ++ .../header-checker/src/frontend_action.cpp | 160 ++++++++++++++++++ .../header-checker/src/frontend_action.h | 49 ++++++ .../src/frontend_action_factory.cpp | 29 ++++ .../src/frontend_action_factory.h | 33 ++++ .../header-checker/src/header_checker.cpp | 98 +++++++++++ vndk/tools/header-checker/tests/example1.h | 50 ++++++ 10 files changed, 552 insertions(+) create mode 100644 vndk/tools/header-checker/Android.bp create mode 100644 vndk/tools/header-checker/MODULE_LICENSE_APACHE2 create mode 100644 vndk/tools/header-checker/README.md create mode 100644 vndk/tools/header-checker/src/frontend_action.cpp create mode 100644 vndk/tools/header-checker/src/frontend_action.h create mode 100644 vndk/tools/header-checker/src/frontend_action_factory.cpp create mode 100644 vndk/tools/header-checker/src/frontend_action_factory.h create mode 100644 vndk/tools/header-checker/src/header_checker.cpp create mode 100644 vndk/tools/header-checker/tests/example1.h diff --git a/vndk/tools/Android.bp b/vndk/tools/Android.bp index c1e980119..d008503fa 100644 --- a/vndk/tools/Android.bp +++ b/vndk/tools/Android.bp @@ -16,4 +16,5 @@ subdirs = [ "abides", + "header-checker", ] diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp new file mode 100644 index 000000000..ccffdad56 --- /dev/null +++ b/vndk/tools/header-checker/Android.bp @@ -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, + }, + }, +} diff --git a/vndk/tools/header-checker/MODULE_LICENSE_APACHE2 b/vndk/tools/header-checker/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/vndk/tools/header-checker/README.md b/vndk/tools/header-checker/README.md new file mode 100644 index 000000000..68edb64d5 --- /dev/null +++ b/vndk/tools/header-checker/README.md @@ -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++ diff --git a/vndk/tools/header-checker/src/frontend_action.cpp b/vndk/tools/header-checker/src/frontend_action.cpp new file mode 100644 index 000000000..8e1b8f0a2 --- /dev/null +++ b/vndk/tools/header-checker/src/frontend_action.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static constexpr bool kLoadRefAsImplicitPCH = false; + +class HeaderCheckVisitor + : public clang::RecursiveASTVisitor { + 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 ¯o_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(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 +HeaderCheckerFrontendAction::CreateASTConsumer(clang::CompilerInstance &ci, + llvm::StringRef header_file) { + // Add preprocessor callbacks. + clang::Preprocessor &pp = ci.getPreprocessor(); + pp.addPPCallbacks(llvm::make_unique()); + + // Create AST consumers. + std::vector> consumers; + consumers.push_back(llvm::make_unique()); + + 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(); + consumers.push_back( + llvm::make_unique( + 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(std::move(consumers)); +} diff --git a/vndk/tools/header-checker/src/frontend_action.h b/vndk/tools/header-checker/src/frontend_action.h new file mode 100644 index 000000000..0d07f9d06 --- /dev/null +++ b/vndk/tools/header-checker/src/frontend_action.h @@ -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 +#include + +#include +#include + +namespace clang { + class ASTConsumer; + class CompilerInstance; +} // namespace clang + +class HeaderCheckerFrontendAction : public clang::ASTFrontendAction { + private: + std::string ref_dump_name_; + std::unique_ptr 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 CreateASTConsumer( + clang::CompilerInstance &ci, llvm::StringRef header_file) override; +}; + +#endif // FRONTEND_ACTION_H_ diff --git a/vndk/tools/header-checker/src/frontend_action_factory.cpp b/vndk/tools/header-checker/src/frontend_action_factory.cpp new file mode 100644 index 000000000..da17ec4f7 --- /dev/null +++ b/vndk/tools/header-checker/src/frontend_action_factory.cpp @@ -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 + +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_); +} diff --git a/vndk/tools/header-checker/src/frontend_action_factory.h b/vndk/tools/header-checker/src/frontend_action_factory.h new file mode 100644 index 000000000..03b99f3ed --- /dev/null +++ b/vndk/tools/header-checker/src/frontend_action_factory.h @@ -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 + +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_ diff --git a/vndk/tools/header-checker/src/header_checker.cpp b/vndk/tools/header-checker/src/header_checker.cpp new file mode 100644 index 000000000..e498e2d94 --- /dev/null +++ b/vndk/tools/header-checker/src/header_checker.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +static llvm::cl::OptionCategory header_checker_category( + "header-checker options"); + +static llvm::cl::opt header_file( + llvm::cl::Positional, llvm::cl::desc("
"), llvm::cl::Required, + llvm::cl::cat(header_checker_category)); + +static llvm::cl::opt 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 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 &map = llvm::cl::getRegisteredOptions(); + for (llvm::StringMapEntry &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 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 header_files{ header_file }; + + clang::tooling::ClangTool tool(*compilations, header_files); + + std::unique_ptr factory( + new HeaderCheckerFrontendActionFactory(ref_dump, gen_ref_dump)); + + return tool.run(factory.get()); +} diff --git a/vndk/tools/header-checker/tests/example1.h b/vndk/tools/header-checker/tests/example1.h new file mode 100644 index 000000000..6ef0c755d --- /dev/null +++ b/vndk/tools/header-checker/tests/example1.h @@ -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 +struct StackNode { +public: + T value_; + StackNode* next_; + +public: + StackNode(T t, StackNode* next = nullptr) + : value_(static_cast(t)), + next_(next) { } +}; + +template +class Stack { +private: + StackNode* head_; + +public: + Stack() : head_(nullptr) { } + + void push(T t) { + head_ = new StackNode(static_cast(t), head_); + } + + T pop() { + StackNode* cur = head_; + head_ = cur->next_; + T res = static_cast(cur->value_); + delete cur; + return res; + } +}; + +#endif // EXAMPLE1_H_