~ubuntu-branches/ubuntu/karmic/pypy/karmic

« back to all changes in this revision

Viewing changes to lib-python/2.4.1/distutils/util.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2007-04-13 09:33:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070413093309-yoojh4jcoocu2krz
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
__revision__ = "$Id: util.py,v 1.76 2004/07/18 06:14:42 tim_one Exp $"
 
8
 
 
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
 
14
 
 
15
def get_platform ():
 
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
 
23
    important.
 
24
 
 
25
    Examples of returned values:
 
26
       linux-i586
 
27
       linux-alpha (?)
 
28
       solaris-2.6-sun4u
 
29
       irix-5.3
 
30
       irix64-6.2
 
31
 
 
32
    For non-POSIX platforms, currently just returns 'sys.platform'.
 
33
    """
 
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.
 
37
        return sys.platform
 
38
 
 
39
    # Try to distinguish various flavours of Unix
 
40
 
 
41
    (osname, host, release, version, machine) = os.uname()
 
42
 
 
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, ' ', '_')
 
48
 
 
49
    if osname[:5] == "linux":
 
50
        # At least on Linux/Intel, 'machine' is the processor --
 
51
        # i386, etc.
 
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
 
56
            osname = "solaris"
 
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":
 
64
        osname = "cygwin"
 
65
        rel_re = re.compile (r'[\d.]+')
 
66
        m = rel_re.match(release)
 
67
        if m:
 
68
            release = m.group()
 
69
 
 
70
    return "%s-%s-%s" % (osname, release, machine)
 
71
 
 
72
# get_platform ()
 
73
 
 
74
 
 
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
 
82
    ends with a slash.
 
83
    """
 
84
    if os.sep == '/':
 
85
        return pathname
 
86
    if not pathname:
 
87
        return pathname
 
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
 
92
 
 
93
    paths = string.split(pathname, '/')
 
94
    while '.' in paths:
 
95
        paths.remove('.')
 
96
    if not paths:
 
97
        return os.curdir
 
98
    return apply(os.path.join, paths)
 
99
 
 
100
# convert_path ()
 
101
 
 
102
 
 
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.
 
108
    """
 
109
    if os.name == 'posix':
 
110
        if not os.path.isabs(pathname):
 
111
            return os.path.join(new_root, pathname)
 
112
        else:
 
113
            return os.path.join(new_root, pathname[1:])
 
114
 
 
115
    elif os.name == 'nt':
 
116
        (drive, path) = os.path.splitdrive(pathname)
 
117
        if path[0] == '\\':
 
118
            path = path[1:]
 
119
        return os.path.join(new_root, path)
 
120
 
 
121
    elif os.name == 'os2':
 
122
        (drive, path) = os.path.splitdrive(pathname)
 
123
        if path[0] == os.sep:
 
124
            path = path[1:]
 
125
        return os.path.join(new_root, path)
 
126
 
 
127
    elif os.name == 'mac':
 
128
        if not os.path.isabs(pathname):
 
129
            return os.path.join(new_root, pathname)
 
130
        else:
 
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)
 
135
 
 
136
    else:
 
137
        raise DistutilsPlatformError, \
 
138
              "nothing known about platform '%s'" % os.name
 
139
 
 
140
 
 
141
_environ_checked = 0
 
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()')
 
149
    """
 
150
    global _environ_checked
 
151
    if _environ_checked:
 
152
        return
 
153
 
 
154
    if os.name == 'posix' and not os.environ.has_key('HOME'):
 
155
        import pwd
 
156
        os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]
 
157
 
 
158
    if not os.environ.has_key('PLAT'):
 
159
        os.environ['PLAT'] = get_platform()
 
160
 
 
161
    _environ_checked = 1
 
162
 
 
163
 
 
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'.
 
172
    """
 
173
    check_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])
 
178
        else:
 
179
            return os.environ[var_name]
 
180
 
 
181
    try:
 
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
 
185
 
 
186
# subst_vars ()
 
187
 
 
188
 
 
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'.
 
196
    """
 
197
    # check for Python 1.5.2-style {IO,OS}Error exception objects
 
198
    if hasattr(exc, 'filename') and hasattr(exc, 'strerror'):
 
199
        if exc.filename:
 
200
            error = prefix + "%s: %s" % (exc.filename, exc.strerror)
 
201
        else:
 
202
            # two-argument functions in posix module don't
 
203
            # include the filename in the exception object!
 
204
            error = prefix + "%s" % exc.strerror
 
205
    else:
 
206
        error = prefix + str(exc[-1])
 
207
 
 
208
    return error
 
209
 
 
210
 
 
211
# Needed by 'split_quoted()'
 
212
_wordchars_re = _squote_re = _dquote_re = None
 
213
def _init_regex():
 
214
    global _wordchars_re, _squote_re, _dquote_re
 
215
    _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
 
216
    _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
 
217
    _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
 
218
 
 
219
def split_quoted (s):
 
220
    """Split a string up according to Unix shell-like rules for quotes and
 
221
    backslashes.  In short: words are delimited by spaces, as long as those
 
222
    spaces are not escaped by a backslash, or inside a quoted string.
 
223
    Single and double quotes are equivalent, and the quote characters can
 
224
    be backslash-escaped.  The backslash is stripped from any two-character
 
225
    escape sequence, leaving only the escaped character.  The quote
 
226
    characters are stripped from any quoted string.  Returns a list of
 
227
    words.
 
228
    """
 
229
 
 
230
    # This is a nice algorithm for splitting up a single string, since it
 
231
    # doesn't require character-by-character examination.  It was a little
 
232
    # bit of a brain-bender to get it working right, though...
 
233
    if _wordchars_re is None: _init_regex()
 
234
 
 
235
    s = string.strip(s)
 
236
    words = []
 
237
    pos = 0
 
238
 
 
239
    while s:
 
240
        m = _wordchars_re.match(s, pos)
 
241
        end = m.end()
 
242
        if end == len(s):
 
243
            words.append(s[:end])
 
244
            break
 
245
 
 
246
        if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
 
247
            words.append(s[:end])       # we definitely have a word delimiter
 
248
            s = string.lstrip(s[end:])
 
249
            pos = 0
 
250
 
 
251
        elif s[end] == '\\':            # preserve whatever is being escaped;
 
252
                                        # will become part of the current word
 
253
            s = s[:end] + s[end+1:]
 
254
            pos = end+1
 
255
 
 
256
        else:
 
257
            if s[end] == "'":           # slurp singly-quoted string
 
258
                m = _squote_re.match(s, end)
 
259
            elif s[end] == '"':         # slurp doubly-quoted string
 
260
                m = _dquote_re.match(s, end)
 
261
            else:
 
262
                raise RuntimeError, \
 
263
                      "this can't happen (bad char '%c')" % s[end]
 
264
 
 
265
            if m is None:
 
266
                raise ValueError, \
 
267
                      "bad string (mismatched %s quotes?)" % s[end]
 
268
 
 
269
            (beg, end) = m.span()
 
270
            s = s[:beg] + s[beg+1:end-1] + s[end:]
 
271
            pos = m.end() - 2
 
272
 
 
273
        if pos >= len(s):
 
274
            words.append(s)
 
275
            break
 
276
 
 
277
    return words
 
278
 
 
279
# split_quoted ()
 
280
 
 
281
 
 
282
def execute (func, args, msg=None, verbose=0, dry_run=0):
 
283
    """Perform some action that affects the outside world (eg.  by
 
284
    writing to the filesystem).  Such actions are special because they
 
285
    are disabled by the 'dry_run' flag.  This method takes care of all
 
286
    that bureaucracy for you; all you have to do is supply the
 
287
    function to call and an argument tuple for it (to embody the
 
288
    "external action" being performed), and an optional message to
 
289
    print.
 
290
    """
 
291
    if msg is None:
 
292
        msg = "%s%r" % (func.__name__, args)
 
293
        if msg[-2:] == ',)':        # correct for singleton tuple
 
294
            msg = msg[0:-2] + ')'
 
295
 
 
296
    log.info(msg)
 
297
    if not dry_run:
 
298
        apply(func, args)
 
299
 
 
300
 
 
301
def strtobool (val):
 
302
    """Convert a string representation of truth to true (1) or false (0).
 
303
 
 
304
    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
 
305
    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
 
306
    'val' is anything else.
 
307
    """
 
308
    val = string.lower(val)
 
309
    if val in ('y', 'yes', 't', 'true', 'on', '1'):
 
310
        return 1
 
311
    elif val in ('n', 'no', 'f', 'false', 'off', '0'):
 
312
        return 0
 
313
    else:
 
314
        raise ValueError, "invalid truth value %r" % (val,)
 
315
 
 
316
 
 
317
def byte_compile (py_files,
 
318
                  optimize=0, force=0,
 
319
                  prefix=None, base_dir=None,
 
320
                  verbose=1, dry_run=0,
 
321
                  direct=None):
 
322
    """Byte-compile a collection of Python source files to either .pyc
 
323
    or .pyo files in the same directory.  'py_files' is a list of files
 
324
    to compile; any files that don't end in ".py" are silently skipped.
 
325
    'optimize' must be one of the following:
 
326
      0 - don't optimize (generate .pyc)
 
327
      1 - normal optimization (like "python -O")
 
328
      2 - extra optimization (like "python -OO")
 
329
    If 'force' is true, all files are recompiled regardless of
 
330
    timestamps.
 
331
 
 
332
    The source filename encoded in each bytecode file defaults to the
 
333
    filenames listed in 'py_files'; you can modify these with 'prefix' and
 
334
    'basedir'.  'prefix' is a string that will be stripped off of each
 
335
    source filename, and 'base_dir' is a directory name that will be
 
336
    prepended (after 'prefix' is stripped).  You can supply either or both
 
337
    (or neither) of 'prefix' and 'base_dir', as you wish.
 
338
 
 
339
    If 'dry_run' is true, doesn't actually do anything that would
 
340
    affect the filesystem.
 
341
 
 
342
    Byte-compilation is either done directly in this interpreter process
 
343
    with the standard py_compile module, or indirectly by writing a
 
344
    temporary script and executing it.  Normally, you should let
 
345
    'byte_compile()' figure out to use direct compilation or not (see
 
346
    the source for details).  The 'direct' flag is used by the script
 
347
    generated in indirect mode; unless you know what you're doing, leave
 
348
    it set to None.
 
349
    """
 
350
 
 
351
    # First, if the caller didn't force us into direct or indirect mode,
 
352
    # figure out which mode we should be in.  We take a conservative
 
353
    # approach: choose direct mode *only* if the current interpreter is
 
354
    # in debug mode and optimize is 0.  If we're not in debug mode (-O
 
355
    # or -OO), we don't know which level of optimization this
 
356
    # interpreter is running with, so we can't do direct
 
357
    # byte-compilation and be certain that it's the right thing.  Thus,
 
358
    # always compile indirectly if the current interpreter is in either
 
359
    # optimize mode, or if either optimization level was requested by
 
360
    # the caller.
 
361
    if direct is None:
 
362
        direct = (__debug__ and optimize == 0)
 
363
 
 
364
    # "Indirect" byte-compilation: write a temporary script and then
 
365
    # run it with the appropriate flags.
 
366
    if not direct:
 
367
        try:
 
368
            from tempfile import mkstemp
 
369
            (script_fd, script_name) = mkstemp(".py")
 
370
        except ImportError:
 
371
            from tempfile import mktemp
 
372
            (script_fd, script_name) = None, mktemp(".py")
 
373
        log.info("writing byte-compilation script '%s'", script_name)
 
374
        if not dry_run:
 
375
            if script_fd is not None:
 
376
                script = os.fdopen(script_fd, "w")
 
377
            else:
 
378
                script = open(script_name, "w")
 
379
 
 
380
            script.write("""\
 
381
from distutils.util import byte_compile
 
382
files = [
 
383
""")
 
384
 
 
385
            # XXX would be nice to write absolute filenames, just for
 
386
            # safety's sake (script should be more robust in the face of
 
387
            # chdir'ing before running it).  But this requires abspath'ing
 
388
            # 'prefix' as well, and that breaks the hack in build_lib's
 
389
            # 'byte_compile()' method that carefully tacks on a trailing
 
390
            # slash (os.sep really) to make sure the prefix here is "just
 
391
            # right".  This whole prefix business is rather delicate -- the
 
392
            # problem is that it's really a directory, but I'm treating it
 
393
            # as a dumb string, so trailing slashes and so forth matter.
 
394
 
 
395
            #py_files = map(os.path.abspath, py_files)
 
396
            #if prefix:
 
397
            #    prefix = os.path.abspath(prefix)
 
398
 
 
399
            script.write(string.join(map(repr, py_files), ",\n") + "]\n")
 
400
            script.write("""
 
401
byte_compile(files, optimize=%r, force=%r,
 
402
             prefix=%r, base_dir=%r,
 
403
             verbose=%r, dry_run=0,
 
404
             direct=1)
 
405
""" % (optimize, force, prefix, base_dir, verbose))
 
406
 
 
407
            script.close()
 
408
 
 
409
        cmd = [sys.executable, script_name]
 
410
        if optimize == 1:
 
411
            cmd.insert(1, "-O")
 
412
        elif optimize == 2:
 
413
            cmd.insert(1, "-OO")
 
414
        spawn(cmd, dry_run=dry_run)
 
415
        execute(os.remove, (script_name,), "removing %s" % script_name,
 
416
                dry_run=dry_run)
 
417
 
 
418
    # "Direct" byte-compilation: use the py_compile module to compile
 
419
    # right here, right now.  Note that the script generated in indirect
 
420
    # mode simply calls 'byte_compile()' in direct mode, a weird sort of
 
421
    # cross-process recursion.  Hey, it works!
 
422
    else:
 
423
        from py_compile import compile
 
424
 
 
425
        for file in py_files:
 
426
            if file[-3:] != ".py":
 
427
                # This lets us be lazy and not filter filenames in
 
428
                # the "install_lib" command.
 
429
                continue
 
430
 
 
431
            # Terminology from the py_compile module:
 
432
            #   cfile - byte-compiled file
 
433
            #   dfile - purported source filename (same as 'file' by default)
 
434
            cfile = file + (__debug__ and "c" or "o")
 
435
            dfile = file
 
436
            if prefix:
 
437
                if file[:len(prefix)] != prefix:
 
438
                    raise ValueError, \
 
439
                          ("invalid prefix: filename %r doesn't start with %r"
 
440
                           % (file, prefix))
 
441
                dfile = dfile[len(prefix):]
 
442
            if base_dir:
 
443
                dfile = os.path.join(base_dir, dfile)
 
444
 
 
445
            cfile_base = os.path.basename(cfile)
 
446
            if direct:
 
447
                if force or newer(file, cfile):
 
448
                    log.info("byte-compiling %s to %s", file, cfile_base)
 
449
                    if not dry_run:
 
450
                        compile(file, cfile, dfile)
 
451
                else:
 
452
                    log.debug("skipping byte-compilation of %s to %s",
 
453
                              file, cfile_base)
 
454
 
 
455
# byte_compile ()
 
456
 
 
457
def rfc822_escape (header):
 
458
    """Return a version of the string escaped for inclusion in an
 
459
    RFC-822 header, by ensuring there are 8 spaces space after each newline.
 
460
    """
 
461
    lines = string.split(header, '\n')
 
462
    lines = map(string.strip, lines)
 
463
    header = string.join(lines, '\n' + 8*' ')
 
464
    return header