/* * 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 "Driver.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Arch.h" #include "DeclarationDatabase.h" #include "versioner.h" using namespace std::chrono_literals; using namespace std::string_literals; using namespace clang; class VersionerASTConsumer : public clang::ASTConsumer { public: HeaderDatabase* header_database; CompilationType type; VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type) : header_database(header_database), type(type) { } void HandleTranslationUnit(ASTContext& ctx) override { header_database->parseAST(type, ctx); } }; class VersionerASTAction : public clang::ASTFrontendAction { public: HeaderDatabase* header_database; CompilationType type; VersionerASTAction(HeaderDatabase* header_database, CompilationType type) : header_database(header_database), type(type) { } std::unique_ptr CreateASTConsumer(CompilerInstance&, llvm::StringRef) override { return std::make_unique(header_database, type); } }; static IntrusiveRefCntPtr constructDiags() { IntrusiveRefCntPtr diag_opts(new DiagnosticOptions()); auto diag_printer = std::make_unique(llvm::errs(), diag_opts.get()); IntrusiveRefCntPtr diag_ids(new DiagnosticIDs()); IntrusiveRefCntPtr diags( new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release())); return diags; } // clang's driver is slow compared to the work it performs to compile our headers. // Run it once to generate flags for each target, and memoize the results. static std::unordered_map> cc1_flags; static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__"; static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr vfs, CompilationType type, const std::vector& include_dirs) { std::vector cmd = { "versioner" }; if (type.cpp) { cmd.push_back("-std=gnu++11"); cmd.push_back("-x"); cmd.push_back("c++"); } else { cmd.push_back("-std=gnu11"); cmd.push_back("-x"); cmd.push_back("c"); } cmd.push_back("-fsyntax-only"); cmd.push_back("-Wall"); cmd.push_back("-Wextra"); cmd.push_back("-Weverything"); cmd.push_back("-Werror"); cmd.push_back("-Wundef"); cmd.push_back("-Wno-unused-macros"); cmd.push_back("-Wno-unused-function"); cmd.push_back("-Wno-unused-variable"); cmd.push_back("-Wno-unknown-attributes"); cmd.push_back("-Wno-pragma-once-outside-header"); cmd.push_back("-target"); cmd.push_back(arch_targets[type.arch]); cmd.push_back("-DANDROID"); cmd.push_back("-D__BIONIC_VERSIONER=1"); cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level)); cmd.push_back("-D_FORTIFY_SOURCE=2"); cmd.push_back("-D_GNU_SOURCE"); cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits)); cmd.push_back("-nostdinc"); if (add_include) { cmd.push_back("-include"); cmd.push_back("android/versioning.h"); } for (const auto& dir : include_dirs) { cmd.push_back("-isystem"); cmd.push_back(dir); } cmd.push_back("-include"); cmd.push_back(filename_placeholder); cmd.push_back("-"); auto diags = constructDiags(); driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, "versioner", vfs); driver.setCheckInputsExist(false); llvm::SmallVector driver_args; for (const std::string& str : cmd) { driver_args.push_back(str.c_str()); } std::unique_ptr Compilation(driver.BuildCompilation(driver_args)); const driver::JobList& jobs = Compilation->getJobs(); if (jobs.size() != 1) { errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str()); } const driver::Command& driver_cmd = llvm::cast(*jobs.begin()); const llvm::opt::ArgStringList& cc_args = driver_cmd.getArguments(); if (cc_args.size() == 0) { errx(1, "driver returned empty command for %s", to_string(type).c_str()); } std::vector result(cc_args.begin(), cc_args.end()); { static std::mutex cc1_init_mutex; std::unique_lock lock(cc1_init_mutex); if (cc1_flags.count(type) > 0) { errx(1, "attemped to generate cc1 flags for existing CompilationType %s", to_string(type).c_str()); } cc1_flags.emplace(std::make_pair(type, std::move(result))); } } static std::vector getCC1Command(CompilationType type, const std::string& filename) { const auto& target_flag_it = cc1_flags.find(type); if (target_flag_it == cc1_flags.end()) { errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str()); } std::vector result; for (const std::string& flag : target_flag_it->second) { if (flag == "-disable-free") { continue; } else if (flag == filename_placeholder) { result.push_back(filename.c_str()); } else { result.push_back(flag.c_str()); } } return result; } void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr vfs, const std::set& types, const std::unordered_map& reqs) { if (!cc1_flags.empty()) { errx(1, "reinitializing target CC1 flag cache?"); } auto start = std::chrono::high_resolution_clock::now(); std::vector threads; for (const CompilationType type : types) { threads.emplace_back([type, &vfs, &reqs]() { const auto& arch_req_it = reqs.find(type.arch); if (arch_req_it == reqs.end()) { errx(1, "CompilationRequirement map missing entry for CompilationType %s", to_string(type).c_str()); } generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies); }); } for (auto& thread : threads) { thread.join(); } auto end = std::chrono::high_resolution_clock::now(); if (verbose) { auto diff = (end - start) / 1.0ms; printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff); } if (cc1_flags.empty()) { errx(1, "failed to initialize target CC1 flag cache"); } } void compileHeader(llvm::IntrusiveRefCntPtr vfs, HeaderDatabase* header_database, CompilationType type, const std::string& filename) { auto diags = constructDiags(); std::vector cc1_flags = getCC1Command(type, filename); auto invocation = std::make_unique(); if (!CompilerInvocation::CreateFromArgs(*invocation.get(), cc1_flags, *diags)) { errx(1, "failed to create CompilerInvocation"); } clang::CompilerInstance Compiler; Compiler.setInvocation(std::move(invocation)); Compiler.setDiagnostics(diags.get()); Compiler.createFileManager(vfs); VersionerASTAction versioner_action(header_database, type); if (!Compiler.ExecuteAction(versioner_action)) { errx(1, "compilation generated warnings or errors"); } if (diags->getNumWarnings() || diags->hasErrorOccurred()) { errx(1, "compilation generated warnings or errors"); } }