Skip to content

Relocatable pkg config files, @rpath/<libraryname> install name on macOS#1546

Merged
Dead2 merged 3 commits intozlib-ng:developfrom
haampie:fix/install-name-issues
Aug 6, 2023
Merged

Relocatable pkg config files, @rpath/<libraryname> install name on macOS#1546
Dead2 merged 3 commits intozlib-ng:developfrom
haampie:fix/install-name-issues

Conversation

@haampie
Copy link
Copy Markdown
Contributor

@haampie haampie commented Jul 24, 2023

Closes #1523

  1. Make the install name @rpath/<libz.dylib> on macOS for CMake and configure. This is what CMake 3.0+ does by default (CMP0042)1 and is a very sensible.

  2. When using GNUInstallDirs, there's CMAKE_INSTALL_* for relative paths, and CMAKE_INSTALL_FULL_* for absolute paths (joined with CMAKE_INSTALL_PREFIX + some special rules for multi-arch systems). Absolute paths in CMAKE_INSTALL_*DIR are allowed but discouraged, since that makes things non-relocatable. This PR drops the complexity of generating relocatable pc files when using absolute paths in those variables. If you want a custom layout, but a relocatable install, use e.g. CMAKE_INSTALL_[LIB|INCLUDE]DIR=relative/path, it's as simple as that.

  3. In contrast to CMake + GNUInstallDirs, the configure script expects absolute paths for --libdir (or ./configure '--libdir=${prefix}/lib' literally?). I'm not familiar enough with autotools, but looks like it's standard: Standard Directory Variables. Or maybe this is to mimic upstream zlib? In any case, with this PR, both the generated autotools & cmake pc file are relocatable by default, unless --libdir=/absolute/path or CMAKE_INSTALL_LIBDIR=/absolute/path is given.

Footnotes

  1. For what it's worth, there are two approaches to make libraries easily locatable in non-standard directories, both are rather hacky:

    1. Set the install name to @rpath/libfoo.dylib instead of libfoo.dylib, so that dependent libraries end up with a load command of @rpath/libfoo.dylib after linking, and rpaths can be used automatically.
    2. Set the install name to /absolute/path/of/libfoo.dylib so that dependent libraries end up with a load command of /absolute/path/of/libfoo.dylib, meaning that the dynamic linker doesn't have to search but can immediately open it.

    As far as I know, the second option is the most hacky and non-relocatable (using an absolute path as a library name...), the first option is the default of CMake. Option 1 is very common.

"-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib${SUFFIX}.map\"")
elseif(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}" OR NOT WITH_RPATH)
# Match configure/make's behavior (i.e. don't use @rpath on mac when using absolute path).
set_target_properties(zlib PROPERTIES INSTALL_NAME_DIR "@rpath/${CMAKE_INSTALL_FULL_LIBDIR}")
Copy link
Copy Markdown
Contributor Author

@haampie haampie Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was completely bogus... @rpath/<absolute path> makes the executable/library that links to zlib-ng add a load command @rpath/absolute/path/to/zlib-ng/lib, and @rpath is substituted by absolute rpaths set in that dependent. So you end up with for example:

/absolute/path/to/zlib-ng/lib/absolute/path/to/zlib-ng/lib/

as a search path :|


# Fix install directory after generating zlib.pc/zlib-ng.pc
if (NOT IS_ABSOLUTE CMAKE_INSTALL_LIBDIR AND WITH_RPATH)
set(CMAKE_INSTALL_LIBDIR "/${CMAKE_INSTALL_LIBDIR}")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also looked very surprising to me.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default prefix is /usr, which made it impossible to use directory structure where libraries are located in lib subdirectory of the output directory. This primarily affected builds where zlib-ng was used as a dependency.

@codecov
Copy link
Copy Markdown

codecov bot commented Jul 24, 2023

Codecov Report

Patch and project coverage have no change.

Comparison is base (a339d85) 83.89% compared to head (322a932) 83.90%.

❗ Current head 322a932 differs from pull request most recent head 0bae8f7. Consider uploading reports for the commit 0bae8f7 to get more accurate results

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #1546   +/-   ##
========================================
  Coverage    83.89%   83.90%           
========================================
  Files          132      132           
  Lines        10850    10850           
  Branches      2794     2794           
========================================
+ Hits          9103     9104    +1     
+ Misses        1048     1047    -1     
  Partials       699      699           
Flag Coverage Δ
macos_clang 42.97% <ø> (ø)
macos_gcc 73.67% <ø> (ø)
ubuntu_clang 82.52% <ø> (+0.22%) ⬆️
ubuntu_clang_debug 82.16% <ø> (ø)
ubuntu_clang_inflate_allow_invalid_dist 82.18% <ø> (ø)
ubuntu_clang_inflate_strict 82.51% <ø> (ø)
ubuntu_clang_mmap 82.62% <ø> (ø)
ubuntu_clang_pigz 13.91% <ø> (-0.06%) ⬇️
ubuntu_clang_pigz_no_optim 11.51% <ø> (ø)
ubuntu_clang_pigz_no_threads 13.73% <ø> (-0.06%) ⬇️
ubuntu_clang_reduced_mem 82.92% <ø> (ø)
ubuntu_clang_toolchain_riscv ∅ <ø> (∅)
ubuntu_gcc 75.24% <ø> (-0.08%) ⬇️
ubuntu_gcc_aarch64 77.41% <ø> (ø)
ubuntu_gcc_aarch64_compat_no_opt 75.67% <ø> (-0.02%) ⬇️
ubuntu_gcc_aarch64_no_acle 76.22% <ø> (ø)
ubuntu_gcc_aarch64_no_neon 76.22% <ø> (ø)
ubuntu_gcc_armhf 77.48% <ø> (ø)
ubuntu_gcc_armhf_compat_no_opt 75.66% <ø> (+0.01%) ⬆️
ubuntu_gcc_armhf_no_acle 77.43% <ø> (ø)
ubuntu_gcc_armhf_no_neon 77.33% <ø> (ø)
ubuntu_gcc_armsf 74.79% <ø> (ø)
ubuntu_gcc_armsf_compat_no_opt 74.25% <ø> (ø)
ubuntu_gcc_benchmark 73.70% <ø> (ø)
ubuntu_gcc_compat_no_opt 76.86% <ø> (+0.01%) ⬆️
ubuntu_gcc_compat_sprefix 73.72% <ø> (-0.16%) ⬇️
ubuntu_gcc_m32 73.36% <ø> (ø)
ubuntu_gcc_mingw_i686 73.57% <ø> (ø)
ubuntu_gcc_mingw_x86_64 73.58% <ø> (ø)
ubuntu_gcc_mips 75.04% <ø> (ø)
ubuntu_gcc_mips64 75.06% <ø> (ø)
ubuntu_gcc_no_avx2 74.33% <ø> (-0.06%) ⬇️
ubuntu_gcc_no_ctz 74.78% <ø> (ø)
ubuntu_gcc_no_ctzll 74.77% <ø> (ø)
ubuntu_gcc_no_pclmulqdq 74.33% <ø> (+0.06%) ⬆️
ubuntu_gcc_no_sse2 74.53% <ø> (-0.06%) ⬇️
ubuntu_gcc_no_sse42 74.78% <ø> (ø)
ubuntu_gcc_o1 74.23% <ø> (ø)
ubuntu_gcc_osb ∅ <ø> (∅)
ubuntu_gcc_pigz 38.34% <ø> (+0.07%) ⬆️
ubuntu_gcc_pigz_aarch64 39.20% <ø> (-0.06%) ⬇️
ubuntu_gcc_ppc 74.05% <ø> (ø)
ubuntu_gcc_ppc64 74.49% <ø> (ø)
ubuntu_gcc_ppc64_power9 74.66% <ø> (ø)
ubuntu_gcc_ppc64le 74.50% <ø> (ø)
ubuntu_gcc_ppc64le_novsx 74.88% <ø> (ø)
ubuntu_gcc_ppc64le_power9 74.44% <ø> (ø)
ubuntu_gcc_ppc_no_power8 74.77% <ø> (ø)
ubuntu_gcc_s390x 74.93% <ø> (ø)
ubuntu_gcc_s390x_dfltcc 72.00% <ø> (ø)
ubuntu_gcc_s390x_dfltcc_compat 74.04% <ø> (ø)
ubuntu_gcc_s390x_no_crc32 74.73% <ø> (ø)
ubuntu_gcc_sparc64 74.87% <ø> (ø)
ubuntu_gcc_sprefix 73.37% <ø> (-0.16%) ⬇️
win64_gcc 74.12% <ø> (+0.04%) ⬆️
win64_gcc_compat_no_opt 74.86% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

see 1 file with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

/*)
LDSHAREDFLAGS="-dynamiclib -install_name ${libdir}/${SHAREDLIBM} -compatibility_version ${VER1} -current_version ${VER3}" ;;
*)
LDSHAREDFLAGS="-dynamiclib -install_name @rpath/${libdir}/${SHAREDLIBM} -compatibility_version ${VER1} -current_version ${VER3}" ;;
Copy link
Copy Markdown
Contributor Author

@haampie haampie Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was bogus in multiple ways:

  1. Relative libdir is not relative to ${prefix} but current working directory on install.
  2. If relative libdir worked, you'd end up with @rpath/lib/libz.dylib, which is very non-standard... the expectation is that the dependent uses an rpath of the form <prefix>/lib, not <prefix>.
  3. For absolute libdir, it's still better to use @rpath/${SHAREDLIBM}, since that's still more relocatable.

@haampie haampie changed the title Relative paths CMAKE_INSTALL_*, absolute paths CMAKE_INSTALL_FULL_*; use @rpath/libname as install name on macOS Relative paths CMAKE_INSTALL_*, absolute paths CMAKE_INSTALL_FULL_*; use @rpath/<libraryname> as install name on macOS Jul 24, 2023
@haampie haampie force-pushed the fix/install-name-issues branch 2 times, most recently from cfac1b9 to f1dc3db Compare July 24, 2023 08:34
@haampie haampie changed the title Relative paths CMAKE_INSTALL_*, absolute paths CMAKE_INSTALL_FULL_*; use @rpath/<libraryname> as install name on macOS Relative paths CMAKE_INSTALL_*, absolute paths CMAKE_INSTALL_FULL_*; use @rpath/<libraryname> as install name on macOS; use prefix not destdir in tests Jul 24, 2023
@haampie haampie force-pushed the fix/install-name-issues branch from f1dc3db to b1d108e Compare July 24, 2023 08:44
@haampie haampie changed the title Relative paths CMAKE_INSTALL_*, absolute paths CMAKE_INSTALL_FULL_*; use @rpath/<libraryname> as install name on macOS; use prefix not destdir in tests Relocatable pkg config files, @rpath/<libraryname> install name on macOS, prefix vs destdir in tests Jul 24, 2023

# Refer to prefix symbolically to ease relocation by end user,
# as Makefile-generated .pc file does.
string(FIND "${CMAKE_INSTALL_INCLUDEDIR}" "${CMAKE_INSTALL_PREFIX}/" INCLUDEDIR_POS)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to drop this complexity. If a user wants to customize the install layout while keeping things relocatable, then just use CMAKE_INSTALL_INCLUDEDIR=myincludedir instead of an absolute path, as suggested by the docs: https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html#result-variables

@haampie haampie changed the title Relocatable pkg config files, @rpath/<libraryname> install name on macOS, prefix vs destdir in tests Relocatable pkg config files, @rpath/<libraryname> install name on macOS Jul 24, 2023
@haampie haampie force-pushed the fix/install-name-issues branch from 322a932 to 0bae8f7 Compare July 24, 2023 10:12
sysctl -n machdep.cpu.leaf7_features
sysctl -n machdep.cpu.extfeatures
CMAKE_ARGS="-DCMAKE_INSTALL_LIBDIR=lib -DPKGCONFIG_INSTALL_DIR=/lib/pkgconfig -DWITH_RPATH=on ${CMAKE_ARGS}"
CONFIGURE_ARGS="--libdir=lib ${CONFIGURE_ARGS}"
Copy link
Copy Markdown
Contributor Author

@haampie haampie Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how this ever passed tests? --libdir=lib with make DESTDIR=hello/ puts the libraries in hello/lib/* instead of hello/${prefix}/lib/*. --libdir takes absolute paths (or maybe --libdir='${prefix}/custompath')

Copy link
Copy Markdown
Collaborator

@mtl1979 mtl1979 Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This worked because Makefile doesn't add directory separator after prefix. This means that prefix also had to always end with directory separator.

In your example, hello/ is the prefix, so should not be duplicated.

@Dead2 Dead2 added Build Env Compatibility API/ABI Compatibility issue cleanup Improving maintainability or removing code. labels Jul 24, 2023
@Dead2
Copy link
Copy Markdown
Member

Dead2 commented Jul 24, 2023

That is quite the cleanup, I'll have to spend some time later for trying to understand the implications of all the changes. But at first glance it is looking quite neat.

Not sure whether you are aware of cmake/detect-install-dirs.cmake or not. I don't immediately see anything that would break, but just want to make sure that you are aware of what is done with paths there as well.
It just checks for non-standard path parameters that are currently in use by some distro packages and/or their standard macros (predating GNUInstallDirs I assume).

Edit: Actually, perhaps some of these changes should also be considered to move into cmake/detect-install-dirs.cmake.

@haampie
Copy link
Copy Markdown
Contributor Author

haampie commented Jul 24, 2023

Hadn't looked at detect-install-dirs.cmake yet.

If I understand correctly, it sets defaults for the (not *FULL*) GNUInstallDir paths if custom & deprecated variables are set. And it also defines PKGCONFIG_INSTALL_DIR.

Looks like it isn't necessary to change any of that: the deprecated variables and pkg config path variables should work whether relative or absolute

@haampie
Copy link
Copy Markdown
Contributor Author

haampie commented Jul 25, 2023

For what it's worth, Spack's pipeline is green with this patch for macOS (and linux). It uses configure not cmake, and it also builds and links hundreds of dependents of zlib-ng. And most importantly, it relies on rpaths for locating libraries, as it builds packages in funny directories that include a hash, like opt/zlib-ng-2.1.3-wkvj63jwbhwqovuaqy5frwxezlwujnqn

Previously it was broken on macOS

CRKatri added a commit to ProcursusTeam/Procursus that referenced this pull request Sep 14, 2023
@Dead2 Dead2 mentioned this pull request Oct 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build Env cleanup Improving maintainability or removing code. Compatibility API/ABI Compatibility issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop disabling RPATH on Mac?

3 participants