]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/distutils/util.py
   3 Miscellaneous utility functions -- anything that doesn't fit into 
   4 one of the other *util.py modules. 
   9 import sys
, os
, string
, re
 
  10 from distutils
.errors 
import DistutilsPlatformError
 
  11 from distutils
.dep_util 
import newer
 
  12 from distutils
.spawn 
import spawn
 
  13 from distutils 
import log
 
  16     """Return a string that identifies the current platform.  This is used 
  17     mainly to distinguish platform-specific build directories and 
  18     platform-specific built distributions.  Typically includes the OS name 
  19     and version and the architecture (as supplied by 'os.uname()'), 
  20     although the exact information included depends on the OS; eg. for IRIX 
  21     the architecture isn't particularly important (IRIX only runs on SGI 
  22     hardware), but for Linux the kernel version isn't particularly 
  25     Examples of returned values: 
  32     For non-POSIX platforms, currently just returns 'sys.platform'. 
  34     if os
.name 
!= "posix" or not hasattr(os
, 'uname'): 
  35         # XXX what about the architecture? NT is Intel or Alpha, 
  36         # Mac OS is M68k or PPC, etc. 
  39     # Try to distinguish various flavours of Unix 
  41     (osname
, host
, release
, version
, machine
) = os
.uname() 
  43     # Convert the OS name to lowercase, remove '/' characters 
  44     # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") 
  45     osname 
= string
.lower(osname
) 
  46     osname 
= string
.replace(osname
, '/', '') 
  47     machine 
= string
.replace(machine
, ' ', '_') 
  49     if osname
[:5] == "linux": 
  50         # At least on Linux/Intel, 'machine' is the processor -- 
  52         # XXX what about Alpha, SPARC, etc? 
  53         return  "%s-%s" % (osname
, machine
) 
  54     elif osname
[:5] == "sunos": 
  55         if release
[0] >= "5":           # SunOS 5 == Solaris 2 
  57             release 
= "%d.%s" % (int(release
[0]) - 3, release
[2:]) 
  58         # fall through to standard osname-release-machine representation 
  59     elif osname
[:4] == "irix":              # could be "irix64"! 
  60         return "%s-%s" % (osname
, release
) 
  61     elif osname
[:3] == "aix": 
  62         return "%s-%s.%s" % (osname
, version
, release
) 
  63     elif osname
[:6] == "cygwin": 
  65         rel_re 
= re
.compile (r
'[\d.]+') 
  66         m 
= rel_re
.match(release
) 
  70     return "%s-%s-%s" % (osname
, release
, machine
) 
  75 def convert_path (pathname
): 
  76     """Return 'pathname' as a name that will work on the native filesystem, 
  77     i.e. split it on '/' and put it back together again using the current 
  78     directory separator.  Needed because filenames in the setup script are 
  79     always supplied in Unix style, and have to be converted to the local 
  80     convention before we can actually use them in the filesystem.  Raises 
  81     ValueError on non-Unix-ish systems if 'pathname' either starts or 
  88     if pathname
[0] == '/': 
  89         raise ValueError, "path '%s' cannot be absolute" % pathname
 
  90     if pathname
[-1] == '/': 
  91         raise ValueError, "path '%s' cannot end with '/'" % pathname
 
  93     paths 
= string
.split(pathname
, '/') 
  98     return apply(os
.path
.join
, paths
) 
 103 def change_root (new_root
, pathname
): 
 104     """Return 'pathname' with 'new_root' prepended.  If 'pathname' is 
 105     relative, this is equivalent to "os.path.join(new_root,pathname)". 
 106     Otherwise, it requires making 'pathname' relative and then joining the 
 107     two, which is tricky on DOS/Windows and Mac OS. 
 109     if os
.name 
== 'posix': 
 110         if not os
.path
.isabs(pathname
): 
 111             return os
.path
.join(new_root
, pathname
) 
 113             return os
.path
.join(new_root
, pathname
[1:]) 
 115     elif os
.name 
== 'nt': 
 116         (drive
, path
) = os
.path
.splitdrive(pathname
) 
 119         return os
.path
.join(new_root
, path
) 
 121     elif os
.name 
== 'os2': 
 122         (drive
, path
) = os
.path
.splitdrive(pathname
) 
 123         if path
[0] == os
.sep
: 
 125         return os
.path
.join(new_root
, path
) 
 127     elif os
.name 
== 'mac': 
 128         if not os
.path
.isabs(pathname
): 
 129             return os
.path
.join(new_root
, pathname
) 
 131             # Chop off volume name from start of path 
 132             elements 
= string
.split(pathname
, ":", 1) 
 133             pathname 
= ":" + elements
[1] 
 134             return os
.path
.join(new_root
, pathname
) 
 137         raise DistutilsPlatformError
, \
 
 138               "nothing known about platform '%s'" % os
.name
 
 142 def check_environ (): 
 143     """Ensure that 'os.environ' has all the environment variables we 
 144     guarantee that users can use in config files, command-line options, 
 145     etc.  Currently this includes: 
 146       HOME - user's home directory (Unix only) 
 147       PLAT - description of the current platform, including hardware 
 148              and OS (see 'get_platform()') 
 150     global _environ_checked
 
 154     if os
.name 
== 'posix' and not os
.environ
.has_key('HOME'): 
 156         os
.environ
['HOME'] = pwd
.getpwuid(os
.getuid())[5] 
 158     if not os
.environ
.has_key('PLAT'): 
 159         os
.environ
['PLAT'] = get_platform() 
 164 def subst_vars (s
, local_vars
): 
 165     """Perform shell/Perl-style variable substitution on 'string'.  Every 
 166     occurrence of '$' followed by a name is considered a variable, and 
 167     variable is substituted by the value found in the 'local_vars' 
 168     dictionary, or in 'os.environ' if it's not in 'local_vars'. 
 169     'os.environ' is first checked/augmented to guarantee that it contains 
 170     certain values: see 'check_environ()'.  Raise ValueError for any 
 171     variables not found in either 'local_vars' or 'os.environ'. 
 174     def _subst (match
, local_vars
=local_vars
): 
 175         var_name 
= match
.group(1) 
 176         if local_vars
.has_key(var_name
): 
 177             return str(local_vars
[var_name
]) 
 179             return os
.environ
[var_name
] 
 182         return re
.sub(r
'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst
, s
) 
 183     except KeyError, var
: 
 184         raise ValueError, "invalid variable '$%s'" % var
 
 189 def grok_environment_error (exc
, prefix
="error: "): 
 190     """Generate a useful error message from an EnvironmentError (IOError or 
 191     OSError) exception object.  Handles Python 1.5.1 and 1.5.2 styles, and 
 192     does what it can to deal with exception objects that don't have a 
 193     filename (which happens when the error is due to a two-file operation, 
 194     such as 'rename()' or 'link()'.  Returns the error message as a string 
 195     prefixed with 'prefix'. 
 197     # check for Python 1.5.2-style {IO,OS}Error exception objects 
 198     if hasattr(exc
, 'filename') and hasattr(exc
, 'strerror'): 
 200             error 
= prefix 
+ "%s: %s" % (exc
.filename
, exc
.strerror
) 
 202             # two-argument functions in posix module don't 
 203             # include the filename in the exception object! 
 204             error 
= prefix 
+ "%s" % exc
.strerror
 
 206         error 
= prefix 
+ str(exc
[-1]) 
 211 # Needed by 'split_quoted()' 
 212 _wordchars_re 
= re
.compile(r
'[^\\\'\"%s ]*' % string.whitespace) 
 213 _squote_re = re.compile(r"'(?
:[^
'\\]|\\.)*'") 
 214 _dquote_re = re.compile(r'"(?
:[^
"\\]|\\.)*"') 
 216 def split_quoted (s): 
 217     """Split a string up according to Unix shell-like rules for quotes and 
 218     backslashes.  In short: words are delimited by spaces, as long as those 
 219     spaces are not escaped by a backslash, or inside a quoted string. 
 220     Single and double quotes are equivalent, and the quote characters can 
 221     be backslash-escaped.  The backslash is stripped from any two-character 
 222     escape sequence, leaving only the escaped character.  The quote 
 223     characters are stripped from any quoted string.  Returns a list of 
 227     # This is a nice algorithm for splitting up a single string, since it 
 228     # doesn't require character
-by
-character examination
.  It was a little
 
 229     # bit of a brain-bender to get it working right, though... 
 236         m 
= _wordchars_re
.match(s
, pos
) 
 239             words
.append(s
[:end
]) 
 242         if s
[end
] in string
.whitespace
: # unescaped, unquoted whitespace: now 
 243             words
.append(s
[:end
])       # we definitely have a word delimiter 
 244             s 
= string
.lstrip(s
[end
:]) 
 247         elif s
[end
] == '\\':            # preserve whatever is being escaped; 
 248                                         # will become part of the current word 
 249             s 
= s
[:end
] + s
[end
+1:] 
 253             if s
[end
] == "'":           # slurp singly-quoted string 
 254                 m 
= _squote_re
.match(s
, end
) 
 255             elif s
[end
] == '"':         # slurp doubly-quoted string 
 256                 m 
= _dquote_re
.match(s
, end
) 
 258                 raise RuntimeError, \
 
 259                       "this can't happen (bad char '%c')" % s
[end
] 
 263                       "bad string (mismatched %s quotes?)" % s
[end
] 
 265             (beg
, end
) = m
.span() 
 266             s 
= s
[:beg
] + s
[beg
+1:end
-1] + s
[end
:] 
 278 def execute (func
, args
, msg
=None, verbose
=0, dry_run
=0): 
 279     """Perform some action that affects the outside world (eg.  by 
 280     writing to the filesystem).  Such actions are special because they 
 281     are disabled by the 'dry_run' flag.  This method takes care of all 
 282     that bureaucracy for you; all you have to do is supply the 
 283     function to call and an argument tuple for it (to embody the 
 284     "external action" being performed), and an optional message to 
 288         msg 
= "%s%s" % (func
.__name
__, `args`
) 
 289         if msg
[-2:] == ',)':        # correct for singleton tuple 
 290             msg 
= msg
[0:-2] + ')' 
 298     """Convert a string representation of truth to true (1) or false (0). 
 300     True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values 
 301     are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if 
 302     'val' is anything else. 
 304     val 
= string
.lower(val
) 
 305     if val 
in ('y', 'yes', 't', 'true', 'on', '1'): 
 307     elif val 
in ('n', 'no', 'f', 'false', 'off', '0'): 
 310         raise ValueError, "invalid truth value %s" % `val`
 
 313 def byte_compile (py_files
, 
 315                   prefix
=None, base_dir
=None, 
 316                   verbose
=1, dry_run
=0, 
 318     """Byte-compile a collection of Python source files to either .pyc 
 319     or .pyo files in the same directory.  'py_files' is a list of files 
 320     to compile; any files that don't end in ".py" are silently skipped. 
 321     'optimize' must be one of the following: 
 322       0 - don't optimize (generate .pyc) 
 323       1 - normal optimization (like "python -O") 
 324       2 - extra optimization (like "python -OO") 
 325     If 'force' is true, all files are recompiled regardless of 
 328     The source filename encoded in each bytecode file defaults to the 
 329     filenames listed in 'py_files'; you can modify these with 'prefix' and 
 330     'basedir'.  'prefix' is a string that will be stripped off of each 
 331     source filename, and 'base_dir' is a directory name that will be 
 332     prepended (after 'prefix' is stripped).  You can supply either or both 
 333     (or neither) of 'prefix' and 'base_dir', as you wish. 
 335     If 'dry_run' is true, doesn't actually do anything that would 
 336     affect the filesystem. 
 338     Byte-compilation is either done directly in this interpreter process 
 339     with the standard py_compile module, or indirectly by writing a 
 340     temporary script and executing it.  Normally, you should let 
 341     'byte_compile()' figure out to use direct compilation or not (see 
 342     the source for details).  The 'direct' flag is used by the script 
 343     generated in indirect mode; unless you know what you're doing, leave 
 347     # First, if the caller didn't force us into direct or indirect mode, 
 348     # figure out which mode we should be in.  We take a conservative 
 349     # approach: choose direct mode *only* if the current interpreter is 
 350     # in debug mode and optimize is 0.  If we're not in debug mode (-O 
 351     # or -OO), we don't know which level of optimization this 
 352     # interpreter is running with, so we can't do direct 
 353     # byte-compilation and be certain that it's the right thing.  Thus, 
 354     # always compile indirectly if the current interpreter is in either 
 355     # optimize mode, or if either optimization level was requested by 
 358         direct 
= (__debug__ 
and optimize 
== 0) 
 360     # "Indirect" byte-compilation: write a temporary script and then 
 361     # run it with the appropriate flags. 
 364             from tempfile 
import mkstemp
 
 365             (script_fd
, script_name
) = mkstemp(".py") 
 367             from tempfile 
import mktemp
 
 368             (script_fd
, script_name
) = None, mktemp(".py") 
 369         log
.info("writing byte-compilation script '%s'", script_name
) 
 371             if script_fd 
is not None: 
 372                 script 
= os
.fdopen(script_fd
, "w") 
 374                 script 
= open(script_name
, "w") 
 377 from distutils.util import byte_compile 
 381             # XXX would be nice to write absolute filenames, just for 
 382             # safety's sake (script should be more robust in the face of 
 383             # chdir'ing before running it).  But this requires abspath'ing 
 384             # 'prefix' as well, and that breaks the hack in build_lib's 
 385             # 'byte_compile()' method that carefully tacks on a trailing 
 386             # slash (os.sep really) to make sure the prefix here is "just 
 387             # right".  This whole prefix business is rather delicate -- the 
 388             # problem is that it's really a directory, but I'm treating it 
 389             # as a dumb string, so trailing slashes and so forth matter. 
 391             #py_files = map(os.path.abspath, py_files) 
 393             #    prefix = os.path.abspath(prefix) 
 395             script
.write(string
.join(map(repr, py_files
), ",\n") + "]\n") 
 397 byte_compile(files, optimize=%s, force=%s, 
 398              prefix=%s, base_dir=%s, 
 399              verbose=%s, dry_run=0, 
 401 """ % (`optimize`
, `force`
, `prefix`
, `base_dir`
, `verbose`
)) 
 405         cmd 
= [sys
.executable
, script_name
] 
 410         spawn(cmd
, dry_run
=dry_run
) 
 411         execute(os
.remove
, (script_name
,), "removing %s" % script_name
, 
 414     # "Direct" byte-compilation: use the py_compile module to compile 
 415     # right here, right now.  Note that the script generated in indirect 
 416     # mode simply calls 'byte_compile()' in direct mode, a weird sort of 
 417     # cross-process recursion.  Hey, it works! 
 419         from py_compile 
import compile 
 421         for file in py_files
: 
 422             if file[-3:] != ".py": 
 423                 # This lets us be lazy and not filter filenames in 
 424                 # the "install_lib" command. 
 427             # Terminology from the py_compile module: 
 428             #   cfile - byte-compiled file 
 429             #   dfile - purported source filename (same as 'file' by default) 
 430             cfile 
= file + (__debug__ 
and "c" or "o") 
 433                 if file[:len(prefix
)] != prefix
: 
 435                           ("invalid prefix: filename %s doesn't start with %s" 
 436                            % (`
file`
, `prefix`
)) 
 437                 dfile 
= dfile
[len(prefix
):] 
 439                 dfile 
= os
.path
.join(base_dir
, dfile
) 
 441             cfile_base 
= os
.path
.basename(cfile
) 
 443                 if force 
or newer(file, cfile
): 
 444                     log
.info("byte-compiling %s to %s", file, cfile_base
) 
 446                         compile(file, cfile
, dfile
) 
 448                     log
.debug("skipping byte-compilation of %s to %s", 
 453 def rfc822_escape (header
): 
 454     """Return a version of the string escaped for inclusion in an 
 455     RFC-822 header, by ensuring there are 8 spaces space after each newline. 
 457     lines 
= string
.split(header
, '\n') 
 458     lines 
= map(string
.strip
, lines
) 
 459     header 
= string
.join(lines
, '\n' + 8*' ')