@@ -8,17 +8,16 @@ use anyhow::Result;
88
99use super :: types:: { CheckStatus , CppCheckResult , Suggestion , ToolInfo } ;
1010
11- /// Use vswhere.exe to locate cl.exe from the latest VS installation.
12- /// vswhere.exe ships with every VS 2017+ install and lives at a fixed path,
13- /// so this works even when the Developer Command Prompt has not been activated.
11+ /// Check whether Visual Studio (with any C++ workload) is installed by
12+ /// querying vswhere.exe.
1413#[ cfg( windows) ]
15- fn find_cl_exe_via_vswhere ( ) -> Option < String > {
14+ fn is_vs_installed ( ) -> bool {
1615 let vswhere = r"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" ;
1716 if !std:: path:: Path :: new ( vswhere) . exists ( ) {
18- return None ;
17+ return false ;
1918 }
2019
21- let vs_path = std:: process:: Command :: new ( vswhere)
20+ std:: process:: Command :: new ( vswhere)
2221 . args ( [
2322 "-latest" ,
2423 "-products" ,
@@ -31,24 +30,51 @@ fn find_cl_exe_via_vswhere() -> Option<String> {
3130 . output ( )
3231 . ok ( )
3332 . filter ( |o| o. status . success ( ) )
34- . map ( |o| String :: from_utf8_lossy ( & o. stdout ) . trim ( ) . to_string ( ) ) ?;
33+ . map ( |o| !String :: from_utf8_lossy ( & o. stdout ) . trim ( ) . is_empty ( ) )
34+ . unwrap_or ( false )
35+ }
3536
36- // Read the default MSVC toolset version from the version file.
37- let version_file =
38- format ! ( r"{}\VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt" , vs_path) ;
39- let msvc_ver = std:: fs:: read_to_string ( & version_file) . ok ( ) ?;
40- let msvc_ver = msvc_ver. trim ( ) ;
37+ #[ cfg( not( windows) ) ]
38+ fn is_vs_installed ( ) -> bool {
39+ false
40+ }
4141
42- let cl = format ! ( r"{}\VC\Tools\MSVC\{}\bin\Hostx64\x64\cl.exe" , vs_path, msvc_ver) ;
43- if std:: path:: Path :: new ( & cl) . exists ( ) {
44- Some ( cl)
42+ /// Use vswhere.exe to locate clang-cl.exe from the latest VS installation.
43+ /// clang-cl.exe is available when the "C++ Clang tools" component is installed.
44+ #[ cfg( windows) ]
45+ fn find_clang_cl_exe_via_vswhere ( ) -> Option < String > {
46+ let vswhere = r"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" ;
47+ if !std:: path:: Path :: new ( vswhere) . exists ( ) {
48+ return None ;
49+ }
50+
51+ let vs_path = std:: process:: Command :: new ( vswhere)
52+ . args ( [
53+ "-latest" ,
54+ "-products" ,
55+ "*" ,
56+ "-requires" ,
57+ "Microsoft.VisualStudio.Component.VC.Llvm.Clang" ,
58+ "-property" ,
59+ "installationPath" ,
60+ ] )
61+ . output ( )
62+ . ok ( )
63+ . filter ( |o| o. status . success ( ) )
64+ . map ( |o| String :: from_utf8_lossy ( & o. stdout ) . trim ( ) . to_string ( ) )
65+ . filter ( |s| !s. is_empty ( ) ) ?;
66+
67+ let clang_cl =
68+ format ! ( r"{}\VC\Tools\Llvm\x64\bin\clang-cl.exe" , vs_path) ;
69+ if std:: path:: Path :: new ( & clang_cl) . exists ( ) {
70+ Some ( clang_cl)
4571 } else {
4672 None
4773 }
4874}
4975
5076#[ cfg( not( windows) ) ]
51- fn find_cl_exe_via_vswhere ( ) -> Option < String > {
77+ fn find_clang_cl_exe_via_vswhere ( ) -> Option < String > {
5278 None
5379}
5480
@@ -308,40 +334,50 @@ pub fn check() -> Result<CppCheckResult> {
308334 } ) ;
309335 }
310336 } else if os == "windows" {
311- // On Windows, cl.exe is not in PATH unless running inside a Developer
312- // Command Prompt / Developer PowerShell. We use vswhere.exe (bundled
313- // with every VS installation) to locate it directly.
314- let cl_path = find_cl_exe_via_vswhere ( ) ;
337+ // Check whether Visual Studio is installed via vswhere.
338+ let has_vs = is_vs_installed ( ) ;
339+
340+ // Check clang-cl.exe (shipped with VS "C++ Clang tools" component).
341+ let clang_cl_path = find_clang_cl_exe_via_vswhere ( ) ;
315342
316- let msvc_check = if let Some ( ref path) = cl_path {
317- std:: process:: Command :: new ( path) . arg ( "/? " ) . output ( )
343+ let clang_cl_check = if let Some ( ref path) = clang_cl_path {
344+ std:: process:: Command :: new ( path) . arg ( "--version " ) . output ( )
318345 } else {
319346 // Fallback: maybe the user already has it in PATH.
320- std:: process:: Command :: new ( "cl.exe" ) . arg ( "/?" ) . output ( )
347+ std:: process:: Command :: new ( "clang-cl.exe" )
348+ . arg ( "--version" )
349+ . output ( )
321350 } ;
322351
323- match msvc_check {
324- Ok ( output) if output. status . success ( ) || !output. stderr . is_empty ( ) => {
325- // cl.exe prints version info to stderr, not stdout.
326- let version_str = String :: from_utf8_lossy ( & output. stderr ) ;
327- let version = version_str. lines ( ) . next ( ) . map ( |s| s. trim ( ) . to_string ( ) ) ;
328-
329- let resolved_path = cl_path. or_else ( || {
352+ match clang_cl_check {
353+ Ok ( output) if output. status . success ( ) => {
354+ let version_str =
355+ String :: from_utf8_lossy ( & output. stdout ) ;
356+ let version = version_str
357+ . lines ( )
358+ . next ( )
359+ . map ( |s| s. trim ( ) . to_string ( ) ) ;
360+
361+ let resolved_path = clang_cl_path. or_else ( || {
330362 std:: process:: Command :: new ( "where.exe" )
331- . arg ( "cl.exe" )
363+ . arg ( "clang- cl.exe" )
332364 . output ( )
333365 . ok ( )
334366 . and_then ( |o| {
335367 if o. status . success ( ) {
336- Some ( String :: from_utf8_lossy ( & o. stdout ) . trim ( ) . to_string ( ) )
368+ Some (
369+ String :: from_utf8_lossy ( & o. stdout )
370+ . trim ( )
371+ . to_string ( ) ,
372+ )
337373 } else {
338374 None
339375 }
340376 } )
341377 } ) ;
342378
343379 compilers. push ( ToolInfo {
344- name : "cl.exe (MSVC) " . to_string ( ) ,
380+ name : "clang- cl.exe" . to_string ( ) ,
345381 version,
346382 path : resolved_path,
347383 status : CheckStatus :: Ok ,
@@ -351,7 +387,7 @@ pub fn check() -> Result<CppCheckResult> {
351387 }
352388 _ => {
353389 compilers. push ( ToolInfo {
354- name : "cl.exe (MSVC) " . to_string ( ) ,
390+ name : "clang- cl.exe" . to_string ( ) ,
355391 version : None ,
356392 path : None ,
357393 status : CheckStatus :: Error ,
@@ -361,13 +397,37 @@ pub fn check() -> Result<CppCheckResult> {
361397 }
362398
363399 if !has_compiler {
364- suggestions. push ( Suggestion {
365- issue : "MSVC compiler not found" . to_string ( ) ,
366- command : Some ( "Install Visual Studio with C++ build tools" . to_string ( ) ) ,
367- help_text : Some (
368- "Please install Visual Studio with C++ development tools" . to_string ( ) ,
369- ) ,
370- } ) ;
400+ if has_vs {
401+ // VS is installed but the Clang component is missing.
402+ suggestions. push ( Suggestion {
403+ issue : "clang-cl.exe not found" . to_string ( ) ,
404+ command : None ,
405+ help_text : Some (
406+ "Visual Studio is installed but the C++ Clang tools \
407+ component is missing. Please open Visual Studio \
408+ Installer, click \" Modify\" , and enable the \
409+ \" C++ Clang tools\" component under \
410+ \" Individual components\" , then install it."
411+ . to_string ( ) ,
412+ ) ,
413+ } ) ;
414+ } else {
415+ // VS is not installed at all.
416+ suggestions. push ( Suggestion {
417+ issue : "clang-cl.exe not found" . to_string ( ) ,
418+ command : Some (
419+ "Install Visual Studio with C++ build tools"
420+ . to_string ( ) ,
421+ ) ,
422+ help_text : Some (
423+ "Please install Visual Studio with C++ development \
424+ tools. During installation, make sure to also enable \
425+ the \" C++ Clang tools\" component under \
426+ \" Individual components\" ."
427+ . to_string ( ) ,
428+ ) ,
429+ } ) ;
430+ }
371431 }
372432 }
373433
0 commit comments