+++ /dev/null
-"""distutils.util\r
-\r
-Miscellaneous utility functions -- anything that doesn't fit into\r
-one of the other *util.py modules.\r
-"""\r
-\r
-__revision__ = "$Id$"\r
-\r
-import sys, os, string, re\r
-from distutils.errors import DistutilsPlatformError\r
-from distutils.dep_util import newer\r
-from distutils.spawn import spawn\r
-from distutils import log\r
-from distutils.errors import DistutilsByteCompileError\r
-\r
-def get_platform ():\r
- """Return a string that identifies the current platform. This is used\r
- mainly to distinguish platform-specific build directories and\r
- platform-specific built distributions. Typically includes the OS name\r
- and version and the architecture (as supplied by 'os.uname()'),\r
- although the exact information included depends on the OS; eg. for IRIX\r
- the architecture isn't particularly important (IRIX only runs on SGI\r
- hardware), but for Linux the kernel version isn't particularly\r
- important.\r
-\r
- Examples of returned values:\r
- linux-i586\r
- linux-alpha (?)\r
- solaris-2.6-sun4u\r
- irix-5.3\r
- irix64-6.2\r
-\r
- Windows will return one of:\r
- win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)\r
- win-ia64 (64bit Windows on Itanium)\r
- win32 (all others - specifically, sys.platform is returned)\r
-\r
- For other non-POSIX platforms, currently just returns 'sys.platform'.\r
- """\r
- if os.name == 'nt':\r
- # sniff sys.version for architecture.\r
- prefix = " bit ("\r
- i = string.find(sys.version, prefix)\r
- if i == -1:\r
- return sys.platform\r
- j = string.find(sys.version, ")", i)\r
- look = sys.version[i+len(prefix):j].lower()\r
- if look=='amd64':\r
- return 'win-amd64'\r
- if look=='itanium':\r
- return 'win-ia64'\r
- return sys.platform\r
-\r
- if os.name != "posix" or not hasattr(os, 'uname'):\r
- # XXX what about the architecture? NT is Intel or Alpha,\r
- # Mac OS is M68k or PPC, etc.\r
- return sys.platform\r
-\r
- # Try to distinguish various flavours of Unix\r
-\r
- (osname, host, release, version, machine) = os.uname()\r
-\r
- # Convert the OS name to lowercase, remove '/' characters\r
- # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")\r
- osname = string.lower(osname)\r
- osname = string.replace(osname, '/', '')\r
- machine = string.replace(machine, ' ', '_')\r
- machine = string.replace(machine, '/', '-')\r
-\r
- if osname[:5] == "linux":\r
- # At least on Linux/Intel, 'machine' is the processor --\r
- # i386, etc.\r
- # XXX what about Alpha, SPARC, etc?\r
- return "%s-%s" % (osname, machine)\r
- elif osname[:5] == "sunos":\r
- if release[0] >= "5": # SunOS 5 == Solaris 2\r
- osname = "solaris"\r
- release = "%d.%s" % (int(release[0]) - 3, release[2:])\r
- # fall through to standard osname-release-machine representation\r
- elif osname[:4] == "irix": # could be "irix64"!\r
- return "%s-%s" % (osname, release)\r
- elif osname[:3] == "aix":\r
- return "%s-%s.%s" % (osname, version, release)\r
- elif osname[:6] == "cygwin":\r
- osname = "cygwin"\r
- rel_re = re.compile (r'[\d.]+')\r
- m = rel_re.match(release)\r
- if m:\r
- release = m.group()\r
- elif osname[:6] == "darwin":\r
- #\r
- # For our purposes, we'll assume that the system version from\r
- # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set\r
- # to. This makes the compatibility story a bit more sane because the\r
- # machine is going to compile and link as if it were\r
- # MACOSX_DEPLOYMENT_TARGET.\r
- from distutils.sysconfig import get_config_vars\r
- cfgvars = get_config_vars()\r
-\r
- macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET')\r
-\r
- if 1:\r
- # Always calculate the release of the running machine,\r
- # needed to determine if we can build fat binaries or not.\r
-\r
- macrelease = macver\r
- # Get the system version. Reading this plist is a documented\r
- # way to get the system version (see the documentation for\r
- # the Gestalt Manager)\r
- try:\r
- f = open('/System/Library/CoreServices/SystemVersion.plist')\r
- except IOError:\r
- # We're on a plain darwin box, fall back to the default\r
- # behaviour.\r
- pass\r
- else:\r
- try:\r
- m = re.search(\r
- r'<key>ProductUserVisibleVersion</key>\s*' +\r
- r'<string>(.*?)</string>', f.read())\r
- if m is not None:\r
- macrelease = '.'.join(m.group(1).split('.')[:2])\r
- # else: fall back to the default behaviour\r
- finally:\r
- f.close()\r
-\r
- if not macver:\r
- macver = macrelease\r
-\r
- if macver:\r
- from distutils.sysconfig import get_config_vars\r
- release = macver\r
- osname = "macosx"\r
-\r
- if (macrelease + '.') >= '10.4.' and \\r
- '-arch' in get_config_vars().get('CFLAGS', '').strip():\r
- # The universal build will build fat binaries, but not on\r
- # systems before 10.4\r
- #\r
- # Try to detect 4-way universal builds, those have machine-type\r
- # 'universal' instead of 'fat'.\r
-\r
- machine = 'fat'\r
- cflags = get_config_vars().get('CFLAGS')\r
-\r
- archs = re.findall('-arch\s+(\S+)', cflags)\r
- archs = tuple(sorted(set(archs)))\r
-\r
- if len(archs) == 1:\r
- machine = archs[0]\r
- elif archs == ('i386', 'ppc'):\r
- machine = 'fat'\r
- elif archs == ('i386', 'x86_64'):\r
- machine = 'intel'\r
- elif archs == ('i386', 'ppc', 'x86_64'):\r
- machine = 'fat3'\r
- elif archs == ('ppc64', 'x86_64'):\r
- machine = 'fat64'\r
- elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'):\r
- machine = 'universal'\r
- else:\r
- raise ValueError(\r
- "Don't know machine value for archs=%r"%(archs,))\r
-\r
- elif machine == 'i386':\r
- # On OSX the machine type returned by uname is always the\r
- # 32-bit variant, even if the executable architecture is\r
- # the 64-bit variant\r
- if sys.maxint >= 2**32:\r
- machine = 'x86_64'\r
-\r
- elif machine in ('PowerPC', 'Power_Macintosh'):\r
- # Pick a sane name for the PPC architecture.\r
- machine = 'ppc'\r
-\r
- # See 'i386' case\r
- if sys.maxint >= 2**32:\r
- machine = 'ppc64'\r
-\r
- return "%s-%s-%s" % (osname, release, machine)\r
-\r
-# get_platform ()\r
-\r
-\r
-def convert_path (pathname):\r
- """Return 'pathname' as a name that will work on the native filesystem,\r
- i.e. split it on '/' and put it back together again using the current\r
- directory separator. Needed because filenames in the setup script are\r
- always supplied in Unix style, and have to be converted to the local\r
- convention before we can actually use them in the filesystem. Raises\r
- ValueError on non-Unix-ish systems if 'pathname' either starts or\r
- ends with a slash.\r
- """\r
- if os.sep == '/':\r
- return pathname\r
- if not pathname:\r
- return pathname\r
- if pathname[0] == '/':\r
- raise ValueError, "path '%s' cannot be absolute" % pathname\r
- if pathname[-1] == '/':\r
- raise ValueError, "path '%s' cannot end with '/'" % pathname\r
-\r
- paths = string.split(pathname, '/')\r
- while '.' in paths:\r
- paths.remove('.')\r
- if not paths:\r
- return os.curdir\r
- return os.path.join(*paths)\r
-\r
-# convert_path ()\r
-\r
-\r
-def change_root (new_root, pathname):\r
- """Return 'pathname' with 'new_root' prepended. If 'pathname' is\r
- relative, this is equivalent to "os.path.join(new_root,pathname)".\r
- Otherwise, it requires making 'pathname' relative and then joining the\r
- two, which is tricky on DOS/Windows and Mac OS.\r
- """\r
- if os.name == 'posix':\r
- if not os.path.isabs(pathname):\r
- return os.path.join(new_root, pathname)\r
- else:\r
- return os.path.join(new_root, pathname[1:])\r
-\r
- elif os.name == 'nt':\r
- (drive, path) = os.path.splitdrive(pathname)\r
- if path[0] == '\\':\r
- path = path[1:]\r
- return os.path.join(new_root, path)\r
-\r
- elif os.name == 'os2':\r
- (drive, path) = os.path.splitdrive(pathname)\r
- if path[0] == os.sep:\r
- path = path[1:]\r
- return os.path.join(new_root, path)\r
-\r
- else:\r
- raise DistutilsPlatformError, \\r
- "nothing known about platform '%s'" % os.name\r
-\r
-\r
-_environ_checked = 0\r
-def check_environ ():\r
- """Ensure that 'os.environ' has all the environment variables we\r
- guarantee that users can use in config files, command-line options,\r
- etc. Currently this includes:\r
- HOME - user's home directory (Unix only)\r
- PLAT - description of the current platform, including hardware\r
- and OS (see 'get_platform()')\r
- """\r
- global _environ_checked\r
- if _environ_checked:\r
- return\r
-\r
- if os.name == 'posix' and 'HOME' not in os.environ:\r
- import pwd\r
- os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]\r
-\r
- if 'PLAT' not in os.environ:\r
- os.environ['PLAT'] = get_platform()\r
-\r
- _environ_checked = 1\r
-\r
-\r
-def subst_vars (s, local_vars):\r
- """Perform shell/Perl-style variable substitution on 'string'. Every\r
- occurrence of '$' followed by a name is considered a variable, and\r
- variable is substituted by the value found in the 'local_vars'\r
- dictionary, or in 'os.environ' if it's not in 'local_vars'.\r
- 'os.environ' is first checked/augmented to guarantee that it contains\r
- certain values: see 'check_environ()'. Raise ValueError for any\r
- variables not found in either 'local_vars' or 'os.environ'.\r
- """\r
- check_environ()\r
- def _subst (match, local_vars=local_vars):\r
- var_name = match.group(1)\r
- if var_name in local_vars:\r
- return str(local_vars[var_name])\r
- else:\r
- return os.environ[var_name]\r
-\r
- try:\r
- return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)\r
- except KeyError, var:\r
- raise ValueError, "invalid variable '$%s'" % var\r
-\r
-# subst_vars ()\r
-\r
-\r
-def grok_environment_error (exc, prefix="error: "):\r
- """Generate a useful error message from an EnvironmentError (IOError or\r
- OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and\r
- does what it can to deal with exception objects that don't have a\r
- filename (which happens when the error is due to a two-file operation,\r
- such as 'rename()' or 'link()'. Returns the error message as a string\r
- prefixed with 'prefix'.\r
- """\r
- # check for Python 1.5.2-style {IO,OS}Error exception objects\r
- if hasattr(exc, 'filename') and hasattr(exc, 'strerror'):\r
- if exc.filename:\r
- error = prefix + "%s: %s" % (exc.filename, exc.strerror)\r
- else:\r
- # two-argument functions in posix module don't\r
- # include the filename in the exception object!\r
- error = prefix + "%s" % exc.strerror\r
- else:\r
- error = prefix + str(exc[-1])\r
-\r
- return error\r
-\r
-\r
-# Needed by 'split_quoted()'\r
-_wordchars_re = _squote_re = _dquote_re = None\r
-def _init_regex():\r
- global _wordchars_re, _squote_re, _dquote_re\r
- _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)\r
- _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")\r
- _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')\r
-\r
-def split_quoted (s):\r
- """Split a string up according to Unix shell-like rules for quotes and\r
- backslashes. In short: words are delimited by spaces, as long as those\r
- spaces are not escaped by a backslash, or inside a quoted string.\r
- Single and double quotes are equivalent, and the quote characters can\r
- be backslash-escaped. The backslash is stripped from any two-character\r
- escape sequence, leaving only the escaped character. The quote\r
- characters are stripped from any quoted string. Returns a list of\r
- words.\r
- """\r
-\r
- # This is a nice algorithm for splitting up a single string, since it\r
- # doesn't require character-by-character examination. It was a little\r
- # bit of a brain-bender to get it working right, though...\r
- if _wordchars_re is None: _init_regex()\r
-\r
- s = string.strip(s)\r
- words = []\r
- pos = 0\r
-\r
- while s:\r
- m = _wordchars_re.match(s, pos)\r
- end = m.end()\r
- if end == len(s):\r
- words.append(s[:end])\r
- break\r
-\r
- if s[end] in string.whitespace: # unescaped, unquoted whitespace: now\r
- words.append(s[:end]) # we definitely have a word delimiter\r
- s = string.lstrip(s[end:])\r
- pos = 0\r
-\r
- elif s[end] == '\\': # preserve whatever is being escaped;\r
- # will become part of the current word\r
- s = s[:end] + s[end+1:]\r
- pos = end+1\r
-\r
- else:\r
- if s[end] == "'": # slurp singly-quoted string\r
- m = _squote_re.match(s, end)\r
- elif s[end] == '"': # slurp doubly-quoted string\r
- m = _dquote_re.match(s, end)\r
- else:\r
- raise RuntimeError, \\r
- "this can't happen (bad char '%c')" % s[end]\r
-\r
- if m is None:\r
- raise ValueError, \\r
- "bad string (mismatched %s quotes?)" % s[end]\r
-\r
- (beg, end) = m.span()\r
- s = s[:beg] + s[beg+1:end-1] + s[end:]\r
- pos = m.end() - 2\r
-\r
- if pos >= len(s):\r
- words.append(s)\r
- break\r
-\r
- return words\r
-\r
-# split_quoted ()\r
-\r
-\r
-def execute (func, args, msg=None, verbose=0, dry_run=0):\r
- """Perform some action that affects the outside world (eg. by\r
- writing to the filesystem). Such actions are special because they\r
- are disabled by the 'dry_run' flag. This method takes care of all\r
- that bureaucracy for you; all you have to do is supply the\r
- function to call and an argument tuple for it (to embody the\r
- "external action" being performed), and an optional message to\r
- print.\r
- """\r
- if msg is None:\r
- msg = "%s%r" % (func.__name__, args)\r
- if msg[-2:] == ',)': # correct for singleton tuple\r
- msg = msg[0:-2] + ')'\r
-\r
- log.info(msg)\r
- if not dry_run:\r
- func(*args)\r
-\r
-\r
-def strtobool (val):\r
- """Convert a string representation of truth to true (1) or false (0).\r
-\r
- True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values\r
- are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if\r
- 'val' is anything else.\r
- """\r
- val = string.lower(val)\r
- if val in ('y', 'yes', 't', 'true', 'on', '1'):\r
- return 1\r
- elif val in ('n', 'no', 'f', 'false', 'off', '0'):\r
- return 0\r
- else:\r
- raise ValueError, "invalid truth value %r" % (val,)\r
-\r
-\r
-def byte_compile (py_files,\r
- optimize=0, force=0,\r
- prefix=None, base_dir=None,\r
- verbose=1, dry_run=0,\r
- direct=None):\r
- """Byte-compile a collection of Python source files to either .pyc\r
- or .pyo files in the same directory. 'py_files' is a list of files\r
- to compile; any files that don't end in ".py" are silently skipped.\r
- 'optimize' must be one of the following:\r
- 0 - don't optimize (generate .pyc)\r
- 1 - normal optimization (like "python -O")\r
- 2 - extra optimization (like "python -OO")\r
- If 'force' is true, all files are recompiled regardless of\r
- timestamps.\r
-\r
- The source filename encoded in each bytecode file defaults to the\r
- filenames listed in 'py_files'; you can modify these with 'prefix' and\r
- 'basedir'. 'prefix' is a string that will be stripped off of each\r
- source filename, and 'base_dir' is a directory name that will be\r
- prepended (after 'prefix' is stripped). You can supply either or both\r
- (or neither) of 'prefix' and 'base_dir', as you wish.\r
-\r
- If 'dry_run' is true, doesn't actually do anything that would\r
- affect the filesystem.\r
-\r
- Byte-compilation is either done directly in this interpreter process\r
- with the standard py_compile module, or indirectly by writing a\r
- temporary script and executing it. Normally, you should let\r
- 'byte_compile()' figure out to use direct compilation or not (see\r
- the source for details). The 'direct' flag is used by the script\r
- generated in indirect mode; unless you know what you're doing, leave\r
- it set to None.\r
- """\r
- # nothing is done if sys.dont_write_bytecode is True\r
- if sys.dont_write_bytecode:\r
- raise DistutilsByteCompileError('byte-compiling is disabled.')\r
-\r
- # First, if the caller didn't force us into direct or indirect mode,\r
- # figure out which mode we should be in. We take a conservative\r
- # approach: choose direct mode *only* if the current interpreter is\r
- # in debug mode and optimize is 0. If we're not in debug mode (-O\r
- # or -OO), we don't know which level of optimization this\r
- # interpreter is running with, so we can't do direct\r
- # byte-compilation and be certain that it's the right thing. Thus,\r
- # always compile indirectly if the current interpreter is in either\r
- # optimize mode, or if either optimization level was requested by\r
- # the caller.\r
- if direct is None:\r
- direct = (__debug__ and optimize == 0)\r
-\r
- # "Indirect" byte-compilation: write a temporary script and then\r
- # run it with the appropriate flags.\r
- if not direct:\r
- try:\r
- from tempfile import mkstemp\r
- (script_fd, script_name) = mkstemp(".py")\r
- except ImportError:\r
- from tempfile import mktemp\r
- (script_fd, script_name) = None, mktemp(".py")\r
- log.info("writing byte-compilation script '%s'", script_name)\r
- if not dry_run:\r
- if script_fd is not None:\r
- script = os.fdopen(script_fd, "w")\r
- else:\r
- script = open(script_name, "w")\r
-\r
- script.write("""\\r
-from distutils.util import byte_compile\r
-files = [\r
-""")\r
-\r
- # XXX would be nice to write absolute filenames, just for\r
- # safety's sake (script should be more robust in the face of\r
- # chdir'ing before running it). But this requires abspath'ing\r
- # 'prefix' as well, and that breaks the hack in build_lib's\r
- # 'byte_compile()' method that carefully tacks on a trailing\r
- # slash (os.sep really) to make sure the prefix here is "just\r
- # right". This whole prefix business is rather delicate -- the\r
- # problem is that it's really a directory, but I'm treating it\r
- # as a dumb string, so trailing slashes and so forth matter.\r
-\r
- #py_files = map(os.path.abspath, py_files)\r
- #if prefix:\r
- # prefix = os.path.abspath(prefix)\r
-\r
- script.write(string.join(map(repr, py_files), ",\n") + "]\n")\r
- script.write("""\r
-byte_compile(files, optimize=%r, force=%r,\r
- prefix=%r, base_dir=%r,\r
- verbose=%r, dry_run=0,\r
- direct=1)\r
-""" % (optimize, force, prefix, base_dir, verbose))\r
-\r
- script.close()\r
-\r
- cmd = [sys.executable, script_name]\r
- if optimize == 1:\r
- cmd.insert(1, "-O")\r
- elif optimize == 2:\r
- cmd.insert(1, "-OO")\r
- spawn(cmd, dry_run=dry_run)\r
- execute(os.remove, (script_name,), "removing %s" % script_name,\r
- dry_run=dry_run)\r
-\r
- # "Direct" byte-compilation: use the py_compile module to compile\r
- # right here, right now. Note that the script generated in indirect\r
- # mode simply calls 'byte_compile()' in direct mode, a weird sort of\r
- # cross-process recursion. Hey, it works!\r
- else:\r
- from py_compile import compile\r
-\r
- for file in py_files:\r
- if file[-3:] != ".py":\r
- # This lets us be lazy and not filter filenames in\r
- # the "install_lib" command.\r
- continue\r
-\r
- # Terminology from the py_compile module:\r
- # cfile - byte-compiled file\r
- # dfile - purported source filename (same as 'file' by default)\r
- cfile = file + (__debug__ and "c" or "o")\r
- dfile = file\r
- if prefix:\r
- if file[:len(prefix)] != prefix:\r
- raise ValueError, \\r
- ("invalid prefix: filename %r doesn't start with %r"\r
- % (file, prefix))\r
- dfile = dfile[len(prefix):]\r
- if base_dir:\r
- dfile = os.path.join(base_dir, dfile)\r
-\r
- cfile_base = os.path.basename(cfile)\r
- if direct:\r
- if force or newer(file, cfile):\r
- log.info("byte-compiling %s to %s", file, cfile_base)\r
- if not dry_run:\r
- compile(file, cfile, dfile)\r
- else:\r
- log.debug("skipping byte-compilation of %s to %s",\r
- file, cfile_base)\r
-\r
-# byte_compile ()\r
-\r
-def rfc822_escape (header):\r
- """Return a version of the string escaped for inclusion in an\r
- RFC-822 header, by ensuring there are 8 spaces space after each newline.\r
- """\r
- lines = string.split(header, '\n')\r
- header = string.join(lines, '\n' + 8*' ')\r
- return header\r