Commit 6114d85a authored by Peter Wu's avatar Peter Wu Committed by Brad King

RPATH: Add option for using $ORIGIN in build tree

This makes binaries independent of the build directory by not embedding
the build directory via RPATH.  The tests are partially based on the
existing RuntimePath test, but with the check moved into a POST_BUILD
command such that it can be skipped when the platform lacks support.

Fixes: #18413
parent bba42bb9
......@@ -138,6 +138,7 @@ Properties on Targets
/prop_tgt/AUTORCC_OPTIONS
/prop_tgt/BINARY_DIR
/prop_tgt/BUILD_RPATH
/prop_tgt/BUILD_RPATH_USE_ORIGIN
/prop_tgt/BUILD_WITH_INSTALL_NAME_DIR
/prop_tgt/BUILD_WITH_INSTALL_RPATH
/prop_tgt/BUNDLE_EXTENSION
......
......@@ -322,6 +322,7 @@ Variables that Control the Build
/variable/CMAKE_AUTOUIC_OPTIONS
/variable/CMAKE_AUTOUIC_SEARCH_PATHS
/variable/CMAKE_BUILD_RPATH
/variable/CMAKE_BUILD_RPATH_USE_ORIGIN
/variable/CMAKE_BUILD_WITH_INSTALL_NAME_DIR
/variable/CMAKE_BUILD_WITH_INSTALL_RPATH
/variable/CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY
......
BUILD_RPATH_USE_ORIGIN
----------------------
Whether to use relative paths for the build ``RPATH``.
This property is initialized by the value of the variable
:variable:`CMAKE_BUILD_RPATH_USE_ORIGIN`.
On platforms that support runtime paths (``RPATH``) with the
``$ORIGIN`` token, setting this property to ``TRUE`` enables relative
paths in the build ``RPATH`` for executables that point to shared
libraries in the same build tree.
Normally the build ``RPATH`` of an executable contains absolute paths
to the directory of shared libraries. Directories contained within the
build tree can be made relative to enable relocatable builds and to
help achieving reproducible builds by omitting the build directory
from the build environment.
This property has no effect on platforms that do not support the
``$ORIGIN`` token in ``RPATH``, or when the :variable:`CMAKE_SKIP_RPATH`
variable is set. The runtime path set through the
:prop_tgt:`BUILD_RPATH` target property is also unaffected by this
property.
relative-rpath
--------------
* A :variable:`CMAKE_BUILD_RPATH_USE_ORIGIN` variable and corresponding
:prop_tgt:`BUILD_RPATH_USE_ORIGIN` target property were added to
enable use of relative runtime paths (RPATHs). This helps achieving
relocatable and reproducible builds that are invariant of the build
directory.
CMAKE_BUILD_RPATH_USE_ORIGIN
----------------------------
Whether to use relative paths for the build ``RPATH``.
This is used to initialize the :prop_tgt:`BUILD_RPATH_USE_ORIGIN` target
property for all targets, see that property for more details.
set(CMAKE_DL_LIBS "dl")
set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":")
set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN")
set(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,")
set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,")
set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic")
......
......@@ -421,7 +421,8 @@ std::string cmComputeLinkInformation::GetRPathLinkString() const
return "";
}
// Construct the linker runtime search path.
// Construct the linker runtime search path. These MUST NOT contain tokens
// such as $ORIGIN, see https://sourceware.org/bugzilla/show_bug.cgi?id=16936
return cmJoin(this->OrderDependentRPath->GetOrderedDirectories(), ":");
}
......@@ -1702,6 +1703,14 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
!this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH") &&
this->Target->GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH");
// Select whether to use $ORIGIN in RPATHs for artifacts in the build tree.
std::string const& originToken = this->Makefile->GetSafeDefinition(
"CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN");
std::string targetOutputDir = this->Target->GetDirectory(this->Config);
bool use_relative_build_rpath =
this->Target->GetPropertyAsBool("BUILD_RPATH_USE_ORIGIN") &&
!originToken.empty() && !targetOutputDir.empty();
// Construct the RPATH.
std::set<std::string> emitted;
if (use_install_rpath) {
......@@ -1711,6 +1720,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
if (use_build_rpath) {
// Add directories explicitly specified by user
if (const char* build_rpath = this->Target->GetProperty("BUILD_RPATH")) {
// This will not resolve entries to use $ORIGIN, the user is expected to
// do that if necessary.
cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted);
}
}
......@@ -1728,6 +1739,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
cmSystemTools::ConvertToUnixSlashes(rootPath);
std::vector<std::string> const& rdirs = this->GetRuntimeSearchPath();
std::string const& topBinaryDir =
this->CMakeInstance->GetHomeOutputDirectory();
for (std::string const& ri : rdirs) {
// Put this directory in the rpath if using build-tree rpath
// support or if using the link path as an rpath.
......@@ -1741,6 +1754,18 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
d += "/";
d += suffix;
cmSystemTools::ConvertToUnixSlashes(d);
} else if (use_relative_build_rpath) {
// If expansion of the $ORIGIN token is supported and permitted per
// policy, use relative paths in the RPATH.
if (cmSystemTools::ComparePath(d, topBinaryDir) ||
cmSystemTools::IsSubDirectory(d, topBinaryDir)) {
d = cmSystemTools::RelativePath(targetOutputDir, d);
if (!d.empty()) {
d = originToken + "/" + d;
} else {
d = originToken;
}
}
}
if (emitted.insert(d).second) {
runtimeDirs.push_back(std::move(d));
......@@ -1749,8 +1774,6 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
// Do not add any path inside the source or build tree.
std::string const& topSourceDir =
this->CMakeInstance->GetHomeDirectory();
std::string const& topBinaryDir =
this->CMakeInstance->GetHomeOutputDirectory();
if (!cmSystemTools::ComparePath(ri, topSourceDir) &&
!cmSystemTools::ComparePath(ri, topBinaryDir) &&
!cmSystemTools::IsSubDirectory(ri, topSourceDir) &&
......
......@@ -218,6 +218,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->SetPropertyDefault("ANDROID_ASSETS_DIRECTORIES", nullptr);
this->SetPropertyDefault("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr);
this->SetPropertyDefault("BUILD_RPATH", nullptr);
this->SetPropertyDefault("BUILD_RPATH_USE_ORIGIN", nullptr);
this->SetPropertyDefault("INSTALL_NAME_DIR", nullptr);
this->SetPropertyDefault("INSTALL_RPATH", "");
this->SetPropertyDefault("INSTALL_RPATH_USE_LINK_PATH", "OFF");
......
enable_language(C)
if(NOT CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN)
if(CMAKE_C_PLATFORM_ID STREQUAL "Linux")
# Sanity check for platform that is definitely known to support $ORIGIN.
message(FATAL_ERROR "Platform fails to report relative RPATH support")
else()
message(STATUS "Platform does not support relative RPATHs, skipping")
endif()
return()
endif()
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
function(CheckRpath target rpath)
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -Dfile=$<TARGET_FILE:${target}> -Drpath=${rpath}
-P "${CMAKE_CURRENT_SOURCE_DIR}/RelativeCheck.cmake"
VERBATIM
)
endfunction()
if(CMAKE_C_COMPILER_ID STREQUAL "XL" AND CMAKE_BINARY_DIR MATCHES " ")
# XL 16.1.0.0 fails building the library if the output path contains a space.
set(externDir)
message(STATUS "Skipping external library test because of a toolchain bug")
else()
get_filename_component(externDir "${CMAKE_BINARY_DIR}" DIRECTORY)
set(externDir "${externDir}/Relative-extern")
endif()
add_library(utils SHARED A.c)
add_library(utils-sub SHARED A.c)
set_property(TARGET utils-sub PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs)
if(externDir)
add_library(utils-extern SHARED A.c)
set_property(TARGET utils-extern PROPERTY LIBRARY_OUTPUT_DIRECTORY ${externDir})
endif()
add_executable(main main.c)
target_link_libraries(main utils)
CheckRpath(main "\$ORIGIN")
add_executable(main-norel main.c)
target_link_libraries(main-norel utils)
set_property(TARGET main-norel PROPERTY BUILD_RPATH_USE_ORIGIN OFF)
CheckRpath(main-norel "${CMAKE_CURRENT_BINARY_DIR}")
add_executable(mainsub main.c)
target_link_libraries(mainsub utils)
set_property(TARGET mainsub PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
CheckRpath(mainsub "\$ORIGIN/../")
add_executable(main-sub main.c)
target_link_libraries(main-sub utils-sub)
CheckRpath(main-sub "\$ORIGIN/libs")
add_executable(mainsub-sub main.c)
target_link_libraries(mainsub-sub utils-sub)
set_property(TARGET mainsub-sub PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
CheckRpath(mainsub-sub "\$ORIGIN/../libs")
if(externDir)
# Binaries linking to libraries outside the build tree should have an absolute RPATH.
add_executable(main-extern main.c)
target_link_libraries(main-extern utils-extern)
CheckRpath(main-extern "${externDir}")
endif()
file(RPATH_CHECK FILE "${file}" RPATH "${rpath}")
if(NOT EXISTS "${file}")
message(FATAL_ERROR "RPATH for ${file} did not contain the expected value")
endif()
......@@ -16,3 +16,17 @@ function(run_SymlinkImplicit)
${CMAKE_COMMAND} -Ddir=${RunCMake_TEST_BINARY_DIR} -P ${RunCMake_SOURCE_DIR}/SymlinkImplicitCheck.cmake)
endfunction()
run_SymlinkImplicit()
function(run_Relative)
# Use a single build tree for a few tests without cleaning.
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Relative-build)
set(RunCMake_TEST_NO_CLEAN 1)
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
endif()
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake(Relative)
run_cmake_command(Relative-build ${CMAKE_COMMAND} --build . --config Debug)
endfunction()
run_Relative()
extern int libA(void);
int main(void)
{
return 0;
return libA();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment