1 """distutils.util
2
3 Miscellaneous utility functions -- anything that doesn't fit into
4 one of the other *util.py modules.
5 """
6
7 import os
8 import re
9 import string
10 import sys
11 from distutils.errors import DistutilsPlatformError
12
13 def get_host_platform():
14 """Return a string that identifies the current platform. This is used mainly to
15 distinguish platform-specific build directories and platform-specific built
16 distributions. Typically includes the OS name and version and the
17 architecture (as supplied by 'os.uname()'), although the exact information
18 included depends on the OS; eg. on Linux, the kernel version isn't
19 particularly important.
20
21 Examples of returned values:
22 linux-i586
23 linux-alpha (?)
24 solaris-2.6-sun4u
25
26 Windows will return one of:
27 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
28 win32 (all others - specifically, sys.platform is returned)
29
30 For other non-POSIX platforms, currently just returns 'sys.platform'.
31
32 """
33 if os.name == 'nt':
34 if 'amd64' in sys.version.lower():
35 return 'win-amd64'
36 if '(arm)' in sys.version.lower():
37 return 'win-arm32'
38 if '(arm64)' in sys.version.lower():
39 return 'win-arm64'
40 return sys.platform
41
42 # Set for cross builds explicitly
43 if "_PYTHON_HOST_PLATFORM" in os.environ:
44 return os.environ["_PYTHON_HOST_PLATFORM"]
45
46 if os.name != "posix" or not hasattr(os, 'uname'):
47 # XXX what about the architecture? NT is Intel or Alpha,
48 # Mac OS is M68k or PPC, etc.
49 return sys.platform
50
51 # Try to distinguish various flavours of Unix
52
53 (osname, host, release, version, machine) = os.uname()
54
55 # Convert the OS name to lowercase, remove '/' characters, and translate
56 # spaces (for "Power Macintosh")
57 osname = osname.lower().replace('/', '')
58 machine = machine.replace(' ', '_')
59 machine = machine.replace('/', '-')
60
61 if osname[:5] == "linux":
62 # At least on Linux/Intel, 'machine' is the processor --
63 # i386, etc.
64 # XXX what about Alpha, SPARC, etc?
65 return "%s-%s" % (osname, machine)
66 elif osname[:5] == "sunos":
67 if release[0] >= "5": # SunOS 5 == Solaris 2
68 osname = "solaris"
69 release = "%d.%s" % (int(release[0]) - 3, release[2:])
70 # We can't use "platform.architecture()[0]" because a
71 # bootstrap problem. We use a dict to get an error
72 # if some suspicious happens.
73 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
74 machine += ".%s" % bitness[sys.maxsize]
75 # fall through to standard osname-release-machine representation
76 elif osname[:3] == "aix":
77 from _aix_support import aix_platform
78 return aix_platform()
79 elif osname[:6] == "cygwin":
80 osname = "cygwin"
81 rel_re = re.compile (r'[\d.]+', re.ASCII)
82 m = rel_re.match(release)
83 if m:
84 release = m.group()
85 elif osname[:6] == "darwin":
86 import _osx_support, sysconfig
87 osname, release, machine = _osx_support.get_platform_osx(
88 sysconfig.get_config_vars(),
89 osname, release, machine)
90
91 return "%s-%s-%s" % (osname, release, machine)
92
93 def get_platform():
94 if os.name == 'nt':
95 TARGET_TO_PLAT = {
96 'x86' : 'win32',
97 'x64' : 'win-amd64',
98 'arm' : 'win-arm32',
99 }
100 return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform()
101 else:
102 return get_host_platform()
103
104
105 # Needed by 'split_quoted()'
106 _wordchars_re = _squote_re = _dquote_re = None
107 def _init_regex():
108 global _wordchars_re, _squote_re, _dquote_re
109 _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
110 _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
111 _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
112
113 def split_quoted (s):
114 """Split a string up according to Unix shell-like rules for quotes and
115 backslashes. In short: words are delimited by spaces, as long as those
116 spaces are not escaped by a backslash, or inside a quoted string.
117 Single and double quotes are equivalent, and the quote characters can
118 be backslash-escaped. The backslash is stripped from any two-character
119 escape sequence, leaving only the escaped character. The quote
120 characters are stripped from any quoted string. Returns a list of
121 words.
122 """
123
124 # This is a nice algorithm for splitting up a single string, since it
125 # doesn't require character-by-character examination. It was a little
126 # bit of a brain-bender to get it working right, though...
127 if _wordchars_re is None: _init_regex()
128
129 s = s.strip()
130 words = []
131 pos = 0
132
133 while s:
134 m = _wordchars_re.match(s, pos)
135 end = m.end()
136 if end == len(s):
137 words.append(s[:end])
138 break
139
140 if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
141 words.append(s[:end]) # we definitely have a word delimiter
142 s = s[end:].lstrip()
143 pos = 0
144
145 elif s[end] == '\\': # preserve whatever is being escaped;
146 # will become part of the current word
147 s = s[:end] + s[end+1:]
148 pos = end+1
149
150 else:
151 if s[end] == "'": # slurp singly-quoted string
152 m = _squote_re.match(s, end)
153 elif s[end] == '"': # slurp doubly-quoted string
154 m = _dquote_re.match(s, end)
155 else:
156 raise RuntimeError("this can't happen (bad char '%c')" % s[end])
157
158 if m is None:
159 raise ValueError("bad string (mismatched %s quotes?)" % s[end])
160
161 (beg, end) = m.span()
162 s = s[:beg] + s[beg+1:end-1] + s[end:]
163 pos = m.end() - 2
164
165 if pos >= len(s):
166 words.append(s)
167 break
168
169 return words
170
171 # split_quoted ()