itk 5.3rc4.post3: Failure to load libtbb library (observed on macOS 10.13)

To follow up on ITK 5.3 Release Candidate 4 available for testing, I may have identified few issues found with itk 5.3rc4.post3 specific to macOS that are related to the bundling of libtbb.12.3.dylib.

Context

While the issue was discovered while testing the use of itk wheels in the context of Slicer PR-6564, it was reproduced in the regular python interpreter available in the Slicer distribution.

The system used was the following:

$ sw_vers 
ProductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G14033

How to reproduce ?

After installing the wheels on macOS 10.13.6 and attempting to trigger the loading of _ITKCommonPython.so simply using print(itk.Image), the following error is reported:

$ PYTHONPATH=$itk_wheels_install_prefix/lib/python3.9/site-packages/:$itk_wheels_install_prefix/lib/python3.9/site-packages/itk \
$slicer_build_dir/python-install/bin/PythonSlicer -c "import itk; print(itk.Image)"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/support/lazy.py", line 138, in __getattribute__
    base.itk_load_swig_module(module, namespace)
  File "/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/support/base.py", line 96, in itk_load_swig_module
    itk_load_swig_module(dep, namespace)
  File "/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/support/base.py", line 132, in itk_load_swig_module
    l_module = loader.load(swig_module_name)
  File "/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/support/base.py", line 291, in load
    l_spec.loader.exec_module(l_module)  # pytype: disable=attribute-error
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/support/../ITKPyBasePython.py", line 69, in <module>
    from itk.pyBasePython import *
  File "/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/pyBasePython.py", line 15, in <module>
    from . import _ITKCommonPython
ImportError: dlopen(/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/_ITKCommonPython.so, 2): Library not loaded: @loader_path/.dylibs/libtbb.12.3.dylib
  Referenced from: /tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/_ITKCommonPython.so
  Reason: no suitable image found.  Did find:
	/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/.dylibs/libtbb.12.3.dylib: cannot load 'libtbb.12.3.dylib' (load command 0x80000034 is unknown)
	/private/tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/.dylibs/libtbb.12.3.dylib: cannot load 'libtbb.12.3.dylib' (load command 0x80000034 is unknown)

Inspecting the libtbb.12.3.dylib library shipped within the wheels prefix with -cp39-cp39-macosx_10_9_x86_64, it looks like the library was not built to target maOS 10.9:

$ cd /tmp/itk-wheels-installed/lib/python3.9/site-packages/itk/.dylibs/
$ otool -l libtbb.12.3.dylib | grep -E -A4 '(LC_VERSION_MIN_MACOSX|LC_BUILD_VERSION)' | grep -B1 sdk
  platform macos
       sdk 12.1

Additionally, it seems the library id is incorrect, instead of the hardcoded path /DLC/itk/.dylibs/libtbb.12.3.dylib, it should be libtbb.12.3.dylib:

$ otool -L libtbb.12.3.dylib 
libtbb.12.3.dylib:
	/DLC/itk/.dylibs/libtbb.12.3.dylib (compatibility version 12.0.0, current version 12.3.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1200.3.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.0.0)

… and attempting to change it is also failing:

$ install_name_tool -id libtbb.12.3.dylib ./libtbb.12.3.dylib
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool: object: ./libtbb.12.3.dylib malformed object (unknown load command 5)

Suggested path forward

Since the version of TBB currently built by ITKPythonPackage is v2021.3.0.tar.gz (See here) and the logic related to the minimum deployment target is setting the variables CMAKE_CXX_OSX_DEPLOYMENT_TARGET_FLAG and CMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG (see here), the corresponding options should be passed when building oneTBB in ITKPythonPackage/CMakeLists.txt

-DCMAKE_CXX_OSX_DEPLOYMENT_TARGET_FLAG:STRING="-mmacosx-version-min=${osx_deployment_target}"
-DCMAKE_C_OSX_DEPLOYMENT_TARGET_FLAG:STRING="-mmacosx-version-min=${osx_deployment_target}"

where osx_deployment_target is set based on the logic already implemented in ITKPythonPackage/scripts/macpython-build-wheels.sh.

3 Likes

@jcfr thanks for the investigation and digging into this issue.

I am surprise that the package tests were still passing.

:+1: could you please create a PR so you get credit for this fix?

1 Like

Pull request

create a PR

Here is a pull request to ensure the TBB library have the expected deployment target.
See https://github.com/InsightSoftwareConsortium/ITKPythonPackage/pull/216.

Once this is being tested, we will be able to also assess if additional option need to be passed to ensure the library id is not hardcoded to /DLC/itk/.dylibs/libtbb.12.3.dylib

_ITKCommonPython.so vs libtbb.12.3.dylib

For comparison, here is the output associated with a compiled module (e.g _ITKCommonPython.so) having its deployment target set:

$ otool -l ./itk/_ITKCommonPython.so  | grep -E -A4 '(LC_VERSION_MIN_MACOSX|LC_BUILD_VERSION)' | grep -B1 sdk
  version 10.9
      sdk 12.1

… and the one that fail to be loaded:

$ otool -l libtbb.12.3.dylib | grep -E -A4 '(LC_VERSION_MIN_MACOSX|LC_BUILD_VERSION)' | grep -B1 sdk
  platform macos
       sdk 12.1

Package Tests

I am surprise that the package tests were still passing.

I am wondering if the build script where ran in the background and the logic checking for error was skipped.

That said, I also confirmed that executing import itk; image = itk.Image[itk.UC, 2].New() using a regular python interpreter also lead to the error.

Here is what I get on factory-south-macos build machine (running macOS 10.13.6):

TEST_VENV=/tmp/itk-wheels-test-env
/Library/Frameworks/Python.framework/Versions/3.7//bin/python3.7 -m venv $TEST_VENV

$TEST_VENV/bin/python  -m pip install \
  itk==5.3rc4.post3 --pre

$TEST_VENV/bin/python -c "import itk; image = itk.Image[itk.UC, 2].New()"
[...]
ImportError: dlopen(/private/tmp/itk-wheels-test-env/lib/python3.7/site-packages/itk/_ITKCommonPython.so, 2): Library not loaded: @loader_path/.dylibs/libtbb.12.3.dylib
  Referenced from: /private/tmp/itk-wheels-test-env/lib/python3.7/site-packages/itk/_ITKCommonPython.so
  Reason: no suitable image found.  Did find:
	/private/tmp/itk-wheels-test-env/lib/python3.7/site-packages/itk/.dylibs/libtbb.12.3.dylib: cannot load 'libtbb.12.3.dylib' (load command 0x80000034 is unknown)
	/private/tmp/itk-wheels-test-env/lib/python3.7/site-packages/itk/.dylibs/libtbb.12.3.dylib: cannot load 'libtbb.12.3.dylib' (load command 0x80000034 is unknown)
1 Like

To follow up, thanks to the help of @Tom_Birdsong, @dzenanz and @matt.mccormick, this has been fixed in pull request ITKPythonPackage#216.

2 Likes