Microsoft Visual C++ 14.0 standalone: Build Tools for Visual Studio 2017 と Python 3.5+ distutils

2.7で起こってたことと同じ状況なのね、今。

Microsoft Build Tools for Visual Studio 2017なんてのが出てる。名前からわかるように、とっても新しい。して、「setuptools は OK だが distutils はダメ」てのはまさに 2.7 で起こったのと同じこと。

どうにか distutils のままいけんかなぁ、と思うも、やっぱ抱え込んで書き換えるくらいしか術がない。3.5 のものに手を入れるならこんなだ:

diff -u Lib/distutils/_msvccompiler.py~ Lib/distutils/_msvccompiler.py
 1 --- Lib/distutils/_msvccompiler.py~	2016-06-25 23:03:50 +0900
 2 +++ Lib/distutils/_msvccompiler.py	2017-07-31 17:08:59 +0900
 3 @@ -35,8 +35,15 @@
 4              access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
 5          )
 6      except OSError:
 7 -        log.debug("Visual C++ is not registered")
 8 -        return None, None
 9 +        try:
10 +            key = winreg.OpenKeyEx(
11 +                winreg.HKEY_LOCAL_MACHINE,
12 +                r"Software\Wow6432Node\Microsoft\VisualStudio\SxS\VS7",
13 +                access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
14 +                )
15 +        except OSError:
16 +            log.debug("Visual C++ is not registered")
17 +            return None, None
18  
19      with key:
20          best_version = 0
21 @@ -44,7 +51,7 @@
22          for i in count():
23              try:
24                  v, vc_dir, vt = winreg.EnumValue(key, i)
25 -            except OSError:
26 +            except OSError as e:
27                  break
28              if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
29                  try:
30 @@ -59,8 +66,10 @@
31  
32          vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
33          if not os.path.isfile(vcvarsall):
34 -            log.debug("%s cannot be found", vcvarsall)
35 -            return None, None
36 +            vcvarsall = os.path.join(best_dir, "VC/Auxiliary/Build", "vcvarsall.bat")
37 +            if not os.path.isfile(vcvarsall):
38 +                log.debug("%s cannot be found", vcvarsall)
39 +                return None, None
40  
41          vcruntime = None
42          vcruntime_spec = _VCVARS_PLAT_TO_VCRUNTIME_REDIST.get(plat_spec)

あんまし distutils だけに拘る必要もない気はするけれども。誰か PR あげてないかなぁと探したが誰もやってない。かといって自分で上げるのはちと控えている。「必要最小限」がポリシーだと思うしさ、大歓迎されるようなもんではない気がする。てだけでなくてさすがに Python 本家に issue なり PR 上げるのって、相当気疲れしちゃうのよねぇ。(ましてやこの「パッチ」、猿んきーにもほどがある。)


18:20 追記:
かなり乱暴だけど、setuptools との組み合わせで:

 1 from distutils import ccompiler
 2 from distutils.errors import DistutilsPlatformError
 3 
 4 # If we have a cross platform compiler, and your native is
 5 # not "win32", this compiler will build for 32bit application
 6 # on 64bit host.
 7 platform = "win32"
 8 try:
 9     cc = ccompiler.new_compiler(platform)
10 except DistutilsPlatformError:
11     try:
12         from setuptools.msvc import msvc14_get_vc_env
13         from distutils import _msvccompiler
14         _msvccompiler._get_vc_env = msvc14_get_vc_env
15         cc = ccompiler.new_compiler(platform)
16     except ImportError:
17         os.environ["DISTUTILS_USE_SDK"] = "1"
18         cc = ccompiler.new_compiler(platform)

こんなんで Build Tools for Visual Studio 2017 も見にいく。これは setuptools が新しくしてあれば、そして 2.7 用の VC9 をインストールしてないなら、Python 2.7 からでも Build Tools for Visual Studio 2017 を使うことを意味する。「Python が使うためのもの」の目的にはそぐわないものの、「とにかく手持ちのコンパイラでブツが作れればいい」なら役に立つかもしれない。

なお、最後の DISTUTILS_USE_SDK はクロスコンパイルとしては機能しないので注意(指定した platform は無駄になる)。つまり vcvars64.bat から起動したコンソールからは 64bit 版しか作らないし、vcvars32.bat からは 32bit 版しか作らない。「クロスコンパイ「ラ」」のつもりではなく「クロスな切り替えは手作業で」のつもりならこれでも一応機能はするけれど。(つまり例えば .vcxproj の記述の「x64」ターゲットを真似してコマンドラインを構成しても、vcvars32.bat からは 32bit のまま、てこと。)


20:00 追記:
なーんだ、setuptools がかなり自動でやってくれるわ:

1 from distutils import ccompiler
2 from setuptools import monkey
3 monkey.patch_for_msvc_specialized_compiler()
4 
5 # If we have a cross platform compiler, and your native is
6 # not "win32", this compiler will build for 32bit application
7 # on 64bit host.
8 platform = "win32"
9 cc = ccompiler.new_compiler(platform)

この monkey を手でやらんといかんとは思ってない。多分何かをインポートすりゃ自動でやるんじゃないのかなと想像してる。これ、msvc.py をガン見してて見つけたって経緯なんでこんなことになってるケド。

これってひょっとしたら これの解決にもなるのかもしれん。