blob: 4ccec54c7383e0bd6d537cb2c35f9c21f89e1f0e [file] [log] [blame]
Kadir Çetinkaya07692002024-02-28 07:10:05 +00001/*
2 * Copyright (C) 2024 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#include "analyzer.h"
17
18#include <memory>
19#include <string>
20#include <utility>
21#include <vector>
22
Michael Merg86cca742024-06-11 15:24:05 +000023#include "cc_analyzer.pb.h"
Kadir Çetinkaya07692002024-02-28 07:10:05 +000024#include "clang/Tooling/CompilationDatabase.h"
25#include "clang/Tooling/JSONCompilationDatabase.h"
Kadir Çetinkaya07692002024-02-28 07:10:05 +000026#include "include_scanner.h"
27#include "llvm/ADT/SmallString.h"
28#include "llvm/ADT/StringRef.h"
29#include "llvm/ADT/Twine.h"
30#include "llvm/Support/Error.h"
31#include "llvm/Support/Path.h"
32#include "llvm/Support/VirtualFileSystem.h"
33
34namespace tools::ide_query::cc_analyzer {
35namespace {
36llvm::Expected<std::unique_ptr<clang::tooling::CompilationDatabase>> LoadCompDB(
37 llvm::StringRef comp_db_path) {
38 std::string err;
39 std::unique_ptr<clang::tooling::CompilationDatabase> db =
40 clang::tooling::JSONCompilationDatabase::loadFromFile(
41 comp_db_path, err, clang::tooling::JSONCommandLineSyntax::AutoDetect);
42 if (!db) {
43 return llvm::createStringError(llvm::inconvertibleErrorCode(),
44 "Failed to load CDB: " + err);
45 }
46 // Provide some heuristic support for missing files.
47 return inferMissingCompileCommands(std::move(db));
48}
49} // namespace
50
Michael Merg86cca742024-06-11 15:24:05 +000051::cc_analyzer::DepsResponse GetDeps(::cc_analyzer::RepoState state) {
52 ::cc_analyzer::DepsResponse results;
Kadir Çetinkaya07692002024-02-28 07:10:05 +000053 auto db = LoadCompDB(state.comp_db_path());
54 if (!db) {
Michael Merg86cca742024-06-11 15:24:05 +000055 results.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
Kadir Çetinkaya07692002024-02-28 07:10:05 +000056 results.mutable_status()->set_message(llvm::toString(db.takeError()));
57 return results;
58 }
59 for (llvm::StringRef active_file : state.active_file_path()) {
60 auto& result = *results.add_deps();
61
62 llvm::SmallString<256> abs_file(state.repo_dir());
63 llvm::sys::path::append(abs_file, active_file);
64 auto cmds = db->get()->getCompileCommands(active_file);
65 if (cmds.empty()) {
Michael Merg86cca742024-06-11 15:24:05 +000066 result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
Kadir Çetinkaya07692002024-02-28 07:10:05 +000067 result.mutable_status()->set_message(
68 llvm::Twine("Can't find compile flags for file: ", abs_file).str());
69 continue;
70 }
71 result.set_source_file(active_file.str());
72 llvm::StringRef file = cmds[0].Filename;
73 if (llvm::StringRef actual_file(cmds[0].Heuristic);
74 actual_file.consume_front("inferred from ")) {
75 file = actual_file;
76 }
77 // TODO: Query ninja graph to figure out a minimal set of targets to build.
78 result.add_build_target(file.str() + "^");
79 }
80 return results;
81}
82
Michael Merg86cca742024-06-11 15:24:05 +000083::cc_analyzer::IdeAnalysis GetBuildInputs(::cc_analyzer::RepoState state) {
Kadir Çetinkaya07692002024-02-28 07:10:05 +000084 auto db = LoadCompDB(state.comp_db_path());
Michael Merg86cca742024-06-11 15:24:05 +000085 ::cc_analyzer::IdeAnalysis results;
Kadir Çetinkaya07692002024-02-28 07:10:05 +000086 if (!db) {
Michael Merg86cca742024-06-11 15:24:05 +000087 results.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
Kadir Çetinkaya07692002024-02-28 07:10:05 +000088 results.mutable_status()->set_message(llvm::toString(db.takeError()));
89 return results;
90 }
91 std::string repo_dir = state.repo_dir();
92 if (!repo_dir.empty() && repo_dir.back() == '/') repo_dir.pop_back();
93
94 llvm::SmallString<256> genfile_root_abs(repo_dir);
95 llvm::sys::path::append(genfile_root_abs, state.out_dir());
96 if (genfile_root_abs.empty() || genfile_root_abs.back() != '/') {
97 genfile_root_abs.push_back('/');
98 }
99
Kadir Çetinkaya07692002024-02-28 07:10:05 +0000100 for (llvm::StringRef active_file : state.active_file_path()) {
101 auto& result = *results.add_sources();
102 result.set_path(active_file.str());
103
104 llvm::SmallString<256> abs_file(repo_dir);
105 llvm::sys::path::append(abs_file, active_file);
106 auto cmds = db->get()->getCompileCommands(abs_file);
107 if (cmds.empty()) {
Michael Merg86cca742024-06-11 15:24:05 +0000108 result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
Kadir Çetinkaya07692002024-02-28 07:10:05 +0000109 result.mutable_status()->set_message(
110 llvm::Twine("Can't find compile flags for file: ", abs_file).str());
111 continue;
112 }
113 const auto& cmd = cmds.front();
114 llvm::StringRef working_dir = cmd.Directory;
115 if (!working_dir.consume_front(repo_dir)) {
Michael Merg86cca742024-06-11 15:24:05 +0000116 result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
Kadir Çetinkaya07692002024-02-28 07:10:05 +0000117 result.mutable_status()->set_message("Command working dir " +
118 working_dir.str() +
Michael Mergd8880ab2024-03-20 11:05:23 +0000119 " outside repository " + repo_dir);
Kadir Çetinkaya07692002024-02-28 07:10:05 +0000120 continue;
121 }
122 working_dir = working_dir.ltrim('/');
123 result.set_working_dir(working_dir.str());
124 for (auto& arg : cmd.CommandLine) result.add_compiler_arguments(arg);
125
126 auto includes =
127 ScanIncludes(cmds.front(), llvm::vfs::createPhysicalFileSystem());
128 if (!includes) {
Michael Merg86cca742024-06-11 15:24:05 +0000129 result.mutable_status()->set_code(::cc_analyzer::Status::FAILURE);
Kadir Çetinkaya07692002024-02-28 07:10:05 +0000130 result.mutable_status()->set_message(
131 llvm::toString(includes.takeError()));
132 continue;
133 }
134
135 for (auto& [req_input, contents] : *includes) {
136 llvm::StringRef req_input_ref(req_input);
137 // We're only interested in generated files.
138 if (!req_input_ref.consume_front(genfile_root_abs)) continue;
139 auto& genfile = *result.add_generated();
140 genfile.set_path(req_input_ref.str());
141 genfile.set_contents(std::move(contents));
142 }
143 }
144 return results;
145}
146} // namespace tools::ide_query::cc_analyzer