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
46 #include "Arch.h"
47 #include "DeclarationDatabase.h"
48 #include "versioner.h"
49
50 using namespace std::chrono_literals;
51 using namespace std::string_literals;
52
53 using namespace clang;
54
55 class VersionerASTConsumer : public clang::ASTConsumer {
56 public:
57 HeaderDatabase* header_database;
58 CompilationType type;
59
VersionerASTConsumer(HeaderDatabase * header_database,CompilationType type)60 VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type)
61 : header_database(header_database), type(type) {
62 }
63
HandleTranslationUnit(ASTContext & ctx)64 virtual void HandleTranslationUnit(ASTContext& ctx) override {
65 header_database->parseAST(type, ctx);
66 }
67 };
68
69 class VersionerASTAction : public clang::ASTFrontendAction {
70 public:
71 HeaderDatabase* header_database;
72 CompilationType type;
73
VersionerASTAction(HeaderDatabase * header_database,CompilationType type)74 VersionerASTAction(HeaderDatabase* header_database, CompilationType type)
75 : header_database(header_database), type(type) {
76 }
77
CreateASTConsumer(CompilerInstance &,llvm::StringRef)78 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override {
79 return std::make_unique<VersionerASTConsumer>(header_database, type);
80 }
81 };
82
constructDiags()83 static IntrusiveRefCntPtr<DiagnosticsEngine> constructDiags() {
84 IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
85 auto diag_printer = std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get());
86 IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
87 IntrusiveRefCntPtr<DiagnosticsEngine> diags(
88 new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release()));
89 return diags;
90 }
91
92 // clang's driver is slow compared to the work it performs to compile our headers.
93 // Run it once to generate flags for each target, and memoize the results.
94 static std::unordered_map<CompilationType, std::vector<std::string>> cc1_flags;
95 static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__";
generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,CompilationType type,const std::vector<std::string> & include_dirs)96 static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
97 CompilationType type,
98 const std::vector<std::string>& include_dirs) {
99 std::vector<std::string> cmd = { "versioner" };
100 cmd.push_back("-std=c11");
101 cmd.push_back("-x");
102 cmd.push_back("c-header");
103 cmd.push_back("-fsyntax-only");
104
105 cmd.push_back("-Wall");
106 cmd.push_back("-Wextra");
107 cmd.push_back("-Werror");
108 cmd.push_back("-Wundef");
109 cmd.push_back("-Wno-unused-macros");
110 cmd.push_back("-Wno-unused-function");
111 cmd.push_back("-Wno-unused-variable");
112 cmd.push_back("-Wno-unknown-attributes");
113 cmd.push_back("-Wno-pragma-once-outside-header");
114
115 cmd.push_back("-target");
116 cmd.push_back(arch_targets[type.arch]);
117
118 cmd.push_back("-DANDROID");
119 cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
120 cmd.push_back("-D_FORTIFY_SOURCE=2");
121 cmd.push_back("-D_GNU_SOURCE");
122 cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
123
124 cmd.push_back("-nostdinc");
125
126 if (add_include) {
127 cmd.push_back("-include");
128 cmd.push_back("android/versioning.h");
129 }
130
131 for (const auto& dir : include_dirs) {
132 cmd.push_back("-isystem");
133 cmd.push_back(dir);
134 }
135
136 cmd.push_back(filename_placeholder);
137
138 auto diags = constructDiags();
139 driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, vfs);
140 driver.setCheckInputsExist(false);
141
142 llvm::SmallVector<const char*, 32> driver_args;
143 for (const std::string& str : cmd) {
144 driver_args.push_back(str.c_str());
145 }
146
147 std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args));
148 const driver::JobList& jobs = Compilation->getJobs();
149 if (jobs.size() != 1) {
150 errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str());
151 }
152
153 const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin());
154 const driver::ArgStringList& cc_args = driver_cmd.getArguments();
155
156 if (cc_args.size() == 0) {
157 errx(1, "driver returned empty command for %s", to_string(type).c_str());
158 }
159
160 std::vector<std::string> result(cc_args.begin(), cc_args.end());
161
162 {
163 static std::mutex cc1_init_mutex;
164 std::unique_lock<std::mutex> lock(cc1_init_mutex);
165 if (cc1_flags.count(type) > 0) {
166 errx(1, "attemped to generate cc1 flags for existing CompilationType %s",
167 to_string(type).c_str());
168 }
169
170 cc1_flags.emplace(std::make_pair(type, std::move(result)));
171 }
172 }
173
getCC1Command(CompilationType type,const std::string & filename)174 static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) {
175 const auto& target_flag_it = cc1_flags.find(type);
176 if (target_flag_it == cc1_flags.end()) {
177 errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str());
178 }
179
180 std::vector<const char*> result;
181 for (const std::string& flag : target_flag_it->second) {
182 if (flag == "-disable-free") {
183 continue;
184 } else if (flag == filename_placeholder) {
185 result.push_back(filename.c_str());
186 } else {
187 result.push_back(flag.c_str());
188 }
189 }
190 return result;
191 }
192
initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,const std::set<CompilationType> & types,const std::unordered_map<Arch,CompilationRequirements> & reqs)193 void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
194 const std::set<CompilationType>& types,
195 const std::unordered_map<Arch, CompilationRequirements>& reqs) {
196 if (!cc1_flags.empty()) {
197 errx(1, "reinitializing target CC1 flag cache?");
198 }
199
200 auto start = std::chrono::high_resolution_clock::now();
201 std::vector<std::thread> threads;
202 for (const CompilationType type : types) {
203 threads.emplace_back([type, &vfs, &reqs]() {
204 const auto& arch_req_it = reqs.find(type.arch);
205 if (arch_req_it == reqs.end()) {
206 errx(1, "CompilationRequirement map missing entry for CompilationType %s",
207 to_string(type).c_str());
208 }
209
210 generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies);
211 });
212 }
213 for (auto& thread : threads) {
214 thread.join();
215 }
216 auto end = std::chrono::high_resolution_clock::now();
217
218 if (verbose) {
219 auto diff = (end - start) / 1.0ms;
220 printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff);
221 }
222
223 if (cc1_flags.empty()) {
224 errx(1, "failed to initialize target CC1 flag cache");
225 }
226 }
227
compileHeader(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,HeaderDatabase * header_database,CompilationType type,const std::string & filename)228 void compileHeader(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
229 HeaderDatabase* header_database, CompilationType type,
230 const std::string& filename) {
231 auto diags = constructDiags();
232 std::vector<const char*> cc1_flags = getCC1Command(type, filename);
233 auto invocation = std::make_unique<CompilerInvocation>();
234 if (!CompilerInvocation::CreateFromArgs(*invocation.get(), &cc1_flags.front(),
235 &cc1_flags.front() + cc1_flags.size(), *diags)) {
236 errx(1, "failed to create CompilerInvocation");
237 }
238
239 clang::CompilerInstance Compiler;
240 Compiler.setInvocation(invocation.release());
241 Compiler.setDiagnostics(diags.get());
242 Compiler.setVirtualFileSystem(vfs);
243
244 VersionerASTAction versioner_action(header_database, type);
245 if (!Compiler.ExecuteAction(versioner_action)) {
246 errx(1, "compilation generated warnings or errors");
247 }
248
249 if (diags->getNumWarnings() || diags->hasErrorOccurred()) {
250 errx(1, "compilation generated warnings or errors");
251 }
252 }
253