@@ -176,21 +176,37 @@ def _get_vc_env(plat_spec):
176176 return env
177177
178178
179- def _find_exe (exe , paths = None ):
179+ def _find_exe (exe , paths = None , clangcl = False ):
180180 """Return path to an MSVC executable program.
181181
182182 Tries to find the program in several places: first, one of the
183183 MSVC program search paths from the registry; next, the directories
184184 in the PATH environment variable. If any of those work, return an
185185 absolute path that is known to exist. If none of them work, just
186186 return the original program name, 'exe'.
187+
188+ If clangcl is set to true, look for the LLVM clang-cl executables,
189+ as well as look for them without the extension (eg. on Linux)
187190 """
191+ if clangcl :
192+ if exe == 'cl.exe' :
193+ exe = 'clang-{}' .format (exe )
194+ elif exe == 'link.exe' :
195+ exe = 'lld-{}' .format (exe )
196+ elif exe == 'mc.exe' :
197+ exe = 'llvm-ml.exe'
198+ else :
199+ exe = 'llvm-{}' .format (exe )
188200 if not paths :
189201 paths = os .getenv ('path' ).split (os .pathsep )
190202 for p in paths :
191203 fn = os .path .join (os .path .abspath (p ), exe )
192204 if os .path .isfile (fn ):
193205 return fn
206+ elif clangcl :
207+ fn = os .path .splitext (fn )[0 ]
208+ if os .path .isfile (fn ):
209+ return fn
194210 return exe
195211
196212
@@ -201,6 +217,32 @@ def _find_exe(exe, paths=None):
201217 'win-arm64' : 'arm64' ,
202218}
203219
220+ _clang_targets = {
221+ 'win32' : 'i686' ,
222+ 'win-amd64' : 'x86_64' ,
223+ 'win-arm32' : 'armv7' ,
224+ 'win-arm64' : 'aarch64' ,
225+ }
226+
227+
228+ def _get_external_sdk (linker = False ):
229+ sdk = []
230+ vctoolsdir = os .environ .get ('DISTUTILS_VCTOOLS_DIR' , None )
231+ winsdkdir = os .environ .get ('DISTUTILS_WINSDK_DIR' , None )
232+ if vctoolsdir :
233+ _vctoolsdir = ['/vctoolsdir' , vctoolsdir ]
234+ if linker :
235+ sdk .append (":" .join (_vctoolsdir ))
236+ else :
237+ sdk += _vctoolsdir
238+ if winsdkdir :
239+ _winsdkdir = ['/winsdkdir' , winsdkdir ]
240+ if linker :
241+ sdk .append (":" .join (_winsdkdir ))
242+ else :
243+ sdk += _winsdkdir
244+ return sdk
245+
204246
205247def _get_vcvars_spec (host_platform , platform ):
206248 """
@@ -298,14 +340,16 @@ def initialize(self, plat_name: str | None = None) -> None:
298340 )
299341 self ._configure (vc_env )
300342
343+ clangcl = True if self .compiler_type == "clangcl" else False
344+
301345 self ._paths = vc_env .get ('path' , '' )
302346 paths = self ._paths .split (os .pathsep )
303- self .cc = _find_exe ("cl.exe" , paths )
304- self .linker = _find_exe ("link.exe" , paths )
305- self .lib = _find_exe ("lib.exe" , paths )
306- self .rc = _find_exe ("rc.exe" , paths ) # resource compiler
307- self .mc = _find_exe ("mc.exe" , paths ) # message compiler
308- self .mt = _find_exe ("mt.exe" , paths ) # message compiler
347+ self .cc = _find_exe ("cl.exe" , paths , clangcl )
348+ self .linker = _find_exe ("link.exe" , paths , clangcl )
349+ self .lib = _find_exe ("lib.exe" , paths , clangcl )
350+ self .rc = _find_exe ("rc.exe" , paths , clangcl ) # resource compiler
351+ self .mc = _find_exe ("mc.exe" , paths , clangcl ) # message compiler
352+ self .mt = _find_exe ("mt.exe" , paths , clangcl ) # message compiler
309353
310354 self .preprocess_options = None
311355 # bpo-38597: Always compile with dynamic linking
@@ -326,6 +370,16 @@ def initialize(self, plat_name: str | None = None) -> None:
326370
327371 ldflags_debug = ['/nologo' , '/INCREMENTAL:NO' , '/LTCG' , '/DEBUG:FULL' ]
328372
373+ if clangcl :
374+ target = '--target={}-windows-msvc' .format (_clang_targets [plat_name ])
375+ compile_sdk = _get_external_sdk ()
376+ self .compile_options .remove ('/GL' )
377+ self .compile_options += ['/FA' , target ] + compile_sdk
378+ self .compile_options_debug += ['/FA' , target ] + compile_sdk
379+ linker_sdk = _get_external_sdk (linker = True )
380+ ldflags += linker_sdk
381+ ldflags_debug += linker_sdk
382+
329383 self .ldflags_exe = [* ldflags , '/MANIFEST:EMBED,ID=1' ]
330384 self .ldflags_exe_debug = [* ldflags_debug , '/MANIFEST:EMBED,ID=1' ]
331385 self .ldflags_shared = [
@@ -437,7 +491,11 @@ def compile( # noqa: C901
437491 rc_dir = os .path .dirname (obj )
438492 try :
439493 # first compile .MC to .RC and .H file
440- self .spawn ([self .mc , '-h' , h_dir , '-r' , rc_dir , src ])
494+ mc_cmd = [self .mc ]
495+ if clangcl and '64' in plat_name :
496+ mc_cmd .append ('--m64' )
497+ mc_cmd += ['-h' , h_dir , '-r' , rc_dir , src ]
498+ self .spawn (mc_cmd )
441499 base , _ = os .path .splitext (os .path .basename (src ))
442500 rc_file = os .path .join (rc_dir , base + '.rc' )
443501 # then compile .RC to .RES file
@@ -612,3 +670,8 @@ def find_library_file(self, dirs, lib, debug=False):
612670 else :
613671 # Oops, didn't find it in *any* of 'dirs'
614672 return None
673+
674+
675+ class ClangCLCompiler (Compiler ):
676+
677+ compiler_type = 'clangcl'
0 commit comments