Help with Rust linker error on Darwin, _CFURLResourceIsReachable

I’m trying to build a trivial Rust program on Darwin with nix-shell and cargo. It uses the notify crate. I created a minimal example here: ~~https://github.com/bwolf/notify-nix-darwin~~.

Using cargo build I get the following error (truncated):

Undefined symbols for architecture x86_64:
            "_CFURLResourceIsReachable", referenced from:
                fsevent_sys::core_foundation::str_path_to_cfstring_ref::hb6fc1a8adf52fba2 in libfsevent_sys-64f7ee290c44ecde.rlib(fsevent_sys-64f7ee290c44ecde.fsevent_sys.aaucxc5j-cgu.6.rcgu.o)
          ld: symbol(s) not found for architecture x86_64
          clang-5.0: error: linker command failed with exit code 1 (use -v to see invocation)

What is the missing peace here? My shell.nix is:

with import <nixpkgs> {};
stdenv.mkDerivation {
  name = "rust-env";
  nativeBuildInputs = [
    rustc cargo
  ];
  buildInputs = [] ++ stdenv.lib.optionals stdenv.isDarwin [
    darwin.cf-private
    darwin.apple_sdk.frameworks.CoreServices
  ];
  RUST_BACKTRACE = 1;
}

That symbol doesn’t seem to exist in Nix’s version of CoreFoundation.framework.

> nm /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation | grep _CFURLResourceIsReachable
000000000008e2ad T _CFURLResourceIsReachable
> nm /nix/store/imvxk8636h9vj8hq8zh8wfh10x67w771-swift-corefoundation/Library/Frameworks/CoreFoundation.framework/Versions/Current/CoreFoundation | grep _CFURLResourceIsReachable
# no output

The function itself is documented as macOS 10.6+ and the CoreFoundation library includes other CFURL symbols, but not that one. It’s also present in CFURL.h, so I have no idea why the symbol is missing. It’s not the only one too, the other resource-related functions seem to be missing as well.

I looked up reverse dependencies of the notify crate and saw mdbook, which we have a package for, so I checked what mdbook is doing and found

    # Because CoreServices needs to be updated,
    # but Apple won't release the source.
    broken = stdenv.isDarwin;

I’m guessing it’s due to this same issue, though I can’t say for certain.

Then I checked another crate that depends on it, watchexec, and found this:

  # FIXME: Use impure version of CoreFoundation because of missing symbols.
  #   Undefined symbols for architecture x86_64: "_CFURLResourceIsReachable"
  preConfigure = stdenv.lib.optionalString stdenv.isDarwin ''
    export NIX_LDFLAGS="-F${CoreFoundation}/Library/Frameworks -framework CoreFoundation $NIX_LDFLAGS"
  '';

So that looks like a fix for your issue right there, though it’s definitely impure. Still, seems like the best approach for the time being.

You might also take a look at Update macOS to 10.12 by matthewbauer · Pull Request #56744 · NixOS/nixpkgs · GitHub (Update macOS to 10.12). Hopefully updating the SDK to 10.12 will fix this issue, though of course I can’t say for sure.


EDIT: After looking into this some more, darwin.cf-private is supposed to contain the Nix-built CoreFoundation, but it includes a setup hook that adds the system CoreFoundation to NIX_LDFLAGS. I don’t know how setup hooks interact with nix-shell, but at the very least it works when actually using Nix to build a derivation. If you can figure out how to make setup hooks from dependencies work then you should be good.

1 Like

I think I know why the symbols are missing from Nix’s CoreFoundation. Nix must have built CoreFoundation from source (of which macOS 10.10.5 is the last version where source was published). I just checked, and the CFURL.c from the public source doesn’t implement those functions. They’re in the header, but not the implementation.

Thanks @lilyball for your well structured analysis and sharp deductions. I learned a lot from it. In the meantime I came to the same conclusion that nix-build is working with cf-private, but nix-shell is not. I have too little understanding about the whole system currently to investigate further into the setup-hooks you mentioned. Hopefully this will be fixed some day.

@bwolf My best guess is that nix-shell leaves a setup hook script in your PATH that you need to run to actually apply the NIX_LDFLAGS changes, but I don’t know for sure.