matthew-brett / delocate

Find and copy needed dynamic libraries into python wheels
BSD 2-Clause "Simplified" License
262 stars 59 forks source link

Dependencies not included on macOS after using delocate #121

Closed johnwlambert closed 2 years ago

johnwlambert commented 2 years ago

Hi, thanks for the excellent work and for creating this package.

I am seeing some unexpected behavior from delocate:

# Bundle external shared libraries into the wheels
for whl in $CURRDIR/wheelhouse_unrepaired/*.whl; do
    delocate-listdeps --all "$whl"
    delocate-wheel -w "$CURRDIR/wheelhouse" -v "$whl"
    rm $whl
done

This creates the expected wheel and lists the following dependencies:

+ for whl in '$CURRDIR/wheelhouse_unrepaired/*.whl'
+ delocate-listdeps --all /Users/runner/work/pycolmap-wheels/pycolmap-wheels/wheelhouse_unrepaired/pycolmap-0.0.1-cp38-cp38-macosx_10_15_x86_64.whl
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL
/usr/lib/libSystem.B.dylib
/usr/lib/libc++.1.dylib
/usr/local/Cellar/boost/1.76.0/lib/libboost_filesystem-mt.dylib
/usr/local/Cellar/boost/1.76.0/lib/libboost_program_options-mt.dylib
/usr/local/Cellar/boost/1.76.0/lib/libboost_system-mt.dylib
/usr/local/Cellar/ceres-solver/2.0.0_4/lib/libceres.2.0.0.dylib
/usr/local/Cellar/freeimage/3.18.0/lib/libfreeimage.3.18.0.dylib
/usr/local/Cellar/gflags/2.2.2/lib/libgflags.2.2.2.dylib
/usr/local/Cellar/glew/2.2.0_1/lib/libGLEW.2.2.0.dylib
/usr/local/Cellar/glog/0.5.0/lib/libglog.0.5.0.dylib
/usr/local/Cellar/gmp/6.2.1/lib/libgmp.10.dylib
/usr/local/Cellar/libomp/12.0.1/lib/libomp.dylib
/usr/local/Cellar/qt@5/5.15.2/lib/QtCore.framework/Versions/5/QtCore
/usr/local/Cellar/qt@5/5.15.2/lib/QtGui.framework/Versions/5/QtGui
/usr/local/Cellar/qt@5/5.15.2/lib/QtOpenGL.framework/Versions/5/QtOpenGL
/usr/local/Cellar/qt@5/5.15.2/lib/QtWidgets.framework/Versions/5/QtWidgets

+ delocate-wheel -w /Users/runner/work/pycolmap-wheels/pycolmap-wheels/wheelhouse -v /Users/runner/work/pycolmap-wheels/pycolmap-wheels/wheelhouse_unrepaired/pycolmap-0.0.1-cp38-cp38-macosx_10_15_x86_64.whl
Fixing: /Users/runner/work/pycolmap-wheels/pycolmap-wheels/wheelhouse_unrepaired/pycolmap-0.0.1-cp38-cp38-macosx_10_15_x86_64.whl
+ rm /Users/runner/work/pycolmap-wheels/pycolmap-wheels/wheelhouse_unrepaired/pycolmap-0.0.1-cp38-cp38-macosx_10_15_x86_64.whl

But when I pip install the wheel, and import the package, I see:

Traceback (most recent call last):
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/utils.py", line 585, in _locate
    import_module(mod)
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 848, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/johnlambert/Downloads/gtsfm/gtsfm/frontend/verifier/loransac.py", line 21, in <module>
    import pycolmap
ImportError: dlopen(/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so, 2): Library not loaded: /usr/local/opt/glog/lib/libglog.0.dylib
  Referenced from: /Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so
  Reason: image not found

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "gtsfm/runner/run_scene_optimizer_colmaploader.py", line 80, in <module>
    run_scene_optimizer(args)
  File "gtsfm/runner/run_scene_optimizer_colmaploader.py", line 21, in run_scene_optimizer
    scene_optimizer: SceneOptimizer = instantiate(cfg.SceneOptimizer)
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/instantiate/_instantiate2.py", line 180, in instantiate
    return instantiate_node(config, *args, recursive=_recursive_, convert=_convert_)
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/instantiate/_instantiate2.py", line 245, in instantiate_node
    value = instantiate_node(
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/instantiate/_instantiate2.py", line 245, in instantiate_node
    value = instantiate_node(
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/instantiate/_instantiate2.py", line 240, in instantiate_node
    _target_ = _resolve_target(node.get(_Keys.TARGET))
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/instantiate/_instantiate2.py", line 104, in _resolve_target
    return _locate(target)
  File "/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/hydra/_internal/utils.py", line 587, in _locate
    raise ImportError(
ImportError: Encountered error: `dlopen(/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so, 2): Library not loaded: /usr/local/opt/glog/lib/libglog.0.dylib
  Referenced from: /Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so
  Reason: image not found` when loading module 'gtsfm.frontend.verifier.loransac.LoRansac'

the same occurs for glog, freeimage, ceres-solver, etc:

ImportError: dlopen(/Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so, 2): Library not loaded: /usr/local/opt/freeimage/lib/libfreeimage.dylib
  Referenced from: /Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so
  Reason: image not found
  Referenced from: /Users/johnlambert/miniconda3/envs/gtsfm-v1/lib/python3.8/site-packages/pycolmap.cpython-38-darwin.so
  Reason: image not found

I can solve this by manually brew installing each of these packages, but my understanding was that delocate was supposed to be doing this for me. Any idea what is leading to this behavior?

matthew-brett commented 2 years ago

Hmm - odd - can you try using delocate==0.8.2 ? Do you see the same behavior?

isuruf commented 2 years ago

Can you share the two wheels? (one before delocate was run and the one after?)

johnwlambert commented 2 years ago

Thanks for the quick replies. I'm trying with 0.8.2 now.

This is the wheel after delocate was run, for python 3.8 (will need to unzip it):

wheels-macos-python@3.8 (1).zip

isuruf commented 2 years ago

Is/usr/local/opt/ a symlink to /usr/local/Cellar/ in your system?

isuruf commented 2 years ago

I mean on the system where delocate was run.

johnwlambert commented 2 years ago

I can check -- ~do you mean on the system where I built the wheels (Github Actions CI), or my local system where I am trying to install and use the wheel?~

locally, it appears to be, since i see:

ls -l /usr/local/opt/
/usr/local/opt/
total 0
lrwxr-xr-x  1 johnlambert  admin  22 Aug 30 11:11 boost -> ../Cellar/boost/1.76.0
lrwxr-xr-x  1 johnlambert  admin  22 Aug 30 11:11 boost@1.76 -> ../Cellar/boost/1.76.0
lrwxr-xr-x  1 johnlambert  admin  30 Aug 30 10:58 ceres-solver -> ../Cellar/ceres-solver/2.0.0_4
lrwxr-xr-x  1 johnlambert  admin  21 Aug 30 10:56 eigen -> ../Cellar/eigen/3.4.0
lrwxr-xr-x  1 johnlambert  admin  26 Aug 30 10:53 freeimage -> ../Cellar/freeimage/3.18.0
lrwxr-xr-x  1 johnlambert  admin  20 Aug 30 10:57 gcc -> ../Cellar/gcc/11.2.0
lrwxr-xr-x  1 johnlambert  admin  20 Aug 30 10:57 gcc@11 -> ../Cellar/gcc/11.2.0
lrwxr-xr-x  1 johnlambert  admin  19 Aug 30 10:57 gdbm -> ../Cellar/gdbm/1.20
lrwxr-xr-x  1 johnlambert  admin  22 Aug 24 06:34 gettext -> ../Cellar/gettext/0.21
lrwxr-xr-x  1 johnlambert  admin  22 Aug 30 10:42 gflags -> ../Cellar/gflags/2.2.2
lrwxr-xr-x  1 johnlambert  admin  20 Aug 30 10:57 gfortran -> ../Cellar/gcc/11.2.0
johnwlambert commented 2 years ago

@matthew-brett i confirmed and I see the same issue with delocate==0.8.2

johnwlambert commented 2 years ago

For reference, if it helps, this is the entire script that builds the OSX wheel: https://github.com/johnwlambert/pycolmap-wheels/blob/main/build-macos-new.sh

johnwlambert commented 2 years ago

@matthew-brett @isuruf here are the unrepaired wheels, if that's helpful: wheels-macos-python@3.8 (2).zip

isuruf commented 2 years ago

This is a problem with delocate running realpath on the dependency and trying to change install_names with the realpath instead of the symlinked path.

johnwlambert commented 2 years ago

I see, is that expected behavior? Or something you recommend changing in the delocate source?

isuruf commented 2 years ago

It should be fixed in delocate.

johnwlambert commented 2 years ago

Thanks @isuruf. Could you point me to the files where you think this needs to be changed?

I see realpath used in the following places:

https://github.com/matthew-brett/delocate/blob/master/delocate/libsana.py https://github.com/matthew-brett/delocate/blob/master/delocate/cmd/delocate_addplat.py https://github.com/matthew-brett/delocate/blob/master/delocate/cmd/delocate_listdeps.py https://github.com/matthew-brett/delocate/blob/master/delocate/delocating.py https://github.com/matthew-brett/delocate/blob/master/wheel_makers/make_wheels.sh

matthew-brett commented 2 years ago

@johnwlambert - sorry - was stuck under a metaphorical rock, just coming back to this now.

Is it possible your module is a single top-level compiled file, and that you have hit https://github.com/matthew-brett/delocate/issues/15 ?

johnwlambert commented 2 years ago

No problem, thanks @matthew-brett. I suppose there is a "single top-level" because there are just a few .cc pybind interface files at the top-level and a single setup.py file. There's actually no python source, besides setup.py. Here's the whole structure: https://github.com/mihaidusmanu/pycolmap

matthew-brett commented 2 years ago

Right - at the moment Delocate doesn't handle modules without their own enclosing directory - that is what I am calling a top-level module. @HexDecimal has done some heroic refactoring recently - Kyle - do you have any ideas to handle this case properly? It's a common complaint, but previous solutions just seemed too difficult to maintain for my taste.

HexDecimal commented 2 years ago

I think there are open pull requests trying to handle the top-level module issues. I don't think I'll suggest anything that hasn't already been talked about on those. I could look into them later.

matthew-brett commented 2 years ago

Yes - the most recent PR was https://github.com/matthew-brett/delocate/pull/39 - started in 2018 (I hang my head in shame). You will see there that it touches quite a lot of the code you've refactored. No worries if no obvious path occurs, just let me know, I will have another look and another think.

matthew-brett commented 2 years ago

@johnwlambert - @HexDecimal has nobly implemented (at last) this feature. Would you mind testing his current PR at https://github.com/matthew-brett/delocate/pull/123 ?