1414import subprocess
1515import sys
1616import sysconfig
17- from distutils import log , spawn
17+ from distutils import spawn
1818from distutils .command import build_ext , install_data , install_lib
1919
2020from setuptools import Extension , setup
8181
8282
8383###############################################################################
84- def _find_msbuild_tool (tool = "msbuild.exe" , use_windows_sdk = False ):
85- """Return full path to one of the Microsoft build tools"""
86- # Search in PATH first
87- path = spawn .find_executable (tool )
88- if path :
89- return path
90-
91- # Search within registry to find build tools
92- try : # PY2
93- import _winreg as winreg
94- except ImportError : # PY3
95- import winreg
96-
97- keys_to_check = WIN_SDK_KEYS if use_windows_sdk else VS_KEYS
98- for rkey in keys_to_check :
99- try :
100- with winreg .OpenKey (winreg .HKEY_LOCAL_MACHINE , rkey .key ) as hkey :
101- val , type_ = winreg .QueryValueEx (hkey , rkey .value_name )
102- if type_ != winreg .REG_SZ :
103- continue
104- path = os .path .join (val , rkey .suffix , tool )
105- if os .path .exists (path ):
106- log .info ("Using {0} from {1}" .format (tool , rkey .sdk_name ))
107- return path
108- except WindowsError :
109- # Key doesn't exist
110- pass
111-
112- # Add Visual C++ for Python as a fall-back in case one
113- # of the other Windows SDKs isn't installed
114- if use_windows_sdk :
115- sdk_name = "Visual C++ for Python"
116- localappdata = os .environ ["LOCALAPPDATA" ]
117- suffix = "Bin\\ x64" if ARCH == "x64" else "Bin"
118- path = os .path .join (localappdata , vs_python , suffix , tool )
119- if os .path .exists (path ):
120- log .info ("Using {0} from {1}" .format (tool , sdk_name ))
121- return path
84+ def _check_output (* args , ** kwargs ):
85+ """Check output wrapper for py2/py3 compatibility"""
86+ output = subprocess .check_output (* args , ** kwargs )
87+ if PY_MAJOR == 2 :
88+ return output
89+ return output .decode ("ascii" )
12290
123- raise RuntimeError ("{0} could not be found" .format (tool ))
91+
92+ def _get_interop_filename ():
93+ """interopXX.cs is auto-generated as part of the build.
94+ For common windows platforms pre-generated files are included
95+ as most windows users won't have Clang installed, which is
96+ required to generate the file.
97+ """
98+ interop_filename = "interop{0}{1}{2}.cs" .format (
99+ PY_MAJOR , PY_MINOR , getattr (sys , "abiflags" , "" ))
100+ return os .path .join ("src" , "runtime" , interop_filename )
124101
125102
126103class BuildExtPythonnet (build_ext .build_ext ):
@@ -139,6 +116,7 @@ def build_extension(self, ext):
139116
140117 # Up to Python 3.2 sys.maxunicode is used to determine the size of
141118 # Py_UNICODE, but from 3.3 onwards Py_UNICODE is a typedef of wchar_t.
119+ # TODO: Is this doing the right check for Py27?
142120 if sys .version_info [:2 ] <= (3 , 2 ):
143121 unicode_width = 2 if sys .maxunicode < 0x10FFFF else 4
144122 else :
@@ -186,7 +164,7 @@ def build_extension(self, ext):
186164 subprocess .check_call ([sys .executable , geninterop , interop_file ])
187165
188166 if DEVTOOLS == "MsDev" :
189- _xbuild = '"{0}"' .format (_find_msbuild_tool ("msbuild.exe" ))
167+ _xbuild = '"{0}"' .format (self . _find_msbuild_tool ("msbuild.exe" ))
190168 _config = "{0}Win" .format (CONFIG )
191169
192170 elif DEVTOOLS == "Mono" :
@@ -220,14 +198,15 @@ def build_extension(self, ext):
220198 self ._build_monoclr ()
221199
222200 def _get_manifest (self , build_dir ):
223- if DEVTOOLS == "MsDev" and sys .version_info [:2 ] > (2 , 5 ):
224- mt = _find_msbuild_tool ("mt.exe" , use_windows_sdk = True )
225- manifest = os .path .abspath (os .path .join (build_dir , "app.manifest" ))
226- cmd = [mt , '-inputresource:"{0}"' .format (sys .executable ),
227- '-out:"{0}"' .format (manifest )]
228- self .announce ("Extracting manifest from {}" .format (sys .executable ))
229- subprocess .check_call (" " .join (cmd ), shell = False )
230- return manifest
201+ if DEVTOOLS != "MsDev" :
202+ return
203+ mt = self ._find_msbuild_tool ("mt.exe" , use_windows_sdk = True )
204+ manifest = os .path .abspath (os .path .join (build_dir , "app.manifest" ))
205+ cmd = [mt , '-inputresource:"{0}"' .format (sys .executable ),
206+ '-out:"{0}"' .format (manifest )]
207+ self .announce ("Extracting manifest from {}" .format (sys .executable ))
208+ subprocess .check_call (" " .join (cmd ), shell = False )
209+ return manifest
231210
232211 def _build_monoclr (self ):
233212 mono_libs = _check_output ("pkg-config --libs mono-2" , shell = True )
@@ -266,6 +245,50 @@ def _install_packages(self):
266245 self .announce ("Installing packages: {0}" .format (cmd ))
267246 subprocess .check_call (cmd , shell = use_shell )
268247
248+ def _find_msbuild_tool (self , tool = "msbuild.exe" , use_windows_sdk = False ):
249+ """Return full path to one of the Microsoft build tools"""
250+ # Search in PATH first
251+ path = spawn .find_executable (tool )
252+ if path :
253+ return path
254+
255+ # Search within registry to find build tools
256+ try : # PY2
257+ import _winreg as winreg
258+ except ImportError : # PY3
259+ import winreg
260+
261+ keys_to_check = WIN_SDK_KEYS if use_windows_sdk else VS_KEYS
262+ hklm = winreg .HKEY_LOCAL_MACHINE
263+ for rkey in keys_to_check :
264+ try :
265+ with winreg .OpenKey (hklm , rkey .key ) as hkey :
266+ val , type_ = winreg .QueryValueEx (hkey , rkey .value_name )
267+ if type_ != winreg .REG_SZ :
268+ continue
269+ path = os .path .join (val , rkey .suffix , tool )
270+ if os .path .exists (path ):
271+ self .announce ("Using {0} from {1}" .format (
272+ tool , rkey .sdk_name ))
273+ return path
274+ except WindowsError :
275+ # Key doesn't exist
276+ pass
277+
278+ # Add Visual C++ for Python as a fall-back in case one
279+ # of the other Windows SDKs isn't installed.
280+ # TODO: Extend checking by using setuptools/msvc.py?
281+ if use_windows_sdk :
282+ sdk_name = "Visual C++ for Python"
283+ localappdata = os .environ ["LOCALAPPDATA" ]
284+ suffix = "Bin\\ x64" if ARCH == "x64" else "Bin"
285+ path = os .path .join (localappdata , vs_python , suffix , tool )
286+ if os .path .exists (path ):
287+ self .announce ("Using {0} from {1}" .format (tool , sdk_name ))
288+ return path
289+
290+ raise RuntimeError ("{0} could not be found" .format (tool ))
291+
269292
270293class InstallLibPythonnet (install_lib .install_lib ):
271294 def install (self ):
@@ -304,25 +327,7 @@ def run(self):
304327 return install_data .install_data .run (self )
305328
306329
307- def _check_output (* args , ** kwargs ):
308- """Check output wrapper for py2/py3 compatibility"""
309- output = subprocess .check_output (* args , ** kwargs )
310- if PY_MAJOR == 2 :
311- return output
312- return output .decode ("ascii" )
313-
314-
315- def _get_interop_filename ():
316- """interopXX.cs is auto-generated as part of the build.
317- For common windows platforms pre-generated files are included
318- as most windows users won't have Clang installed, which is
319- required to generate the file.
320- """
321- interop_filename = "interop{0}{1}{2}.cs" .format (
322- PY_MAJOR , PY_MINOR , getattr (sys , "abiflags" , "" ))
323- return os .path .join ("src" , "runtime" , interop_filename )
324-
325-
330+ ###############################################################################
326331if __name__ == "__main__" :
327332 setupdir = os .path .dirname (__file__ )
328333 if setupdir :
0 commit comments