From 3705bf5bb59f6fff907adfa546d2f0a2ad3a611d Mon Sep 17 00:00:00 2001 From: zaniebot <242828183+zaniebot@users.noreply.github.com> Date: Thu, 22 Oct 2026 22:51:06 +0000 Subject: [PATCH] gh-119696: Use RUNPATH for relocatable builds --- Doc/using/configure.rst | 17 +++- Makefile.pre.in | 16 +-- ...-10-22-18-00-00.gh-issue-119696.r3Flag.rst | 5 +- configure | 99 +++++++++++++++++++ configure.ac | 48 +++++++++ 5 files changed, 176 insertions(+), 9 deletions(-) diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 2cc8e94ebf075c..894c25a38733e9 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -1076,12 +1076,27 @@ Linker options .. option:: --enable-relocatable Enable experimental support for relocatable builds (default is no). - This option currently has no effect on the build. .. warning:: This feature is under active development. Do not use it in production. + Currently, this sets relative runtime library paths on Linux and for + non-framework macOS builds. Framework relocation is not implemented yet. + Combining this option with :option:`--enable-framework` is an error. + + Move the installation as a unit. It must use the default ``bin`` and ``lib`` + directories under a common prefix. :option:`--with-platlibdir` may select + a different directory name for the standard library, but not a nested path. + On Linux, these paths use ``DT_RUNPATH``. + + Dependencies stored in other directories, such as ``lib/tcltk``, may + need additional loader search paths. + + This option does not bundle dependencies, adjust data-file lookup for + libraries such as Tcl/Tk, or rewrite script shebangs and build-time + paths in :mod:`sysconfig`. + .. versionadded:: 3.16 .. option:: --without-static-libpython diff --git a/Makefile.pre.in b/Makefile.pre.in index 7dcfc455797696..93bc9bffbb612b 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -61,6 +61,10 @@ DSYMUTIL_PATH= @DSYMUTIL_PATH@ GNULD= @GNULD@ RELOCATABLE= @RELOCATABLE@ +RELOCATABLE_PYTHON_LDFLAGS=@RELOCATABLE_PYTHON_LDFLAGS@ +RELOCATABLE_LIBPYTHON_LDFLAGS=@RELOCATABLE_LIBPYTHON_LDFLAGS@ +RELOCATABLE_EXTENSION_LDFLAGS=@RELOCATABLE_EXTENSION_LDFLAGS@ +LIBPYTHON_INSTALL_NAME=@LIBPYTHON_INSTALL_NAME@ # Shell used by make (some versions default to the login shell, which is bad) SHELL= /bin/sh -e @@ -286,7 +290,7 @@ DIST= $(DISTFILES) $(DISTDIRS) LIBRARY= @LIBRARY@ LDLIBRARY= @LDLIBRARY@ BLDLIBRARY= @BLDLIBRARY@ -MODULE_LDFLAGS_SHARED=$(if $(LIBPYTHON),$(BLDLIBRARY)) +MODULE_LDFLAGS_SHARED=$(if $(LIBPYTHON),$(BLDLIBRARY)) $(RELOCATABLE_EXTENSION_LDFLAGS) PY3LIBRARY= @PY3LIBRARY@ DLLLIBRARY= @DLLLIBRARY@ LDLIBRARYDIR= @LDLIBRARYDIR@ @@ -972,7 +976,7 @@ clinic-tests: check-clean-src $(srcdir)/Lib/test/clinic.test.c # Build the interpreter $(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) - $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) $(RELOCATABLE_PYTHON_LDFLAGS) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform @@ -1004,7 +1008,7 @@ $(LIBRARY): $(LIBRARY_OBJS) libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) # AIX Linker don't support "-h" option if test "$(MACHDEP)" != "aix"; then \ - $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(BLDSHARED) $(RELOCATABLE_LIBPYTHON_LDFLAGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ else \ $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ fi @@ -1013,10 +1017,10 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) fi libpython3.so: libpython$(LDVERSION).so - $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ + $(BLDSHARED) $(RELOCATABLE_LIBPYTHON_LDFLAGS) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) - $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(CC) -dynamiclib $(PY_CORE_LDFLAGS) $(RELOCATABLE_LIBPYTHON_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(LIBPYTHON_INSTALL_NAME) -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ libpython$(VERSION).sl: $(LIBRARY_OBJS) @@ -1617,7 +1621,7 @@ regen-re: $(BUILDPYTHON) $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/build/generate_re_casefix.py $(srcdir)/Lib/re/_casefix.py Programs/_testembed: Programs/_testembed.o $(LINK_PYTHON_DEPS) - $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) $(RELOCATABLE_PYTHON_LDFLAGS) -o $@ Programs/_testembed.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) ############################################################################ # "Bootstrap Python" used to run Programs/_freeze_module.py diff --git a/Misc/NEWS.d/next/Build/2026-10-22-18-00-00.gh-issue-119696.r3Flag.rst b/Misc/NEWS.d/next/Build/2026-10-22-18-00-00.gh-issue-119696.r3Flag.rst index 328d94c1094e4c..6d6f48bcb1915d 100644 --- a/Misc/NEWS.d/next/Build/2026-10-22-18-00-00.gh-issue-119696.r3Flag.rst +++ b/Misc/NEWS.d/next/Build/2026-10-22-18-00-00.gh-issue-119696.r3Flag.rst @@ -1,2 +1,3 @@ -Add the experimental :option:`--enable-relocatable` configure option. -The option currently has no effect on the build. +Add the experimental :option:`--enable-relocatable` configure option +to set relative runtime library paths on Linux and non-framework macOS +builds. The feature is under active development. diff --git a/configure b/configure index 9ab251a0fe22ee..43c793225d153b 100755 --- a/configure +++ b/configure @@ -848,6 +848,10 @@ LIBREADLINE_CFLAGS WHEEL_PKG_DIR LIBPL PY_ENABLE_SHARED +LIBPYTHON_INSTALL_NAME +RELOCATABLE_EXTENSION_LDFLAGS +RELOCATABLE_LIBPYTHON_LDFLAGS +RELOCATABLE_PYTHON_LDFLAGS BINLIBDEST LIBDEST PLATLIBDIR @@ -28787,6 +28791,101 @@ fi LIBDEST='${prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' BINLIBDEST='${exec_prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' +# Runtime paths are specific to the installed location of each kind of binary. +# Do not add them to LDSHARED: third-party extensions may be installed elsewhere. +RELOCATABLE_PYTHON_LDFLAGS= +RELOCATABLE_LIBPYTHON_LDFLAGS= +RELOCATABLE_EXTENSION_LDFLAGS= +LIBPYTHON_INSTALL_NAME='$(prefix)/lib/libpython$(LDVERSION).dylib' +if test "$enable_relocatable" = yes && test -n "$enable_framework" +then : + + as_fn_error $? "--enable-relocatable does not support framework builds yet" "$LINENO" 5 + +fi +if test "$enable_relocatable" = yes && test -z "$enable_framework" +then : + + case $ac_sys_system in #( + Linux|Darwin) : + ;; #( + *) : + as_fn_error $? "relocatable builds are not implemented on $ac_sys_system yet" "$LINENO" 5 ;; +esac + if test "x$prefix" = xNONE +then : + relocatable_prefix=$ac_default_prefix +else case e in #( + e) relocatable_prefix=$prefix ;; +esac +fi + if test "$exec_prefix" != NONE && test "$exec_prefix" != '${prefix}' && test "$exec_prefix" != "$relocatable_prefix" +then : + as_fn_error $? "--enable-relocatable requires a common prefix and exec-prefix" "$LINENO" 5 +fi + if test "$bindir" != '${exec_prefix}/bin' && test "$bindir" != '${prefix}/bin' && test "$bindir" != "$relocatable_prefix/bin" +then : + as_fn_error $? "--enable-relocatable requires the default bin directory" "$LINENO" 5 +fi + if test "$libdir" != '${exec_prefix}/lib' && test "$libdir" != '${prefix}/lib' && test "$libdir" != "$relocatable_prefix/lib" +then : + as_fn_error $? "--enable-relocatable requires the default lib directory" "$LINENO" 5 +fi + case $PLATLIBDIR in #( + */*|.|..) : + as_fn_error $? "--enable-relocatable requires a single directory name for --with-platlibdir" "$LINENO" 5 ;; #( + *) : + ;; +esac + case $ac_sys_system in #( + Linux) : + + # Keep the dollar sign through configure, make, and the shell. + # DT_RUNPATH permits LD_LIBRARY_PATH to select build-tree libraries. + RELOCATABLE_PYTHON_LDFLAGS="-Wl,--enable-new-dtags,-rpath,'\$\$ORIGIN/../lib'" + RELOCATABLE_LIBPYTHON_LDFLAGS="-Wl,--enable-new-dtags,-rpath,'\$\$ORIGIN'" + RELOCATABLE_EXTENSION_LDFLAGS="-Wl,--enable-new-dtags,-rpath,'\$\$ORIGIN/../../../lib'" + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -Wl,--enable-new-dtags" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + +else case e in #( + e) as_fn_error $? "--enable-relocatable requires linker support for --enable-new-dtags" "$LINENO" 5 ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + ;; #( + Darwin) : + + LIBPYTHON_INSTALL_NAME='@rpath/libpython$(LDVERSION).dylib' + RELOCATABLE_PYTHON_LDFLAGS='-Wl,-rpath,@executable_path/../lib' + RELOCATABLE_LIBPYTHON_LDFLAGS='-Wl,-rpath,@loader_path' + RELOCATABLE_EXTENSION_LDFLAGS='-Wl,-rpath,@loader_path/../../../lib' + ;; #( + *) : + ;; +esac + +fi + + + + + if test x$PLATFORM_TRIPLET = x; then LIBPL='$(LIBDEST)'"/config-${LDVERSION}" diff --git a/configure.ac b/configure.ac index 1afbeba002bbbf..98473e7d46575b 100644 --- a/configure.ac +++ b/configure.ac @@ -6874,6 +6874,54 @@ AC_SUBST([BINLIBDEST]) LIBDEST='${prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' BINLIBDEST='${exec_prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' +# Runtime paths are specific to the installed location of each kind of binary. +# Do not add them to LDSHARED: third-party extensions may be installed elsewhere. +RELOCATABLE_PYTHON_LDFLAGS= +RELOCATABLE_LIBPYTHON_LDFLAGS= +RELOCATABLE_EXTENSION_LDFLAGS= +LIBPYTHON_INSTALL_NAME='$(prefix)/lib/libpython$(LDVERSION).dylib' +AS_IF([test "$enable_relocatable" = yes && test -n "$enable_framework"], [ + AC_MSG_ERROR([--enable-relocatable does not support framework builds yet]) +]) +AS_IF([test "$enable_relocatable" = yes && test -z "$enable_framework"], [ + AS_CASE([$ac_sys_system], + [Linux|Darwin], [], + [AC_MSG_ERROR([relocatable builds are not implemented on $ac_sys_system yet])]) + AS_VAR_IF([prefix], [NONE], [relocatable_prefix=$ac_default_prefix], + [relocatable_prefix=$prefix]) + AS_IF([test "$exec_prefix" != NONE && test "$exec_prefix" != '${prefix}' && test "$exec_prefix" != "$relocatable_prefix"], + [AC_MSG_ERROR([--enable-relocatable requires a common prefix and exec-prefix])]) + AS_IF([test "$bindir" != '${exec_prefix}/bin' && test "$bindir" != '${prefix}/bin' && test "$bindir" != "$relocatable_prefix/bin"], + [AC_MSG_ERROR([--enable-relocatable requires the default bin directory])]) + AS_IF([test "$libdir" != '${exec_prefix}/lib' && test "$libdir" != '${prefix}/lib' && test "$libdir" != "$relocatable_prefix/lib"], + [AC_MSG_ERROR([--enable-relocatable requires the default lib directory])]) + AS_CASE([$PLATLIBDIR], [*/*|.|..], + [AC_MSG_ERROR([--enable-relocatable requires a single directory name for --with-platlibdir])]) + AS_CASE([$ac_sys_system], + [Linux], [ + # Keep the dollar sign through configure, make, and the shell. + # DT_RUNPATH permits LD_LIBRARY_PATH to select build-tree libraries. + RELOCATABLE_PYTHON_LDFLAGS="-Wl,--enable-new-dtags,-rpath,'\$\$ORIGIN/../lib'" + RELOCATABLE_LIBPYTHON_LDFLAGS="-Wl,--enable-new-dtags,-rpath,'\$\$ORIGIN'" + RELOCATABLE_EXTENSION_LDFLAGS="-Wl,--enable-new-dtags,-rpath,'\$\$ORIGIN/../../../lib'" + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -Wl,--enable-new-dtags" + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [], + [AC_MSG_ERROR([--enable-relocatable requires linker support for --enable-new-dtags])]) + LDFLAGS=$save_LDFLAGS + ], + [Darwin], [ + LIBPYTHON_INSTALL_NAME='@rpath/libpython$(LDVERSION).dylib' + RELOCATABLE_PYTHON_LDFLAGS='-Wl,-rpath,@executable_path/../lib' + RELOCATABLE_LIBPYTHON_LDFLAGS='-Wl,-rpath,@loader_path' + RELOCATABLE_EXTENSION_LDFLAGS='-Wl,-rpath,@loader_path/../../../lib' + ]) +]) +AC_SUBST([RELOCATABLE_PYTHON_LDFLAGS]) +AC_SUBST([RELOCATABLE_LIBPYTHON_LDFLAGS]) +AC_SUBST([RELOCATABLE_EXTENSION_LDFLAGS]) +AC_SUBST([LIBPYTHON_INSTALL_NAME]) + dnl define LIBPL after ABIFLAGS and LDVERSION is defined. AC_SUBST([PY_ENABLE_SHARED]) if test x$PLATFORM_TRIPLET = x; then