In Python, how do you determine whether the windows kernel is running in 32-bit or 64-bit mode?

C/C++の「クロスコンパイル」にご用で。

普段はこんなこと意識することはないんだけれど、「C/C++ で書かれたプログラムをクロスコンパイラでビルドしたい」となると、真面目にやらないといけない。

そもそも「amd64 ってナニよ」からしてこれまで考えないようにしてたんで、そっから。相変わらず wikipedia でイチぱつ:

x64またはx86-64とは、x86アーキテクチャを64ビットに拡張した命令セットアーキテクチャ。

実際には、AMDが発表したAMD64命令セット、続けてインテルが採用したIntel 64命令セット(かつてIA-32eまたはEM64Tと呼ばれていた)などを含む、各社のAMD64互換命令セットの総称である。x86命令セットと互換性を持っていることから、広義にはx86にx64を含む場合がある。

なお、インテルはIntel 64の他にIA-64の名前で64ビット命令セットアーキテクチャを開発・展開しているが、これはx64命令セット、x86命令セットのいずれとも互換性がない。

この一文だけでなくほかいくつか見た結果の非常に乱暴だが多分 87.6% くらいは正しい理解: 「世界は x86 と adm64 と ia64 だけで出来ている」。明らかに言い過ぎだろうがともあれこの三分法で実用上問題なさそうである。(少しだけ厳密には、ARM がいるので、この区別も必要だがこちらは多分問題なく区別出来るだろうと想像してる。)

と、ここまで理解出来たところで。

distutils にあるこれが初見でものすごーく違和感あったのね:

distutils/util.py
 1 def get_platform ():
 2     """Return a string that identifies the current platform.  This is used
 3     mainly to distinguish platform-specific build directories and
 4     platform-specific built distributions.  Typically includes the OS name
 5     and version and the architecture (as supplied by 'os.uname()'),
 6     although the exact information included depends on the OS; eg. for IRIX
 7     the architecture isn't particularly important (IRIX only runs on SGI
 8     hardware), but for Linux the kernel version isn't particularly
 9     important.
10 
11     Examples of returned values:
12        linux-i586
13        linux-alpha (?)
14        solaris-2.6-sun4u
15        irix-5.3
16        irix64-6.2
17 
18     Windows will return one of:
19        win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
20        win-ia64 (64bit Windows on Itanium)
21        win32 (all others - specifically, sys.platform is returned)
22 
23     For other non-POSIX platforms, currently just returns 'sys.platform'.
24     """
25     if os.name == 'nt':
26         # sniff sys.version for architecture.
27         prefix = " bit ("
28         i = sys.version.find(prefix)
29         if i == -1:
30             return sys.platform
31         j = sys.version.find(")", i)
32         look = sys.version[i+len(prefix):j].lower()
33         if look == 'amd64':
34             return 'win-amd64'
35         if look == 'itanium':
36             return 'win-ia64'
37         return sys.platform
38     # 以降 windows 以外のコードが続く

これは対話 python でお馴染みのこれ:

1 Python 3.5.3 (v3.5.3:1880cb95a742, Jan 16 2017, 16:02:32) [MSC v.1900 64 bit (AMD64)] on win32
2 Type "help", "copyright", "credits" or "license" for more information.
3 >>> import this

この「AMD64」を取ろうとしている…、のだが…。当然先に紹介した理解をする前に既に「ダメじゃね? これ」と思っていた。だってこの情報は「python がどんなビルドで作られたか」だもの。

まず、「おぬしの Windows は 64bit や否や?」を検出する一番簡単な方法はどうやらこれらしい:

1 >>> import os
2 >>> is_64 = ('PROGRAMFILES(X86)' in os.environ)
3 >>> is_64
4 True

すなわち、distutis 流儀ではこんなことになるのだ:

  • 64bit build on amd64, 64bit build on ia64 ⇒ sys.version だけで amd64 か ia64 かわかる。ヤッタネ。
  • 32bit build on x86 ⇒ sys.version から x86 とわかり、os.environ に ‘PROGRAMFILES(X86)’ がいない ⇒ win32 確定。まーべらす。
  • sys.version は x86 と主張するも、os.environ に ‘PROGRAMFILES(X86)’ がいる ⇒ amd64 なのか ia64 なのか不定。どっちやねん。

最後のケースでも、今のところ Windows に限れば「ほぼ十中八九 amd64 でどうやらいいようなんだけれど、だからといって「なかったことには出来ない」、とするならば、のハナシ。

しかるにタイトルにしたような検索をしてみたわけなのであった。これね。:

 1 import os
 2 import sys
 3 import platform
 4 
 5 def machine():
 6     """Return type of machine."""
 7     if os.name == 'nt' and sys.version_info[:2] < (2,7):
 8         return os.environ.get("PROCESSOR_ARCHITEW6432", 
 9                os.environ.get('PROCESSOR_ARCHITECTURE', ''))
10     else:
11         return platform.machine()
12 
13 def os_bits(machine=machine()):
14     """Return bitness of operating system, or None if unknown."""
15     machine2bits = {'AMD64': 64, 'x86_64': 64, 'i386': 32, 'x86': 32}
16     return machine2bits.get(machine, None)

注目すべきは os_bits() のほうではなくて machine() のほう。python 2.7 以降は platform.machine() で期待通りっぽいけれど、2.6 と 2.7 の初期では間違った情報返してたみたいね。該当のパッチがまさに、上で紹介の環境変数を見る方法なのね。

os_bits 内の翻訳部分は先の理解を不安にさせる。これが正しいなら IA64 を特定出来ないてことはない? 32bit版をわざわざインストールしてまで確認する気力はなくて、ちょっとどうしようかなぁと思うんだけれど、このコメントを信用してみたい:

platform.machine() can return anything the posix command uname -m returns. PROCESSOR_ARCHITECTURE can be AMD64, IA64 or x86. I think PROCESSOR_ARCHITEW6432 can only be AMD64 or empty. There might be new values for Windows on ARM. – phobie Sep 27 ’12 at 11:15

これが正しいならまさに期待通りのものだ。