From e125dc143762f66af8afa787e193f8c64a3b5dda Mon Sep 17 00:00:00 2001 From: Zhuo Zhang Date: Sun, 24 Nov 2024 00:57:37 +0800 Subject: [PATCH] feat: add rocbuild_link_as_needed() --- rocbuild.cmake | 17 ++++++++++++- test.py | 38 +++++++++++++++++++++++++++++ tests/link_as_needed/CMakeLists.txt | 25 +++++++++++++++++++ tests/link_as_needed/foo_math.c | 21 ++++++++++++++++ tests/link_as_needed/foo_math.h | 29 ++++++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 tests/link_as_needed/CMakeLists.txt create mode 100644 tests/link_as_needed/foo_math.c create mode 100644 tests/link_as_needed/foo_math.h diff --git a/rocbuild.cmake b/rocbuild.cmake index 0c4f581..4a045f5 100644 --- a/rocbuild.cmake +++ b/rocbuild.cmake @@ -1,10 +1,11 @@ # Author: Zhuo Zhang # Homepage: https://github.com/zchrissirhcz/rocbuild -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.13) # CMake 3.10: include_guard() # CMake 3.21: $ +# CMake 3.13: target_link_options() use "LINKER:" as a portable way for different compiler + linker combo include_guard() @@ -268,5 +269,19 @@ function(rocbuild_hide_symbols TARGET) endfunction() +function(rocbuild_link_as_needed TARGET) + get_target_property(TARGET_TYPE ${TARGET} TYPE) + if(TARGET_TYPE STREQUAL "SHARED_LIBRARY") + if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang)$") OR + (CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")) + target_link_options(${TARGET} PRIVATE "LINKER:-as-needed") + elseif((CMAKE_C_COMPILER_ID STREQUAL "AppleClang") OR + (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) + target_link_options(${TARGET} PRIVATE "LINKER:-dead_strip_dylibs") + endif() + endif() +endfunction() + + rocbuild_set_artifacts_path() rocbuild_enable_ninja_colorful_output() \ No newline at end of file diff --git a/test.py b/test.py index 14653a9..2c9a9d8 100644 --- a/test.py +++ b/test.py @@ -222,5 +222,43 @@ def test_copy_dlls(self): self.assertTrue(items[3] == 'test.exe') shutil.rmtree('build/copy_dlls') + def test_link_as_needed(self): + if os_name == 'linux': + self.check_generate('link_as_needed', args='-DLINK_AS_NEEDED=0') + self.check_build('link_as_needed') + cmd = 'ldd build/link_as_needed/libfoo_math.so' + ret, out = check_output(cmd) + print(out) + self.assertEqual(0, ret, out) + self.assertIn('libqux.so =>', out) + shutil.rmtree('build/link_as_needed') + + self.check_generate('link_as_needed', args='-DLINK_AS_NEEDED=1') + self.check_build('link_as_needed') + cmd = 'ldd build/link_as_needed/libfoo_math.so' + ret, out = check_output(cmd) + print(out) + self.assertEqual(0, ret, out) + self.assertTrue('libqux.so =>' not in out) + shutil.rmtree('build/link_as_needed') + elif os_name == 'mac': + self.check_generate('link_as_needed', args='-DLINK_AS_NEEDED=0') + self.check_build('link_as_needed') + cmd = 'otool -L build/link_as_needed/libfoo_math.dylib' + ret, out = check_output(cmd) + print(out) + self.assertEqual(0, ret, out) + self.assertIn('@rpath/libqux.dylib', out) + shutil.rmtree('build/link_as_needed') + + self.check_generate('link_as_needed', args='-DLINK_AS_NEEDED=1') + self.check_build('link_as_needed') + cmd = 'otool -L build/link_as_needed/libfoo_math.dylib' + ret, out = check_output(cmd) + print(out) + self.assertEqual(0, ret, out) + self.assertTrue('@rpath/libqux.dylib' not in out) + shutil.rmtree('build/link_as_needed') + if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/tests/link_as_needed/CMakeLists.txt b/tests/link_as_needed/CMakeLists.txt new file mode 100644 index 0000000..963c127 --- /dev/null +++ b/tests/link_as_needed/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.10) +project(test_link_as_needed) + +include(../../rocbuild.cmake) + +# For test purpose, we explicitly enable `--no-as-needed` for GNU and Clang as the default behavior. +if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang)$") + add_link_options("LINKER:--no-as-needed") +endif() + +add_library(foo_math SHARED + foo_math.h + foo_math.c +) +target_include_directories(foo_math PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(foo_math PRIVATE FOO_MATH_EXPORTS) + +add_library(foo SHARED ../src/foo.c) +target_include_directories(foo PRIVATE ../src) + +target_link_libraries(foo_math PRIVATE foo) + +if(LINK_AS_NEEDED) + rocbuild_link_as_needed(foo_math) +endif() diff --git a/tests/link_as_needed/foo_math.c b/tests/link_as_needed/foo_math.c new file mode 100644 index 0000000..2b929ac --- /dev/null +++ b/tests/link_as_needed/foo_math.c @@ -0,0 +1,21 @@ +#include "foo_math.h" +#include + +static int compare(const void *a, const void *b) +{ + int int_a = *(int *)a; + int int_b = *(int *)b; + + if (int_a < int_b) return -1; + else if (int_a > int_b) return 1; + else return 0; +} + +void abs_sort(int n, int* data) +{ + for (int i = 0; i < n; i++) + { + data[i] = abs(i); + } + qsort(data, n, sizeof(int), compare); +} diff --git a/tests/link_as_needed/foo_math.h b/tests/link_as_needed/foo_math.h new file mode 100644 index 0000000..0bd1257 --- /dev/null +++ b/tests/link_as_needed/foo_math.h @@ -0,0 +1,29 @@ +#pragma once + +#ifdef FOO_MATH_EXPORTS +# if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) +# define FOO_MATH_API __declspec(dllexport) +# elif defined(__GNUC__) && __GNUC__ >= 4 +# define FOO_MATH_API __attribute__ ((visibility ("default"))) +# endif +#else +# if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) +# define FOO_MATH_API __declspec(dllimport) +# else +# define FOO_MATH_API +# endif +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +FOO_MATH_API +void abs_sort(int n, int* data); + +#ifdef __cplusplus +} +#endif +