• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Provide access to Python's configuration information.  The specific
2configuration variables available depend heavily on the platform and
3configuration.  The values may be retrieved using
4get_config_var(name), and the list of variables is available via
5get_config_vars().keys().  Additional convenience functions are also
6available.
7
8Written by:   Fred L. Drake, Jr.
9Email:        <fdrake@acm.org>
10"""
11
12__revision__ = "$Id$"
13
14import os
15import re
16import string
17import sys
18
19from distutils.errors import DistutilsPlatformError
20
21# These are needed in a couple of spots, so just compute them once.
22PREFIX = os.path.normpath(sys.prefix)
23EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
24
25# Path to the base directory of the project. On Windows the binary may
26# live in project/PCBuild9.  If we're dealing with an x64 Windows build,
27# it'll live in project/PCbuild/amd64.
28project_base = os.path.dirname(os.path.abspath(sys.executable))
29if os.name == "nt" and "pcbuild" in project_base[-8:].lower():
30    project_base = os.path.abspath(os.path.join(project_base, os.path.pardir))
31# PC/VS7.1
32if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower():
33    project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
34                                                os.path.pardir))
35# PC/AMD64
36if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower():
37    project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
38                                                os.path.pardir))
39
40# python_build: (Boolean) if true, we're either building Python or
41# building an extension with an un-installed Python, so we use
42# different (hard-wired) directories.
43# Setup.local is available for Makefile builds including VPATH builds,
44# Setup.dist is available on Windows
45def _python_build():
46    for fn in ("Setup.dist", "Setup.local"):
47        if os.path.isfile(os.path.join(project_base, "Modules", fn)):
48            return True
49    return False
50python_build = _python_build()
51
52
53def get_python_version():
54    """Return a string containing the major and minor Python version,
55    leaving off the patchlevel.  Sample return values could be '1.5'
56    or '2.2'.
57    """
58    return sys.version[:3]
59
60
61def get_python_inc(plat_specific=0, prefix=None):
62    """Return the directory containing installed Python header files.
63
64    If 'plat_specific' is false (the default), this is the path to the
65    non-platform-specific header files, i.e. Python.h and so on;
66    otherwise, this is the path to platform-specific header files
67    (namely pyconfig.h).
68
69    If 'prefix' is supplied, use it instead of sys.prefix or
70    sys.exec_prefix -- i.e., ignore 'plat_specific'.
71    """
72    if prefix is None:
73        prefix = plat_specific and EXEC_PREFIX or PREFIX
74
75    if os.name == "posix":
76        if python_build:
77            buildir = os.path.dirname(sys.executable)
78            if plat_specific:
79                # python.h is located in the buildir
80                inc_dir = buildir
81            else:
82                # the source dir is relative to the buildir
83                srcdir = os.path.abspath(os.path.join(buildir,
84                                         get_config_var('srcdir')))
85                # Include is located in the srcdir
86                inc_dir = os.path.join(srcdir, "Include")
87            return inc_dir
88        return os.path.join(prefix, "include", "python" + get_python_version())
89    elif os.name == "nt":
90        return os.path.join(prefix, "include")
91    elif os.name == "os2":
92        return os.path.join(prefix, "Include")
93    else:
94        raise DistutilsPlatformError(
95            "I don't know where Python installs its C header files "
96            "on platform '%s'" % os.name)
97
98
99def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
100    """Return the directory containing the Python library (standard or
101    site additions).
102
103    If 'plat_specific' is true, return the directory containing
104    platform-specific modules, i.e. any module from a non-pure-Python
105    module distribution; otherwise, return the platform-shared library
106    directory.  If 'standard_lib' is true, return the directory
107    containing standard Python library modules; otherwise, return the
108    directory for site-specific modules.
109
110    If 'prefix' is supplied, use it instead of sys.prefix or
111    sys.exec_prefix -- i.e., ignore 'plat_specific'.
112    """
113    if prefix is None:
114        prefix = plat_specific and EXEC_PREFIX or PREFIX
115
116    if os.name == "posix":
117        libpython = os.path.join(prefix,
118                                 "lib", "python" + get_python_version())
119        if standard_lib:
120            return libpython
121        else:
122            return os.path.join(libpython, "site-packages")
123
124    elif os.name == "nt":
125        if standard_lib:
126            return os.path.join(prefix, "Lib")
127        else:
128            if get_python_version() < "2.2":
129                return prefix
130            else:
131                return os.path.join(prefix, "Lib", "site-packages")
132
133    elif os.name == "os2":
134        if standard_lib:
135            return os.path.join(prefix, "Lib")
136        else:
137            return os.path.join(prefix, "Lib", "site-packages")
138
139    else:
140        raise DistutilsPlatformError(
141            "I don't know where Python installs its library "
142            "on platform '%s'" % os.name)
143
144
145def customize_compiler(compiler):
146    """Do any platform-specific customization of a CCompiler instance.
147
148    Mainly needed on Unix, so we can plug in the information that
149    varies across Unices and is stored in Python's Makefile.
150    """
151    if compiler.compiler_type == "unix":
152        (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \
153            get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
154                            'CCSHARED', 'LDSHARED', 'SO')
155
156        if 'CC' in os.environ:
157            cc = os.environ['CC']
158        if 'CXX' in os.environ:
159            cxx = os.environ['CXX']
160        if 'LDSHARED' in os.environ:
161            ldshared = os.environ['LDSHARED']
162        if 'CPP' in os.environ:
163            cpp = os.environ['CPP']
164        else:
165            cpp = cc + " -E"           # not always
166        if 'LDFLAGS' in os.environ:
167            ldshared = ldshared + ' ' + os.environ['LDFLAGS']
168        if 'CFLAGS' in os.environ:
169            cflags = opt + ' ' + os.environ['CFLAGS']
170            ldshared = ldshared + ' ' + os.environ['CFLAGS']
171        if 'CPPFLAGS' in os.environ:
172            cpp = cpp + ' ' + os.environ['CPPFLAGS']
173            cflags = cflags + ' ' + os.environ['CPPFLAGS']
174            ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
175
176        cc_cmd = cc + ' ' + cflags
177        compiler.set_executables(
178            preprocessor=cpp,
179            compiler=cc_cmd,
180            compiler_so=cc_cmd + ' ' + ccshared,
181            compiler_cxx=cxx,
182            linker_so=ldshared,
183            linker_exe=cc)
184
185        compiler.shared_lib_extension = so_ext
186
187
188def get_config_h_filename():
189    """Return full pathname of installed pyconfig.h file."""
190    if python_build:
191        if os.name == "nt":
192            inc_dir = os.path.join(project_base, "PC")
193        else:
194            inc_dir = project_base
195    else:
196        inc_dir = get_python_inc(plat_specific=1)
197    if get_python_version() < '2.2':
198        config_h = 'config.h'
199    else:
200        # The name of the config.h file changed in 2.2
201        config_h = 'pyconfig.h'
202    return os.path.join(inc_dir, config_h)
203
204
205def get_makefile_filename():
206    """Return full pathname of installed Makefile from the Python build."""
207    if python_build:
208        return os.path.join(os.path.dirname(sys.executable), "Makefile")
209    lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
210    return os.path.join(lib_dir, "config", "Makefile")
211
212
213def parse_config_h(fp, g=None):
214    """Parse a config.h-style file.
215
216    A dictionary containing name/value pairs is returned.  If an
217    optional dictionary is passed in as the second argument, it is
218    used instead of a new dictionary.
219    """
220    if g is None:
221        g = {}
222    define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
223    undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
224    #
225    while 1:
226        line = fp.readline()
227        if not line:
228            break
229        m = define_rx.match(line)
230        if m:
231            n, v = m.group(1, 2)
232            try: v = int(v)
233            except ValueError: pass
234            g[n] = v
235        else:
236            m = undef_rx.match(line)
237            if m:
238                g[m.group(1)] = 0
239    return g
240
241
242# Regexes needed for parsing Makefile (and similar syntaxes,
243# like old-style Setup files).
244_variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
245_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
246_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
247
248def parse_makefile(fn, g=None):
249    """Parse a Makefile-style file.
250
251    A dictionary containing name/value pairs is returned.  If an
252    optional dictionary is passed in as the second argument, it is
253    used instead of a new dictionary.
254    """
255    from distutils.text_file import TextFile
256    fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1)
257
258    if g is None:
259        g = {}
260    done = {}
261    notdone = {}
262
263    while 1:
264        line = fp.readline()
265        if line is None:  # eof
266            break
267        m = _variable_rx.match(line)
268        if m:
269            n, v = m.group(1, 2)
270            v = v.strip()
271            # `$$' is a literal `$' in make
272            tmpv = v.replace('$$', '')
273
274            if "$" in tmpv:
275                notdone[n] = v
276            else:
277                try:
278                    v = int(v)
279                except ValueError:
280                    # insert literal `$'
281                    done[n] = v.replace('$$', '$')
282                else:
283                    done[n] = v
284
285    # do variable interpolation here
286    while notdone:
287        for name in notdone.keys():
288            value = notdone[name]
289            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
290            if m:
291                n = m.group(1)
292                found = True
293                if n in done:
294                    item = str(done[n])
295                elif n in notdone:
296                    # get it on a subsequent round
297                    found = False
298                elif n in os.environ:
299                    # do it like make: fall back to environment
300                    item = os.environ[n]
301                else:
302                    done[n] = item = ""
303                if found:
304                    after = value[m.end():]
305                    value = value[:m.start()] + item + after
306                    if "$" in after:
307                        notdone[name] = value
308                    else:
309                        try: value = int(value)
310                        except ValueError:
311                            done[name] = value.strip()
312                        else:
313                            done[name] = value
314                        del notdone[name]
315            else:
316                # bogus variable reference; just drop it since we can't deal
317                del notdone[name]
318
319    fp.close()
320
321    # strip spurious spaces
322    for k, v in done.items():
323        if isinstance(v, str):
324            done[k] = v.strip()
325
326    # save the results in the global dictionary
327    g.update(done)
328    return g
329
330
331def expand_makefile_vars(s, vars):
332    """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
333    'string' according to 'vars' (a dictionary mapping variable names to
334    values).  Variables not present in 'vars' are silently expanded to the
335    empty string.  The variable values in 'vars' should not contain further
336    variable expansions; if 'vars' is the output of 'parse_makefile()',
337    you're fine.  Returns a variable-expanded version of 's'.
338    """
339
340    # This algorithm does multiple expansion, so if vars['foo'] contains
341    # "${bar}", it will expand ${foo} to ${bar}, and then expand
342    # ${bar}... and so forth.  This is fine as long as 'vars' comes from
343    # 'parse_makefile()', which takes care of such expansions eagerly,
344    # according to make's variable expansion semantics.
345
346    while 1:
347        m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
348        if m:
349            (beg, end) = m.span()
350            s = s[0:beg] + vars.get(m.group(1)) + s[end:]
351        else:
352            break
353    return s
354
355
356_config_vars = None
357
358def _init_posix():
359    """Initialize the module as appropriate for POSIX systems."""
360    g = {}
361    # load the installed Makefile:
362    try:
363        filename = get_makefile_filename()
364        parse_makefile(filename, g)
365    except IOError, msg:
366        my_msg = "invalid Python installation: unable to open %s" % filename
367        if hasattr(msg, "strerror"):
368            my_msg = my_msg + " (%s)" % msg.strerror
369
370        raise DistutilsPlatformError(my_msg)
371
372    # load the installed pyconfig.h:
373    try:
374        filename = get_config_h_filename()
375        parse_config_h(file(filename), g)
376    except IOError, msg:
377        my_msg = "invalid Python installation: unable to open %s" % filename
378        if hasattr(msg, "strerror"):
379            my_msg = my_msg + " (%s)" % msg.strerror
380
381        raise DistutilsPlatformError(my_msg)
382
383    # On MacOSX we need to check the setting of the environment variable
384    # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so
385    # it needs to be compatible.
386    # If it isn't set we set it to the configure-time value
387    if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g:
388        cfg_target = g['MACOSX_DEPLOYMENT_TARGET']
389        cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '')
390        if cur_target == '':
391            cur_target = cfg_target
392            os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target
393        elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')):
394            my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure'
395                % (cur_target, cfg_target))
396            raise DistutilsPlatformError(my_msg)
397
398    # On AIX, there are wrong paths to the linker scripts in the Makefile
399    # -- these paths are relative to the Python source, but when installed
400    # the scripts are in another directory.
401    if python_build:
402        g['LDSHARED'] = g['BLDSHARED']
403
404    elif get_python_version() < '2.1':
405        # The following two branches are for 1.5.2 compatibility.
406        if sys.platform == 'aix4':          # what about AIX 3.x ?
407            # Linker script is in the config directory, not in Modules as the
408            # Makefile says.
409            python_lib = get_python_lib(standard_lib=1)
410            ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix')
411            python_exp = os.path.join(python_lib, 'config', 'python.exp')
412
413            g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp)
414
415        elif sys.platform == 'beos':
416            # Linker script is in the config directory.  In the Makefile it is
417            # relative to the srcdir, which after installation no longer makes
418            # sense.
419            python_lib = get_python_lib(standard_lib=1)
420            linkerscript_path = string.split(g['LDSHARED'])[0]
421            linkerscript_name = os.path.basename(linkerscript_path)
422            linkerscript = os.path.join(python_lib, 'config',
423                                        linkerscript_name)
424
425            # XXX this isn't the right place to do this: adding the Python
426            # library to the link, if needed, should be in the "build_ext"
427            # command.  (It's also needed for non-MS compilers on Windows, and
428            # it's taken care of for them by the 'build_ext.get_libraries()'
429            # method.)
430            g['LDSHARED'] = ("%s -L%s/lib -lpython%s" %
431                             (linkerscript, PREFIX, get_python_version()))
432
433    global _config_vars
434    _config_vars = g
435
436
437def _init_nt():
438    """Initialize the module as appropriate for NT"""
439    g = {}
440    # set basic install directories
441    g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
442    g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
443
444    # XXX hmmm.. a normal install puts include files here
445    g['INCLUDEPY'] = get_python_inc(plat_specific=0)
446
447    g['SO'] = '.pyd'
448    g['EXE'] = ".exe"
449    g['VERSION'] = get_python_version().replace(".", "")
450    g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
451
452    global _config_vars
453    _config_vars = g
454
455
456def _init_os2():
457    """Initialize the module as appropriate for OS/2"""
458    g = {}
459    # set basic install directories
460    g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
461    g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
462
463    # XXX hmmm.. a normal install puts include files here
464    g['INCLUDEPY'] = get_python_inc(plat_specific=0)
465
466    g['SO'] = '.pyd'
467    g['EXE'] = ".exe"
468
469    global _config_vars
470    _config_vars = g
471
472
473def get_config_vars(*args):
474    """With no arguments, return a dictionary of all configuration
475    variables relevant for the current platform.  Generally this includes
476    everything needed to build extensions and install both pure modules and
477    extensions.  On Unix, this means every variable defined in Python's
478    installed Makefile; on Windows and Mac OS it's a much smaller set.
479
480    With arguments, return a list of values that result from looking up
481    each argument in the configuration variable dictionary.
482    """
483    global _config_vars
484    if _config_vars is None:
485        func = globals().get("_init_" + os.name)
486        if func:
487            func()
488        else:
489            _config_vars = {}
490
491        # Normalized versions of prefix and exec_prefix are handy to have;
492        # in fact, these are the standard versions used most places in the
493        # Distutils.
494        _config_vars['prefix'] = PREFIX
495        _config_vars['exec_prefix'] = EXEC_PREFIX
496
497        if sys.platform == 'darwin':
498            kernel_version = os.uname()[2] # Kernel version (8.4.3)
499            major_version = int(kernel_version.split('.')[0])
500
501            if major_version < 8:
502                # On Mac OS X before 10.4, check if -arch and -isysroot
503                # are in CFLAGS or LDFLAGS and remove them if they are.
504                # This is needed when building extensions on a 10.3 system
505                # using a universal build of python.
506                for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
507                        # a number of derived variables. These need to be
508                        # patched up as well.
509                        'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
510                    flags = _config_vars[key]
511                    flags = re.sub('-arch\s+\w+\s', ' ', flags)
512                    flags = re.sub('-isysroot [^ \t]*', ' ', flags)
513                    _config_vars[key] = flags
514
515            else:
516
517                # Allow the user to override the architecture flags using
518                # an environment variable.
519                # NOTE: This name was introduced by Apple in OSX 10.5 and
520                # is used by several scripting languages distributed with
521                # that OS release.
522
523                if 'ARCHFLAGS' in os.environ:
524                    arch = os.environ['ARCHFLAGS']
525                    for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
526                        # a number of derived variables. These need to be
527                        # patched up as well.
528                        'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
529
530                        flags = _config_vars[key]
531                        flags = re.sub('-arch\s+\w+\s', ' ', flags)
532                        flags = flags + ' ' + arch
533                        _config_vars[key] = flags
534
535                # If we're on OSX 10.5 or later and the user tries to
536                # compiles an extension using an SDK that is not present
537                # on the current machine it is better to not use an SDK
538                # than to fail.
539                #
540                # The major usecase for this is users using a Python.org
541                # binary installer  on OSX 10.6: that installer uses
542                # the 10.4u SDK, but that SDK is not installed by default
543                # when you install Xcode.
544                #
545                m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS'])
546                if m is not None:
547                    sdk = m.group(1)
548                    if not os.path.exists(sdk):
549                        for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
550                             # a number of derived variables. These need to be
551                             # patched up as well.
552                            'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
553
554                            flags = _config_vars[key]
555                            flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags)
556                            _config_vars[key] = flags
557
558    if args:
559        vals = []
560        for name in args:
561            vals.append(_config_vars.get(name))
562        return vals
563    else:
564        return _config_vars
565
566def get_config_var(name):
567    """Return the value of a single variable using the dictionary
568    returned by 'get_config_vars()'.  Equivalent to
569    get_config_vars().get(name)
570    """
571    return get_config_vars().get(name)
572