开发者

Python distutils builds extensions differently on different machines

I have been working on a Python extension module with lots of files. While building on one machine, python setup.py build will ha开发者_StackOverflow中文版ppily detect changed files, build just those files, and link the whole thing together, just like make. On another machine, however, a single change to any file triggers a recompile of all sources.

Just to be clear. Both machines detect when the package is up to date and won't do anything. It is only when a single file changes that their behavior diverges.

Why is the second machine doing this?

Machine 1 (Does proper per-file dependency check and build.)

Python 2.6.4 (r264:75706, Feb 15 2010, 17:06:03) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

setuptools-0.6c11-py2.6

LSB Version: :core-3.1-amd64:core-3.1-ia32:core-3.1-noarch:graphics-3.1-amd64:graphics-3.1-ia32:graphics-3.1-noarch
Distributor ID: CentOS
Description: CentOS release 5.4 (Final)
Release: 5.4
Codename: Final

Machine 2 (Rebuilds everything when a single source file changes.)

Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.

setuptools-0.6c11-py2.6

No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 10.04 LTS
Release: 10.04
Codename: lucid


I looked into the Mercurial repo and found this change:

Issue #5372: Drop the reuse of .o files in Distutils' ccompiler (since Extension extra options may change the output without changing the .c file).

IOW, it was a simplistic optimization that was removed.


I've tracked this down to a change in distutils between Python 2.6.4 and Python 2.6.5. Two methods in distutils.ccompiler.CCompiler, namely, _setup_compile and _prep_compile, have had the same chunk of code removed:

if self.force:
    skip_source = {}            # rebuild everything
    for source in sources:
        skip_source[source] = 0
elif depends is None:
    # If depends is None, figure out which source files we
    # have to recompile according to a simplistic check. We
    # just compare the source and object file, no deep
    # dependency checking involving header files.
    skip_source = {}            # rebuild everything
    for source in sources:      # no wait, rebuild nothing
        skip_source[source] = 1

    n_sources, n_objects = newer_pairwise(sources, objects)
    for source in n_sources:    # no really, only rebuild what's
        skip_source[source] = 0 # out-of-date
else:
    # If depends is a list of files, then do a different
    # simplistic check.  Assume that each object depends on
    # its source and all files in the depends list.
    skip_source = {}
    # L contains all the depends plus a spot at the end for a
    # particular source file
    L = depends[:] + [None]
    for i in range(len(objects)):
        source = sources[i]
        L[-1] = source
        if newer_group(L, objects[i]):
            skip_source[source] = 0
        else:
            skip_source[source] = 1

This code checks each source file against its target object, and marks it to be skipped if it is older than the target. I don't know why it was removed, but it explains the discrepancy. When I put it back in as a test, the compiler reverts to per-source dependency analysis.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜