Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "Driver.h"
     18 
     19 #include <err.h>
     20 #include <string.h>
     21 
     22 #include <chrono>
     23 #include <mutex>
     24 #include <string>
     25 #include <thread>
     26 #include <unordered_map>
     27 #include <vector>
     28 
     29 #include <clang/AST/ASTConsumer.h>
     30 #include <clang/Basic/Diagnostic.h>
     31 #include <clang/Basic/TargetInfo.h>
     32 #include <clang/Basic/VirtualFileSystem.h>
     33 #include <clang/Driver/Compilation.h>
     34 #include <clang/Driver/Driver.h>
     35 #include <clang/Frontend/CompilerInstance.h>
     36 #include <clang/Frontend/CompilerInvocation.h>
     37 #include <clang/Frontend/FrontendAction.h>
     38 #include <clang/Frontend/FrontendActions.h>
     39 #include <clang/Frontend/TextDiagnosticPrinter.h>
     40 #include <clang/Frontend/Utils.h>
     41 #include <clang/FrontendTool/Utils.h>
     42 #include <llvm/ADT/IntrusiveRefCntPtr.h>
     43 #include <llvm/ADT/SmallVector.h>
     44 #include <llvm/ADT/StringRef.h>
     45 #include <llvm/Config/config.h>
     46 
     47 #include "Arch.h"
     48 #include "DeclarationDatabase.h"
     49 #include "versioner.h"
     50 
     51 using namespace std::chrono_literals;
     52 using namespace std::string_literals;
     53 
     54 using namespace clang;
     55 
     56 class VersionerASTConsumer : public clang::ASTConsumer {
     57  public:
     58   HeaderDatabase* header_database;
     59   CompilationType type;
     60 
     61   VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type)
     62       : header_database(header_database), type(type) {
     63   }
     64 
     65   virtual void HandleTranslationUnit(ASTContext& ctx) override {
     66     header_database->parseAST(type, ctx);
     67   }
     68 };
     69 
     70 class VersionerASTAction : public clang::ASTFrontendAction {
     71  public:
     72   HeaderDatabase* header_database;
     73   CompilationType type;
     74 
     75   VersionerASTAction(HeaderDatabase* header_database, CompilationType type)
     76       : header_database(header_database), type(type) {
     77   }
     78 
     79   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override {
     80     return std::make_unique<VersionerASTConsumer>(header_database, type);
     81   }
     82 };
     83 
     84 static IntrusiveRefCntPtr<DiagnosticsEngine> constructDiags() {
     85   IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
     86   auto diag_printer = std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get());
     87   IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
     88   IntrusiveRefCntPtr<DiagnosticsEngine> diags(
     89       new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release()));
     90   return diags;
     91 }
     92 
     93 // clang's driver is slow compared to the work it performs to compile our headers.
     94 // Run it once to generate flags for each target, and memoize the results.
     95 static std::unordered_map<CompilationType, std::vector<std::string>> cc1_flags;
     96 static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__";
     97 static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
     98                                    CompilationType type,
     99                                    const std::vector<std::string>& include_dirs) {
    100   std::vector<std::string> cmd = { "versioner" };
    101   if (type.cpp) {
    102     cmd.push_back("-std=gnu++11");
    103     cmd.push_back("-x");
    104     cmd.push_back("c++");
    105   } else {
    106     cmd.push_back("-std=gnu11");
    107     cmd.push_back("-x");
    108     cmd.push_back("c");
    109   }
    110 
    111   cmd.push_back("-fsyntax-only");
    112 
    113   cmd.push_back("-Wall");
    114   cmd.push_back("-Wextra");
    115   cmd.push_back("-Weverything");
    116   cmd.push_back("-Werror");
    117   cmd.push_back("-Wundef");
    118   cmd.push_back("-Wno-unused-macros");
    119   cmd.push_back("-Wno-unused-function");
    120   cmd.push_back("-Wno-unused-variable");
    121   cmd.push_back("-Wno-unknown-attributes");
    122   cmd.push_back("-Wno-pragma-once-outside-header");
    123 
    124   cmd.push_back("-target");
    125   cmd.push_back(arch_targets[type.arch]);
    126 
    127   cmd.push_back("-DANDROID");
    128   cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
    129   // FIXME: Re-enable FORTIFY properly once our clang in external/ is new enough
    130   // to support diagnose_if without giving us syntax errors.
    131 #if 0
    132   cmd.push_back("-D_FORTIFY_SOURCE=2");
    133 #else
    134   cmd.push_back("-D_FORTIFY_SOURCE=0");
    135   cmd.push_back("-D__BIONIC_DECLARE_FORTIFY_HELPERS");
    136 #endif
    137   cmd.push_back("-D_GNU_SOURCE");
    138   cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
    139 
    140   cmd.push_back("-nostdinc");
    141 
    142   if (add_include) {
    143     cmd.push_back("-include");
    144     cmd.push_back("android/versioning.h");
    145   }
    146 
    147   for (const auto& dir : include_dirs) {
    148     cmd.push_back("-isystem");
    149     cmd.push_back(dir);
    150   }
    151 
    152   cmd.push_back("-include");
    153   cmd.push_back(filename_placeholder);
    154   cmd.push_back("-");
    155 
    156   auto diags = constructDiags();
    157   driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, vfs);
    158   driver.setCheckInputsExist(false);
    159 
    160   llvm::SmallVector<const char*, 32> driver_args;
    161   for (const std::string& str : cmd) {
    162     driver_args.push_back(str.c_str());
    163   }
    164 
    165   std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args));
    166   const driver::JobList& jobs = Compilation->getJobs();
    167   if (jobs.size() != 1) {
    168     errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str());
    169   }
    170 
    171   const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin());
    172   const driver::ArgStringList& cc_args = driver_cmd.getArguments();
    173 
    174   if (cc_args.size() == 0) {
    175     errx(1, "driver returned empty command for %s", to_string(type).c_str());
    176   }
    177 
    178   std::vector<std::string> result(cc_args.begin(), cc_args.end());
    179 
    180   {
    181     static std::mutex cc1_init_mutex;
    182     std::unique_lock<std::mutex> lock(cc1_init_mutex);
    183     if (cc1_flags.count(type) > 0) {
    184       errx(1, "attemped to generate cc1 flags for existing CompilationType %s",
    185            to_string(type).c_str());
    186     }
    187 
    188     cc1_flags.emplace(std::make_pair(type, std::move(result)));
    189   }
    190 }
    191 
    192 static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) {
    193   const auto& target_flag_it = cc1_flags.find(type);
    194   if (target_flag_it == cc1_flags.end()) {
    195     errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str());
    196   }
    197 
    198   std::vector<const char*> result;
    199   for (const std::string& flag : target_flag_it->second) {
    200     if (flag == "-disable-free") {
    201       continue;
    202     } else if (flag == filename_placeholder) {
    203       result.push_back(filename.c_str());
    204     } else {
    205       result.push_back(flag.c_str());
    206     }
    207   }
    208   return result;
    209 }
    210 
    211 void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
    212                                   const std::set<CompilationType>& types,
    213                                   const std::unordered_map<Arch, CompilationRequirements>& reqs) {
    214   if (!cc1_flags.empty()) {
    215     errx(1, "reinitializing target CC1 flag cache?");
    216   }
    217 
    218   auto start = std::chrono::high_resolution_clock::now();
    219   std::vector<std::thread> threads;
    220   for (const CompilationType type : types) {
    221     threads.emplace_back([type, &vfs, &reqs]() {
    222       const auto& arch_req_it = reqs.find(type.arch);
    223       if (arch_req_it == reqs.end()) {
    224         errx(1, "CompilationRequirement map missing entry for CompilationType %s",
    225              to_string(type).c_str());
    226       }
    227 
    228       generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies);
    229     });
    230   }
    231   for (auto& thread : threads) {
    232     thread.join();
    233   }
    234   auto end = std::chrono::high_resolution_clock::now();
    235 
    236   if (verbose) {
    237     auto diff = (end - start) / 1.0ms;
    238     printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff);
    239   }
    240 
    241   if (cc1_flags.empty()) {
    242     errx(1, "failed to initialize target CC1 flag cache");
    243   }
    244 }
    245 
    246 void compileHeader(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
    247                    HeaderDatabase* header_database, CompilationType type,
    248                    const std::string& filename) {
    249   auto diags = constructDiags();
    250   std::vector<const char*> cc1_flags = getCC1Command(type, filename);
    251   auto invocation = std::make_unique<CompilerInvocation>();
    252   if (!CompilerInvocation::CreateFromArgs(*invocation.get(), &cc1_flags.front(),
    253                                           &cc1_flags.front() + cc1_flags.size(), *diags)) {
    254     errx(1, "failed to create CompilerInvocation");
    255   }
    256 
    257   clang::CompilerInstance Compiler;
    258 
    259 // Remove the workaround once b/35936936 is fixed.
    260 #if LLVM_VERSION_MAJOR >= 5
    261   Compiler.setInvocation(std::move(invocation));
    262 #else
    263   Compiler.setInvocation(invocation.release());
    264 #endif
    265 
    266   Compiler.setDiagnostics(diags.get());
    267   Compiler.setVirtualFileSystem(vfs);
    268 
    269   VersionerASTAction versioner_action(header_database, type);
    270   if (!Compiler.ExecuteAction(versioner_action)) {
    271     errx(1, "compilation generated warnings or errors");
    272   }
    273 
    274   if (diags->getNumWarnings() || diags->hasErrorOccurred()) {
    275     errx(1, "compilation generated warnings or errors");
    276   }
    277 }
    278