@@ -37,10 +37,44 @@ struct CompletedProcess {
37
37
}
38
38
};
39
39
40
+ } // namespace
41
+
42
+ static CompletedProcess runProcess (std::vector<std::string> &args,
43
+ const char *logContext) {
44
+ CompletedProcess out{.exitCode = EXIT_FAILURE,
45
+ .error = std::nullopt,
46
+ .stdoutLines = {},
47
+ .stderrLines = {}};
48
+ boost::process::ipstream stdoutStream, stderrStream;
49
+ BOOST_TRY {
50
+ spdlog::debug (" {}{}invoking '{}'" , logContext ? logContext : " " ,
51
+ logContext ? " by " : " " , fmt::join (args, " " ));
52
+ boost::process::child worker (args, boost::process::std_out > stdoutStream,
53
+ boost::process::std_err > stderrStream);
54
+ worker.wait ();
55
+ out.exitCode = worker.exit_code ();
56
+ }
57
+ BOOST_CATCH (boost::process::process_error & ex) {
58
+ out.error = ex;
59
+ }
60
+ BOOST_CATCH_END
61
+ std::string line;
62
+ while (std::getline (stdoutStream, line) && !line.empty ()) {
63
+ out.stdoutLines .push_back (line);
64
+ }
65
+ while (std::getline (stderrStream, line) && !line.empty ()) {
66
+ out.stderrLines .push_back (line);
67
+ }
68
+ return out;
69
+ }
70
+
71
+ namespace {
72
+
73
+ using AbsolutePath = scip_clang::AbsolutePath;
40
74
using ToolchainInfo = scip_clang::compdb::ToolchainInfo;
41
75
using CompilerKind = scip_clang::compdb::CompilerKind;
42
76
43
- struct ClangToolchainInfo : ToolchainInfo {
77
+ struct ClangToolchainInfo : public ToolchainInfo {
44
78
std::string resourceDir;
45
79
std::vector<std::string> findResourceDirInvocation;
46
80
std::string compilerDriverPath;
@@ -84,9 +118,57 @@ struct ClangToolchainInfo : ToolchainInfo {
84
118
commandLine.push_back (" -resource-dir" );
85
119
commandLine.push_back (this ->resourceDir );
86
120
}
121
+
122
+ static std::unique_ptr<ClangToolchainInfo>
123
+ tryInfer (const AbsolutePath &compilerPath) {
124
+ std::vector<std::string> findResourceDirInvocation = {
125
+ compilerPath.asStringRef (), " -print-resource-dir" };
126
+ auto printResourceDirResult = ::runProcess (
127
+ findResourceDirInvocation, " attempting to find resource dir" );
128
+ if (!printResourceDirResult.isSuccess ()) {
129
+ return nullptr ;
130
+ }
131
+ if (printResourceDirResult.stdoutLines .empty ()) {
132
+ spdlog::warn (" {} succeeded but returned an empty result" ,
133
+ fmt::join (findResourceDirInvocation, " " ));
134
+ return nullptr ;
135
+ }
136
+ auto resourceDir = std::string (
137
+ absl::StripAsciiWhitespace (printResourceDirResult.stdoutLines .front ()));
138
+ spdlog::debug (" got resource dir {} from {}" , resourceDir,
139
+ compilerPath.asStringRef ());
140
+
141
+ std::vector<std::string> findDriverInvocation = {compilerPath.asStringRef (),
142
+ " -###" };
143
+ auto hashHashHashResult = ::runProcess (
144
+ findDriverInvocation, " attempting to find installed directory" );
145
+ std::string compilerDriverPath = " " ;
146
+ if (hashHashHashResult.isSuccess ()) {
147
+ for (auto &line : hashHashHashResult.stderrLines ) {
148
+ auto clangDriverDir = absl::StripPrefix (line, " InstalledDir: " );
149
+ if (clangDriverDir.length () != line.length ()) {
150
+ compilerDriverPath = scip_clang::joinPath (
151
+ absl::StripAsciiWhitespace (clangDriverDir), " clang" );
152
+ spdlog::debug (" found compiler driver at {}" , compilerDriverPath);
153
+ break ;
154
+ }
155
+ }
156
+ }
157
+ if (compilerDriverPath.empty ()) {
158
+ spdlog::warn (
159
+ " failed to determine compiler path using -### for compiler at '{}'" ,
160
+ compilerPath.asStringRef ());
161
+ ToolchainInfo::logStdlibWarning ();
162
+ return nullptr ;
163
+ }
164
+
165
+ return std::make_unique<ClangToolchainInfo>(
166
+ resourceDir, findResourceDirInvocation, compilerDriverPath,
167
+ findDriverInvocation);
168
+ }
87
169
};
88
170
89
- struct GccToolchainInfo : ToolchainInfo {
171
+ struct GccToolchainInfo : public ToolchainInfo {
90
172
std::string installDir;
91
173
std::vector<std::string> findInstallDirInvocation;
92
174
@@ -116,13 +198,68 @@ struct GccToolchainInfo : ToolchainInfo {
116
198
// gcc-7 adds headers like limits.h and syslimits.h in include-fixed
117
199
commandLine.push_back (fmt::format (" -I{}/include-fixed" , this ->installDir ));
118
200
}
201
+
202
+ static std::unique_ptr<GccToolchainInfo>
203
+ tryInfer (const AbsolutePath &compilerPath) {
204
+ std::vector<std::string> findSearchDirsInvocation = {
205
+ compilerPath.asStringRef (), " -print-search-dirs" };
206
+ auto printSearchDirsResult = ::runProcess (findSearchDirsInvocation,
207
+ " attempting to find search dirs" );
208
+ if (!printSearchDirsResult.isSuccess ()) {
209
+ return nullptr ;
210
+ }
211
+ std::string installDir;
212
+ absl::c_any_of (printSearchDirsResult.stdoutLines ,
213
+ [&](const std::string &line) -> bool {
214
+ if (line.starts_with (" install:" )) {
215
+ installDir = absl::StripAsciiWhitespace (
216
+ absl::StripPrefix (line, " install:" ));
217
+ return true ;
218
+ }
219
+ return false ;
220
+ });
221
+ if (installDir.empty ()) {
222
+ spdlog::warn (
223
+ " missing 'install:' line in -print-search-dirs from GCC(-like?) {}" ,
224
+ compilerPath.asStringRef ());
225
+ ToolchainInfo::logStdlibWarning ();
226
+ return nullptr ;
227
+ }
228
+ spdlog::debug (" found gcc install directory at {}" , installDir);
229
+ return std::make_unique<GccToolchainInfo>(installDir,
230
+ findSearchDirsInvocation);
231
+ }
119
232
};
120
233
121
- struct NvccToolchainInfo : ToolchainInfo {
122
- scip_clang::AbsolutePath cudaDir;
234
+ struct NvccToolchainInfo : public ToolchainInfo {
235
+ AbsolutePath cudaDir;
236
+
237
+ // / Identify where the clang toolchain is based on PATH, if possible.
238
+ // / Without the appropriate Clang headers, it seems like the frontend
239
+ // / doesn't even construct the appropriate CUDAKernelCallExpr values.
240
+ std::unique_ptr<ClangToolchainInfo> clangInfo;
123
241
124
- NvccToolchainInfo (scip_clang::AbsolutePath cudaDir)
125
- : ToolchainInfo(), cudaDir(cudaDir) {}
242
+ NvccToolchainInfo (AbsolutePath cudaDir)
243
+ : ToolchainInfo(), cudaDir(cudaDir), clangInfo(nullptr ) {
244
+ // TODO: In principle, we could pick up Clang from -ccbin but that
245
+ // requires more plumbing; it would require using the -ccbin arg
246
+ // as part of the hash map key for toolchainInfoMap. So instead,
247
+ // for now, just require that the same Clang be available on PATH.
248
+ auto clangPath = boost::process::search_path (" clang" ).native ();
249
+ if (!clangPath.empty ()) {
250
+ auto clangAbsPath =
251
+ AbsolutePath (std::string (clangPath.data (), clangPath.size ()));
252
+ this ->clangInfo = ClangToolchainInfo::tryInfer (clangAbsPath);
253
+ }
254
+ if (clangInfo) {
255
+ return ;
256
+ }
257
+ spdlog::error (" clang not found on PATH; may be unable to locate headers "
258
+ " like __clang_cuda_runtime_wrapper.h" );
259
+ spdlog::warn (" code navigation for kernel call expressions may not work in "
260
+ " the absence of Clang CUDA headers" );
261
+ ToolchainInfo::logStdlibWarning ();
262
+ }
126
263
127
264
virtual CompilerKind kind () const override {
128
265
return CompilerKind::Nvcc;
@@ -147,139 +284,58 @@ struct NvccToolchainInfo : ToolchainInfo {
147
284
commandLine.push_back (
148
285
fmt::format (" -isystem{}{}include" , this ->cudaDir .asStringRef (),
149
286
std::filesystem::path::preferred_separator));
287
+ if (this ->clangInfo ) {
288
+ this ->clangInfo ->adjustCommandLine (commandLine);
289
+ }
290
+ }
291
+
292
+ static std::unique_ptr<NvccToolchainInfo>
293
+ tryInfer (const AbsolutePath &compilerPath) {
294
+ std::vector<std::string> argv = {compilerPath.asStringRef (), " --version" };
295
+ auto compilerVersionResult = ::runProcess (argv, " checking for NVCC" );
296
+ if (compilerVersionResult.isSuccess ()
297
+ && !compilerVersionResult.stdoutLines .empty ()
298
+ && absl::StrContains (compilerVersionResult.stdoutLines [0 ], " NVIDIA" )) {
299
+ if (auto binDir = compilerPath.asRef ().prefix ()) {
300
+ if (auto cudaDir = binDir->prefix ()) {
301
+ return std::make_unique<NvccToolchainInfo>(AbsolutePath (*cudaDir));
302
+ }
303
+ }
304
+ }
305
+ return nullptr ;
150
306
}
151
307
};
152
308
153
309
} // namespace
154
310
155
- static CompletedProcess runProcess (std::vector<std::string> &args,
156
- const char *logContext) {
157
- CompletedProcess out{.exitCode = EXIT_FAILURE,
158
- .error = std::nullopt,
159
- .stdoutLines = {},
160
- .stderrLines = {}};
161
- boost::process::ipstream stdoutStream, stderrStream;
162
- BOOST_TRY {
163
- spdlog::debug (" {}{}invoking '{}'" , logContext ? logContext : " " ,
164
- logContext ? " by " : " " , fmt::join (args, " " ));
165
- boost::process::child worker (args, boost::process::std_out > stdoutStream,
166
- boost::process::std_err > stderrStream);
167
- worker.wait ();
168
- out.exitCode = worker.exit_code ();
169
- }
170
- BOOST_CATCH (boost::process::process_error & ex) {
171
- out.error = ex;
172
- }
173
- BOOST_CATCH_END
174
- std::string line;
175
- while (std::getline (stdoutStream, line) && !line.empty ()) {
176
- out.stdoutLines .push_back (line);
177
- }
178
- while (std::getline (stderrStream, line) && !line.empty ()) {
179
- out.stderrLines .push_back (line);
180
- }
181
- return out;
311
+ /* static*/ void ToolchainInfo::logDiagnosticsHint () {
312
+ spdlog::info (" compilation errors are suppressed by default, but can be "
313
+ " turned on using --show-compiler-diagnostics" );
182
314
}
183
315
184
- /* static*/ std::unique_ptr<ToolchainInfo>
185
- ToolchainInfo::infer (const scip_clang::AbsolutePath &compilerPath) {
186
-
187
- auto noteStdlib = []() {
188
- spdlog::warn (" may be unable to locate standard library headers" );
189
- spdlog::info (" compilation errors are suppressed by default, but can be "
190
- " turned on using --show-compiler-diagnostics" );
191
- };
192
-
193
- std::vector<std::string> findResourceDirInvocation = {
194
- compilerPath.asStringRef (), " -print-resource-dir" };
195
- auto failure = std::unique_ptr<ToolchainInfo>(nullptr );
196
-
197
- auto printResourceDirResult = ::runProcess (findResourceDirInvocation,
198
- " attempting to find resource dir" );
199
- if (printResourceDirResult.isSuccess ()) {
200
- if (printResourceDirResult.stdoutLines .empty ()) {
201
- spdlog::warn (" {} succeeded but returned an empty result" ,
202
- fmt::join (findResourceDirInvocation, " " ));
203
- return failure;
204
- }
205
- auto resourceDir = std::string (
206
- absl::StripAsciiWhitespace (printResourceDirResult.stdoutLines .front ()));
207
- spdlog::debug (" got resource dir {} from {}" , resourceDir,
208
- compilerPath.asStringRef ());
209
-
210
- std::vector<std::string> findDriverInvocation = {compilerPath.asStringRef (),
211
- " -###" };
212
- auto hashHashHashResult = ::runProcess (
213
- findDriverInvocation, " attempting to find installed directory" );
214
- std::string compilerDriverPath = " " ;
215
- if (hashHashHashResult.isSuccess ()) {
216
- for (auto &line : hashHashHashResult.stderrLines ) {
217
- auto clangDriverDir = absl::StripPrefix (line, " InstalledDir: " );
218
- if (clangDriverDir.length () != line.length ()) {
219
- compilerDriverPath = scip_clang::joinPath (
220
- absl::StripAsciiWhitespace (clangDriverDir), " clang" );
221
- spdlog::debug (" found compiler driver at {}" , compilerDriverPath);
222
- break ;
223
- }
224
- }
225
- }
226
- if (compilerDriverPath.empty ()) {
227
- spdlog::warn (
228
- " failed to determine compiler path using -### for compiler at '{}'" ,
229
- compilerPath.asStringRef ());
230
- noteStdlib ();
231
- return failure;
232
- }
316
+ /* static*/ void ToolchainInfo::logStdlibWarning () {
317
+ spdlog::warn (" may be unable to locate standard library headers" );
318
+ ToolchainInfo::logDiagnosticsHint ();
319
+ }
233
320
234
- return std::make_unique<ClangToolchainInfo>(
235
- resourceDir, findResourceDirInvocation, compilerDriverPath,
236
- findDriverInvocation);
321
+ /* static*/ std::unique_ptr<ToolchainInfo>
322
+ ToolchainInfo::infer (const AbsolutePath &compilerPath) {
323
+ if (auto clangInfo = ClangToolchainInfo::tryInfer (compilerPath)) {
324
+ return clangInfo;
237
325
}
238
326
239
- std::vector<std::string> findSearchDirsInvocation = {
240
- compilerPath.asStringRef (), " -print-search-dirs" };
241
- auto printSearchDirsResult =
242
- ::runProcess (findSearchDirsInvocation, " attempting to find search dirs" );
243
- if (printSearchDirsResult.isSuccess ()) {
244
- std::string installDir;
245
- absl::c_any_of (printSearchDirsResult.stdoutLines ,
246
- [&](const std::string &line) -> bool {
247
- if (line.starts_with (" install:" )) {
248
- installDir = absl::StripAsciiWhitespace (
249
- absl::StripPrefix (line, " install:" ));
250
- return true ;
251
- }
252
- return false ;
253
- });
254
- if (installDir.empty ()) {
255
- spdlog::warn (
256
- " missing 'install:' line in -print-search-dirs from GCC(-like?) {}" ,
257
- compilerPath.asStringRef ());
258
- noteStdlib ();
259
- return failure;
260
- }
261
- spdlog::debug (" found gcc install directory at {}" , installDir);
262
- return std::make_unique<GccToolchainInfo>(installDir,
263
- findSearchDirsInvocation);
327
+ if (auto gccInfo = GccToolchainInfo::tryInfer (compilerPath)) {
328
+ return gccInfo;
264
329
}
265
330
266
- std::vector<std::string> argv = {compilerPath.asStringRef (), " --version" };
267
- auto compilerVersionResult = ::runProcess (argv, " checking for NVCC" );
268
- if (compilerVersionResult.isSuccess ()
269
- && !compilerVersionResult.stdoutLines .empty ()
270
- && absl::StrContains (compilerVersionResult.stdoutLines [0 ], " NVIDIA" )) {
271
- if (auto binDir = compilerPath.asRef ().prefix ()) {
272
- if (auto cudaDir = binDir->prefix ()) {
273
- return std::make_unique<NvccToolchainInfo>(
274
- scip_clang::AbsolutePath (*cudaDir));
275
- }
276
- }
331
+ if (auto nvccInfo = NvccToolchainInfo::tryInfer (compilerPath)) {
332
+ return nvccInfo;
277
333
}
278
334
279
335
spdlog::warn (" compiler at '{}' is not one of clang/clang++/gcc/g++/nvcc" ,
280
336
compilerPath.asStringRef ());
281
- noteStdlib ();
282
- return failure ;
337
+ ToolchainInfo::logStdlibWarning ();
338
+ return nullptr ;
283
339
}
284
340
285
341
namespace scip_clang {
0 commit comments