Skip to content

Commit 260c268

Browse files
authored
Fix: improve some case that user's environment do not have driver installed (#185)
1 parent 0bf09a8 commit 260c268

File tree

6 files changed

+157
-49
lines changed

6 files changed

+157
-49
lines changed

include/Compiler/Command.h

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
#pragma once
22

3+
#include "Support/Enum.h"
4+
#include "Support/Format.h"
35
#include <expected>
46
#include "llvm/ADT/DenseSet.h"
57
#include "llvm/ADT/StringMap.h"
68
#include "llvm/Support/Allocator.h"
79
#include "llvm/ADT/ArrayRef.h"
8-
#include <deque>
910

1011
namespace clice {
1112

13+
struct CommandOptions {
14+
/// Attach resource directory to the command.
15+
bool resource_dir = false;
16+
17+
/// Query the compiler driver for additional information, such as system includes and target.
18+
bool query_driver = false;
19+
20+
/// Suppress the warning log if failed to query driver info.
21+
/// Set true in unittests to avoid cluttering test output.
22+
bool suppress_log = false;
23+
};
24+
1225
class CompilationDatabase {
1326
public:
1427
using Self = CompilationDatabase;
@@ -49,6 +62,23 @@ class CompilationDatabase {
4962
std::vector<const char*> arguments;
5063
};
5164

65+
struct QueryDriverError {
66+
struct ErrorKind : refl::Enum<ErrorKind> {
67+
enum Kind : std::uint8_t {
68+
NotFoundInPATH,
69+
FailToCreateTempFile,
70+
InvokeDriverFail,
71+
OutputFileNotReadable,
72+
InvalidOutputFormat,
73+
};
74+
75+
using Enum::Enum;
76+
};
77+
78+
ErrorKind kind;
79+
std::string detail;
80+
};
81+
5282
CompilationDatabase();
5383

5484
auto save_string(this Self& self, llvm::StringRef string) -> llvm::StringRef;
@@ -61,7 +91,7 @@ class CompilationDatabase {
6191

6292
/// Query the compiler driver and return its driver info.
6393
auto query_driver(this Self& self, llvm::StringRef driver)
64-
-> std::expected<DriverInfo, std::string>;
94+
-> std::expected<DriverInfo, QueryDriverError>;
6595

6696
/// Update with arguments.
6797
auto update_command(this Self& self,
@@ -79,10 +109,8 @@ class CompilationDatabase {
79109
auto load_commands(this Self& self, llvm::StringRef json_content)
80110
-> std::expected<std::vector<UpdateInfo>, std::string>;
81111

82-
auto get_command(this Self& self,
83-
llvm::StringRef file,
84-
bool resource_dir = false,
85-
bool query_driver = false) -> LookupInfo;
112+
auto get_command(this Self& self, llvm::StringRef file, CommandOptions options = {})
113+
-> LookupInfo;
86114

87115
private:
88116
/// The memory pool to hold all cstring and command list.
@@ -132,3 +160,14 @@ struct DenseMapInfo<llvm::ArrayRef<const char*>> {
132160
};
133161

134162
} // namespace llvm
163+
164+
template <>
165+
struct std::formatter<clice::CompilationDatabase::QueryDriverError> :
166+
std::formatter<llvm::StringRef> {
167+
168+
template <typename FormatContext>
169+
auto format(clice::CompilationDatabase::QueryDriverError& e, FormatContext& ctx) const {
170+
return std::format_to(ctx.out(), "{} {}", e.kind.name(), e.detail);
171+
}
172+
};
173+

include/Test/Tester.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ struct Tester {
3636

3737
database.update_command("fake", src_path, command);
3838
params.kind = CompilationUnit::Content;
39-
params.arguments = database.get_command(src_path, true, true).arguments;
39+
40+
CommandOptions options;
41+
options.resource_dir = true;
42+
options.query_driver = true;
43+
options.suppress_log = true;
44+
params.arguments = database.get_command(src_path, options).arguments;
4045

4146
for(auto& [file, source]: sources.all_files) {
4247
if(file == src_path) {
@@ -67,7 +72,12 @@ struct Tester {
6772

6873
database.update_command("fake", src_path, command);
6974
params.kind = CompilationUnit::Preamble;
70-
params.arguments = database.get_command(src_path, true, true).arguments;
75+
76+
CommandOptions options;
77+
options.resource_dir = true;
78+
options.query_driver = true;
79+
options.suppress_log = true;
80+
params.arguments = database.get_command(src_path, options).arguments;
7181

7282
auto path = fs::createTemporaryFile("clice", "pch");
7383
if(!path) {

src/Compiler/Command.cpp

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -95,21 +95,42 @@ std::optional<std::uint32_t> CompilationDatabase::get_option_id(llvm::StringRef
9595
}
9696
}
9797

98+
namespace {
99+
100+
llvm::SmallVector<llvm::StringRef, 4> driver_invocation_argv(llvm::StringRef driver) {
101+
/// FIXME: MSVC command:` cl /Bv`, should we support it?
102+
/// if (driver.starts_with("gcc") || driver.starts_with("g++") || driver.starts_with("clang")) {
103+
/// return {"-E", "-v", "-xc++", "/dev/null"};
104+
/// } else if (driver.starts_with("cl") || driver.starts_with("clang-cl")) {
105+
/// return {"/Bv"};
106+
/// }
107+
return {driver, "-E", "-v", "-xc++", "/dev/null"};
108+
}
109+
110+
using QueryDriverError = CompilationDatabase::QueryDriverError;
111+
using ErrorKind = CompilationDatabase::QueryDriverError::ErrorKind;
112+
113+
auto unexpected(ErrorKind kind, std::string message) {
114+
return std::unexpected<QueryDriverError>({kind, std::move(message)});
115+
};
116+
117+
} // namespace
118+
98119
auto CompilationDatabase::query_driver(this Self& self, llvm::StringRef driver)
99-
-> std::expected<DriverInfo, std::string> {
100-
llvm::SmallString<128> buffer;
101-
102-
/// FIXME: Should we use a better way?
103-
if(auto error = fs::real_path(driver, buffer)) {
104-
auto result = llvm::sys::findProgramByName(driver);
105-
if(!result) {
106-
return std::unexpected(std::format("{}", result.getError()));
107-
} else {
108-
buffer = *result;
120+
-> std::expected<DriverInfo, QueryDriverError> {
121+
{
122+
/// FIXME: Should we use a better way?
123+
llvm::SmallString<128> absolute_path;
124+
if(auto error = fs::real_path(driver, absolute_path)) {
125+
auto result = llvm::sys::findProgramByName(driver);
126+
if(!result) {
127+
return unexpected(ErrorKind::NotFoundInPATH, result.getError().message());
128+
}
129+
absolute_path = *result;
109130
}
110-
}
111131

112-
driver = self.save_string(buffer);
132+
driver = self.save_string(absolute_path);
133+
}
113134

114135
auto it = self.driver_infos.find(driver.data());
115136
if(it != self.driver_infos.end()) {
@@ -120,18 +141,26 @@ auto CompilationDatabase::query_driver(this Self& self, llvm::StringRef driver)
120141

121142
llvm::SmallString<128> output_path;
122143
if(auto error = llvm::sys::fs::createTemporaryFile("system-includes", "clice", output_path)) {
123-
return std::unexpected(std::format("{}", error));
144+
return unexpected(ErrorKind::FailToCreateTempFile, error.message());
124145
}
125146

126-
auto clean_up = llvm::make_scope_exit([&output_path]() { fs::remove(output_path); });
147+
// If we fail to get the driver infomation, keep the output file for user to debug.
148+
bool keep_output_file = true;
149+
auto clean_up = llvm::make_scope_exit([&output_path, &keep_output_file]() {
150+
if(keep_output_file) {
151+
return;
152+
}
127153

128-
bool is_std_err = true;
154+
if(auto errc = llvm::sys::fs::remove(output_path)) {
155+
log::warn("Fail to remove temporary file: {}", errc.message());
156+
}
157+
});
129158

159+
bool is_std_err = true;
130160
std::optional<llvm::StringRef> redirects[] = {{""}, {""}, {""}};
131161
redirects[is_std_err ? 2 : 1] = output_path.str();
132162

133-
llvm::StringRef argv[] = {driver, "-E", "-v", "-xc++", "/dev/null"};
134-
163+
llvm::SmallVector argv = driver_invocation_argv(driver);
135164
std::string message;
136165
if(int RC = llvm::sys::ExecuteAndWait(driver,
137166
argv,
@@ -140,12 +169,12 @@ auto CompilationDatabase::query_driver(this Self& self, llvm::StringRef driver)
140169
/*SecondsToWait=*/0,
141170
/*MemoryLimit=*/0,
142171
&message)) {
143-
return std::unexpected(std::format("{}", message));
172+
return unexpected(ErrorKind::InvokeDriverFail, std::move(message));
144173
}
145174

146175
auto file = llvm::MemoryBuffer::getFile(output_path);
147176
if(!file) {
148-
return std::unexpected(std::format("{}", file.getError()));
177+
return unexpected(ErrorKind::OutputFileNotReadable, file.getError().message());
149178
}
150179

151180
llvm::StringRef content = file.get()->getBuffer();
@@ -191,13 +220,16 @@ auto CompilationDatabase::query_driver(this Self& self, llvm::StringRef driver)
191220
}
192221

193222
if(!found_start_marker) {
194-
return std::unexpected("Start marker not found...");
223+
return unexpected(ErrorKind::InvalidOutputFormat, "Start marker not found...");
195224
}
196225

197226
if(in_includes_block) {
198-
return std::unexpected("End marker not found...");
227+
return unexpected(ErrorKind::InvalidOutputFormat, "End marker not found...");
199228
}
200229

230+
// Get driver information success, remove temporary file.
231+
keep_output_file = false;
232+
201233
llvm::SmallVector<const char*, 8> includes;
202234
for(auto include: system_includes) {
203235
llvm::SmallString<64> buffer;
@@ -415,10 +447,8 @@ auto CompilationDatabase::load_commands(this Self& self, llvm::StringRef json_co
415447
return infos;
416448
}
417449

418-
auto CompilationDatabase::get_command(this Self& self,
419-
llvm::StringRef file,
420-
bool resource_dir,
421-
bool query_driver) -> LookupInfo {
450+
auto CompilationDatabase::get_command(this Self& self, llvm::StringRef file, CommandOptions options)
451+
-> LookupInfo {
422452
LookupInfo info;
423453

424454
file = self.save_string(file);
@@ -439,8 +469,9 @@ auto CompilationDatabase::get_command(this Self& self,
439469
info.arguments.emplace_back(self.save_string(argument).data());
440470
};
441471

442-
if(query_driver) {
443-
if(auto driver_info = self.query_driver(info.arguments[0])) {
472+
if(options.query_driver) {
473+
llvm::StringRef driver = info.arguments[0];
474+
if(auto driver_info = self.query_driver(driver)) {
444475
append_argument("-nostdlibinc");
445476

446477
/// FIXME: Use target information here, this is useful for cross compilation.
@@ -450,18 +481,16 @@ auto CompilationDatabase::get_command(this Self& self,
450481
append_argument("-I");
451482
append_argument(system_header);
452483
}
453-
} else {
454-
/// FIXME: Error handle here.
455-
log::warn("Fail query info for {}, because", info.arguments[0], driver_info.error());
484+
} else if(!options.suppress_log) {
485+
log::warn("Failed to query driver:{}, error:{}", driver, driver_info.error());
456486
}
457487
}
458488

459-
if(resource_dir) {
489+
if(options.resource_dir) {
460490
append_argument(std::format("-resource-dir={}", fs::resource_dir));
461491
}
462492

463493
info.arguments.emplace_back(file.data());
464-
465494
return info;
466495
}
467496

src/Server/Document.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ void Server::save_cache_info() {
110110
}
111111

112112
auto clean_up = llvm::make_scope_exit([&temp_path]() {
113-
if(auto errc = llvm::sys::fs::remove(temp_path); errc != std::error_code{}) {
113+
if(auto errc = llvm::sys::fs::remove(temp_path)) {
114114
log::warn("Fail to remove temporary file: {}", errc.message());
115115
}
116116
});
@@ -245,8 +245,12 @@ async::Task<bool> build_pch_task(CompilationDatabase::LookupInfo& info,
245245
} // namespace
246246

247247
async::Task<bool> Server::build_pch(std::string file, std::string content) {
248+
CommandOptions options;
249+
options.resource_dir = true;
250+
options.query_driver = true;
251+
auto info = database.get_command(file, options);
252+
248253
auto bound = compute_preamble_bound(content);
249-
auto info = database.get_command(file, true, true);
250254
auto& open_file = opening_files.get_or_add(file);
251255

252256
/// Check update ...
@@ -299,9 +303,13 @@ async::Task<> Server::build_ast(std::string path, std::string content) {
299303
log::fatal("Expected PCH built at this point");
300304
}
301305

306+
CommandOptions options;
307+
options.resource_dir = true;
308+
options.query_driver = true;
309+
302310
CompilationParams params;
303311
params.kind = CompilationUnit::Content;
304-
params.arguments = database.get_command(path, true, true).arguments;
312+
params.arguments = database.get_command(path, options).arguments;
305313
params.add_remapped_file(path, content);
306314
params.pch = {pch->path, pch->preamble.size()};
307315
file->diagnostics->clear();

src/Server/Feature.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ auto Server::on_completion(proto::CompletionParams params) -> Result {
2525
auto offset = to_offset(kind, content, params.position);
2626
auto& pch = opening_file->pch;
2727
{
28+
CommandOptions options;
29+
options.resource_dir = true;
30+
2831
/// Set compilation params ... .
2932
CompilationParams params;
3033
params.kind = CompilationUnit::Completion;
31-
params.arguments = database.get_command(path, true).arguments;
34+
params.arguments = database.get_command(path).arguments;
3235
params.add_remapped_file(path, content);
3336
params.pch = {pch->path, pch->preamble.size()};
3437
params.completion = {path, offset};
@@ -79,10 +82,13 @@ async::Task<json::Value> Server::on_signature_help(proto::SignatureHelpParams pa
7982
auto offset = to_offset(kind, content, params.position);
8083
auto& pch = opening_file->pch;
8184
{
85+
CommandOptions options;
86+
options.resource_dir = true;
87+
8288
/// Set compilation params ... .
8389
CompilationParams params;
8490
params.kind = CompilationUnit::Completion;
85-
params.arguments = database.get_command(path, true).arguments;
91+
params.arguments = database.get_command(path, options).arguments;
8692
params.add_remapped_file(path, content);
8793
params.pch = {pch->path, pch->preamble.size()};
8894
params.completion = {path, offset};

0 commit comments

Comments
 (0)