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