maturin icon indicating copy to clipboard operation
maturin copied to clipboard

Link errors on macOS when a dependency which also uses pyo3 has a cdylib target

Open konstin opened this issue 3 years ago • 10 comments

Bug Description

I have two crates, cell and mitochondria, where cell depends on mitochondria. Each has pyo3 bindings on its own. If mitochondria has cdylib as crate-type, trying to maturin build on mac os will fail with pyo3 linker errors, with only the default crate-type rlib or on ubuntu and windows it works. I've made a minimal example with github actions at https://github.com/konstin/maturin-mac-os-dep-repro.

The error looks like it's missing the link-arg=-undefined/link-arg=dynamic_lookup, even though they are present and work for the top level crate.

[package]
name = "mitochondria"
version = "0.1.0"
edition = "2021"

[lib]
# REMOVE cdyblib FROM THE LINE BELOW TO MAKE THIS PASS
crate-type = ["cdylib", "rlib"]

[dependencies]
pyo3 = { version = "0.17.1", features = ["extension-module"] }
[package]
name = "cell"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
pyo3 = { version = "0.17.1", features = ["extension-module"] }
mitochondria = { path = "mitochondria" }

Your Python version (python -V)

3.8

Your pip version (pip -V)

22.2.2

What bindings you're using

pyo3

Does cargo build work?

n/a, this is a linker error

If on windows, have you checked that you aren't accidentally using unix path (those with the forward slash /)?

  • [x] Yes

Steps to Reproduce

See minimal example at https://github.com/konstin/maturin-mac-os-dep-repro

Ubuntu, passing: https://github.com/konstin/maturin-mac-os-dep-repro/runs/8144523404?check_suite_focus=true Mac os, failing: https://github.com/konstin/maturin-mac-os-dep-repro/runs/8144523536?check_suite_focus=true

konstin avatar Sep 01 '22 20:09 konstin

The error looks like it's missing the link-arg=-undefined/link-arg=dynamic_lookup, even though they are present and work for the top level crate.

I think it's because we are using cargo rustc which only applies these flags to top level crate.

messense avatar Sep 02 '22 02:09 messense

See also https://github.com/PyO3/setuptools-rust/issues/235

messense avatar Sep 02 '22 02:09 messense

Any workarounds for this?

mlucool avatar Nov 19 '22 02:11 mlucool

@mlucool If you mean https://github.com/deshaw/nbstripout-fast/pull/3 you can feature-gate the extension-module feature of pyo3.

[dependencies]
pyo3 = "0.17.3"

[features]
default = ["extension-module"]
extension-module = ["pyo3/extension-module"]

Then you can build bin bindings with maturin build --no-default-features to disable extension-module feature. When using cibuildwheel you can pass --no-default-features option via MATURIN_PEP517_ARGS="--no-default-features" env var.

messense avatar Nov 19 '22 02:11 messense

@messense I tried feature-gating the python bindings as you said by setting

# Cargo.toml

[features]
default = ["extension-module"]
extension-module = ["pyo3/extension-module"]

and

# pyproject.toml

[tool.maturin]
bindings = "bin"

to match what you suggested above.

When I tried to build the project using maturin build --no-default-features, I got a binary - which is what I want - but when I just did maturin build, I got a binary also. Interestingly, the only thing that allowed me to build python bindings was removing tool.maturin.bindings = "bin" from pyproject.toml - but when I did that, I could no longer build the python bindings. Here's a table of the resulting build artifact that I get when I change settings with the different build commands:

maturin build maturin build --no-default-features
tool.maturin.bindings = "bin" bin bin
No tool.maturin settings python bindings python bindings

In summary, I wasn't able to get --no-default-features to act as a feature gate to allow me to switch from building bin to building the python bindings. What did I miss here? :thinking:

peytondmurray avatar Nov 19 '22 21:11 peytondmurray

@peytondmurray Have you tried override bindings type in cli? maturin build -b pyo3.

messense avatar Nov 20 '22 01:11 messense

@messense Thanks for the help - overriding the bindings type in CLI does work, but I'm still getting macOS build issues using cibuildwheel even after feature-gating the extension-module as you suggested above. From the log I've confirmed that the build command used on the runner is correct:

    Running `maturin pep517 build-wheel -i /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/cibw-run-klnop_4a/cp39-macosx_arm64/build/venv/bin/python --compatibility off --no-default-features`

but the build still fails at the linking stage for binary output :thinking:. I can keep poking around with this, maybe there's something else I'm doing wrong here.

I'm also not sure if this helps, but cibuildwheel recently added the option to pass CLI args to the build backend with the CIBW_BUILD_SETTINGS variable: https://github.com/pypa/cibuildwheel/pull/1244 which could be useful here too.

peytondmurray avatar Nov 28 '22 19:11 peytondmurray

Not sure why, but with some debugging it show that even when extension-module feature disabled CARGO_FEATURE_EXTENSION_MODULE env var still presents:

https://github.com/PyO3/pyo3/blob/a71905052e9320767a1275a85b07e2dc895a17e0/pyo3-build-config/src/impl_.rs#L710

which in turn causes pyo3 to not emit libpython link args.

cc @davidhewitt

Reproduce steps:

git clone https://github.com/peytondmurray/nbstripout-fast.git
cd nbstripout-fast
git checkout add-bin
maturin build --no-default-features

PYO3_PRINT_CONFIG looks fine to me

$ PYO3_PRINT_CONFIG=1 maturin build --no-default-features
📦 Including license file "/Users/messense/Projects/nbstripout-fast/LICENSE.txt"
🐍 Found CPython 3.11 at /Users/messense/.pyenv/versions/3.11.0/bin/python3
📡 Using build options bindings from pyproject.toml
   Compiling pyo3-ffi v0.16.6
error: failed to run custom build command for `pyo3-ffi v0.16.6`

Caused by:
  process didn't exit successfully: `/Users/messense/Projects/nbstripout-fast/target/debug/build/pyo3-ffi-2eeeb1eb9310c032/build-script-build` (exit status: 101)
  --- stdout
  cargo:rerun-if-env-changed=PYO3_CROSS
  cargo:rerun-if-env-changed=PYO3_CROSS_LIB_DIR
  cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_VERSION
  cargo:rerun-if-env-changed=PYO3_CROSS_PYTHON_IMPLEMENTATION
  cargo:rerun-if-env-changed=PYO3_PRINT_CONFIG

  -- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --
  implementation=CPython
  version=3.11
  shared=true
  abi3=false
  lib_name=python3.11
  lib_dir=/Users/messense/.pyenv/versions/3.11.0/lib
  executable=/Users/messense/.pyenv/versions/3.11.0/bin/python3
  pointer_width=64
  build_flags=
  suppress_build_script_link_lines=false

  note: unset the PYO3_PRINT_CONFIG environment variable and retry to compile with the above config
💥 maturin failed
  Caused by: Failed to build a native library through cargo
  Caused by: Cargo build finished with "exit status: 101": `"cargo" "rustc" "--no-default-features" "--manifest-path" "/Users/messense/Projects/nbstripout-fast/Cargo.toml" "--message-format" "json" "--bin" "nbstripout-fast"`

messense avatar Nov 29 '22 02:11 messense

xref https://github.com/rust-lang/cargo/issues/9235

there is an implicit dependency from a binary to the library

messense avatar Dec 14 '22 06:12 messense

For future reference, i've confirmed that changing

      - name: Build wheels - x86_64
        uses: PyO3/maturin-action@v1
        with:
          target: x86_64

to

      - name: Build wheels - x86_64
        uses: PyO3/maturin-action@v1
        with:
          target: x86_64
        env:
          RUSTFLAGS: "-C link-arg=-undefined -C link-arg=dynamic_lookup"

is a workaround

I'm not sure where to document that properly, it's definitely a rare problem.

konstin avatar Apr 04 '23 17:04 konstin