How does distutils determine what binary files to copy?
<p>I have a <code>setup</code> command defined like this for distutils (using py2app for Mac OS X, if it matters):</p>

<pre><code>setup(...,
      extensions=Extension('tracking_funcs',
                           ['tracking_funcs/tracking_funcs.pyx'],
                           include_dirs=[numpyincludedirs,]),
                 Extension('_psutil_osx',
                           sources = ['psutil/_psutil_osx.c',
                                      'psutil/_psutil_common.c',
                                      'psutil/arch/osx/process_info.c'],
                                      define_macros=[('PSUTIL_VERSION', int(get_psutilver().replace('.', '')))],
                                      extra_link_args=['-framework', 'CoreFoundation',
                                                       '-framework', 'IOKit']),
                 Extension('_psutil_posix',
                           sources = ['psutil/_psutil_posix.c'])],
      ...)
</code></pre>

<p>It builds all three extensions correctly:</p>

<pre><code>...
building 'tracking_funcs' extension
creating build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/tracking_funcs
/usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -g -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/numpy/core/include -I/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c tracking_funcs/tracking_funcs.c -o build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/tracking_funcs/tracking_funcs.o
... (some compiler warnings) ...
42 warnings generated.
/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/tracking_funcs/tracking_funcs.o -o build/bdist.macosx-10.6-x86_64/lib.macosx-10.6-x86_64-2.7/tracking_funcs.so
building '_psutil_osx' extension
creating build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/psutil
... (some compiler warnings) ...
2 warnings generated.
/usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -g -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -DPSUTIL_VERSION=400 -I/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c psutil/_psutil_common.c -o build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/psutil/_psutil_common.o
/usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -g -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -DPSUTIL_VERSION=400 -I/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c psutil/arch/osx/process_info.c -o build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/psutil/arch/osx/process_info.o
/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/psutil/_psutil_osx.o build/bdist.macosx-10.6-x86_64/lib.macosx-10.6-x86_64-2.7/_psutil_osx.so -framework CoreFoundation -framework IOKit
building '_psutil_posix' extension
/usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -arch i386 -arch x86_64 -g -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c psutil/_psutil_posix.c -o build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/psutil/_psutil_posix.o
/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g build/bdist.macosx-10.6-x86_64/temp.macosx-10.6-x86_64-2.7/psutil/_psutil_posix.o -o build/dist.macosx-10.6-x86_64/lib.macosx-10.6-x86_64-2.7/_psutil_posix.so
...
</code></pre>

<p>Then it copies the built binaries into the package destination, but it only copies two of the three extensions:</p>

<pre><code>...
copying file /.../build/bdist.macosx-10.6-x86_64/lib.macosx-10.6-x86_64-2.7/tracking_funcs.so -&gt; /.../dist/my.app/Contents/Resources/lib/python2.7/lib-dynload/tracking_funcs.so
copying file /.../build/bdist.macosx-10.6-x86_64/lib.macosx-10.6-x86_64-2.7/_psutil_posix.so -&gt; /.../dist/my.app/Contents/Resources/lib/python2.7/lib-dynload/_psutil_posix.so
...
</code></pre>

<p>Then my application crashes because it can't find the third extension at runtime.</p>

<p>How can I debug this? Where does distutils get its dependency tree from, if not from the list of extensions I define? Perhaps the bug is in py2app rather than distutils itself?</p>

<p>Py2app <a href="https://pythonhosted.org/py2app/implementation.html" rel="nofollow">uses</a> <a href="http://pythonhosted.org/modulegraph/index.html" rel="nofollow">modulegraph</a> to recursively build a source tree containing all the dependencies of all the dependencies of your project. Running py2app with the <a href="https://bitbucket.org/ronaldoussoren/py2app/src/149c25c413420120d3f383a9e854a17bc10d96fd/py2app/build_app.py?at=default&amp;fileviewer=file-view-default#build_app.py-355" rel="nofollow"><code>debug-modulegraph</code></a> flag set will print a bunch of debugging information to the console and drop into <a href="https://bitbucket.org/ronaldoussoren/py2app/src/149c25c413420120d3f383a9e854a17bc10d96fd/py2app/build_app.py?at=default&amp;fileviewer=file-view-default#build_app.py-945" rel="nofollow">a breakpoint</a> that allow you to browse the contents of the module graph:</p>

<pre><code>$ python setup.py py2app --debug-modulegraph
...
(Pdb) for item in mf.flatten(): print item
</code></pre>

<p>This will probably show something like <code>(MissingModule) psutil._psutil_osx</code> in its output, which means modulegraph isn't able to find the import path for that extension.</p>

<p>Modulegraph exposes a public function called <a href="https://bitbucket.org/ronaldoussoren/modulegraph/src/967aeeadd7a56f0bc3ea20fdb4d63d391fea0504/modulegraph/modulegraph.py?at=default&amp;fileviewer=file-view-default#modulegraph.py-344" rel="nofollow"><code>addPackagePath</code></a> that will allow you to give it additional hints as to where it should look for files in particular packages. In this case, adding something like this in <code>setup.py</code> should resolve the issue:</p>

<pre><code>from modulegraph import modulegraph
modulegraph.addPackagePath('psutil', 'build/bdist.macosx-10.6-x86_64/lib.macosx-10.6-x86_64-2.7/psutil/')
</code></pre>

python distutils py2app