使用 CMake 生成静态可执行文件
Generate Static Executable with CMake
这个问题比较长,请耐心等待。
TLDR:带有子目录库的 CMake 项目链接成功,但创建了一个动态可执行文件。
代码位于:https://github.com/georcon/cmake-issue
还有注意:我已经阅读了所有相关内容questions/answers,并且none回答了这个问题。
我创建了以下最小示例:
创建静态链接的可执行文件(正确工作)
(Git 标签:SimpleExecutable)
main.c
#include <uuid/uuid.h>
#include <stdio.h>
int main(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
return 0;
}
CMakeLists.txt
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_executable(main-dynamic main.c)
target_link_libraries(main-dynamic uuid)
add_executable(main-static main.c)
target_link_libraries(main-static uuid -static)
结果
ldd main-dynamic
linux-vdso.so.1 (0x00007ffc406bb000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f76781cd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7677fdb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f76781eb000)
ldd main-static
not a dynamic executable
使用库创建静态可执行文件
(Git 标签:ExecutableWithLibrary)
lib/lib.h
#ifndef LIB_H
#define LIB_H
void PrintUUID();
#endif //LIB_H
lib/lib.c
#include <uuid/uuid.h>
#include <stdio.h>
void PrintUUID(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
}
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(testlibrary VERSION 1.0 DESCRIPTION "Static target issue - Library" LANGUAGES C)
add_library(testlib lib.c)
target_link_libraries(testlib uuid)
main.c
#include "lib/lib.h"
int main(){
PrintUUID();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_subdirectory(lib ./bin)
link_directories(./lib/ ./bin)
add_executable(main-dynamic main.c)
add_dependencies(main-dynamic testlib)
target_link_libraries(main-dynamic libtestlib.a uuid -static)
link_libraries("-static")
add_executable(main-static main.c)
target_link_libraries(main-static PUBLIC "-static" libtestlib.a uuid)
add_dependencies(main-static testlib)
#target_link_libraries(main-static libtestlib.a uuid -static)
结果
ldd main-static
linux-vdso.so.1 (0x00007ffe6b485000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007fc67edd3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc67ebe1000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007fc67edec000)
查看链接器命令:
~/cmake_issue$ cat CMakeFiles/main-static.dir/link.txt /usr/bin/cc
CMakeFiles/main-static.dir/main.c.o -o main-static
-L/home/georcon/cmake_issue/./lib -L/home/georcon/cmake_issue/./bin -Wl,-rpath,/home/georcon/cmake_issue/./lib:/home/georcon/cmake_issue/./bin -static -static
-Wl,-Bstatic -ltestlib -Wl,-Bdynamic -luuid
为什么在这种情况下 CMake 不生成静态链接的可执行文件?
长话短说:您需要告诉 CMake 您 更喜欢静态 linking 库。
这是通过设置 属性 LINK_SEARCH_START_STATIC
来完成的。您还需要告诉 CMake 不要在库列表末尾重置 static linkage 。
这是通过设置 属性 LINK_SEARCH_END_STATIC
:
set_target_properties(main-static PROPERTIES
LINK_SEARCH_START_STATIC ON
LINK_SEARCH_END_STATIC ON
)
另请参阅该问题:CMake and Static Linking。
这是怎么回事
实际上,linker 选项 -static
不仅会禁用 PIE,还会影响命令中列出的其他库...除非指定 -dynamic
。
CMake 有一个关于“default linking type”的概念,它适用于 CMake 适用的每个库(uuid
在你的情况下)无法推断其类型。此外,CMake 维护在每个库添加到linker的命令行后默认的linking类型。并且 CMake 期望用户有相同的行为,用户手动添加 linker 标志。
您的第一个示例 错误 ,但突然 有效 :
您添加 -static
将当前 linkage 类型变为 static。因此,您打破了 CMake 对当前 linkage 类型的期望。
当使用 uuid 为 link 生成 linker 选项时,CMake 期望当前 linkage 是 dynamic。因此,CMake 不会添加 -dynamic
linker 开关。
那时 CMake 的期望与现实不符,而现实又符合您的期望:uuid
是 link 静态编辑的。
但是第二个例子暴露了问题:
当 link 使用 libtestlib.a
库时,CMake 完全知道这是一个静态库,因此在该库之前添加 Wl,-Bstatic
选项。但 CMake 需要在每个选项后保持默认 linkage,因此它在库后添加 -Wl,-Bdynamic
:
-Wl,-Bstatic -ltestlib -Wl,-Bdynamic
有了这样的选项,CMake 对默认动态 linking 的期望 对应于现实:uuid
是动态 linked。但现在现实与你的期望不符。
这个问题比较长,请耐心等待。
TLDR:带有子目录库的 CMake 项目链接成功,但创建了一个动态可执行文件。
代码位于:https://github.com/georcon/cmake-issue
还有注意:我已经阅读了所有相关内容questions/answers,并且none回答了这个问题。
我创建了以下最小示例:
创建静态链接的可执行文件(正确工作)
(Git 标签:SimpleExecutable)
main.c
#include <uuid/uuid.h>
#include <stdio.h>
int main(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
return 0;
}
CMakeLists.txt
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_executable(main-dynamic main.c)
target_link_libraries(main-dynamic uuid)
add_executable(main-static main.c)
target_link_libraries(main-static uuid -static)
结果
ldd main-dynamic
linux-vdso.so.1 (0x00007ffc406bb000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f76781cd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7677fdb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f76781eb000)
ldd main-static
not a dynamic executable
使用库创建静态可执行文件
(Git 标签:ExecutableWithLibrary)
lib/lib.h
#ifndef LIB_H
#define LIB_H
void PrintUUID();
#endif //LIB_H
lib/lib.c
#include <uuid/uuid.h>
#include <stdio.h>
void PrintUUID(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
}
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(testlibrary VERSION 1.0 DESCRIPTION "Static target issue - Library" LANGUAGES C)
add_library(testlib lib.c)
target_link_libraries(testlib uuid)
main.c
#include "lib/lib.h"
int main(){
PrintUUID();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_subdirectory(lib ./bin)
link_directories(./lib/ ./bin)
add_executable(main-dynamic main.c)
add_dependencies(main-dynamic testlib)
target_link_libraries(main-dynamic libtestlib.a uuid -static)
link_libraries("-static")
add_executable(main-static main.c)
target_link_libraries(main-static PUBLIC "-static" libtestlib.a uuid)
add_dependencies(main-static testlib)
#target_link_libraries(main-static libtestlib.a uuid -static)
结果
ldd main-static
linux-vdso.so.1 (0x00007ffe6b485000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007fc67edd3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc67ebe1000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007fc67edec000)
查看链接器命令:
~/cmake_issue$ cat CMakeFiles/main-static.dir/link.txt /usr/bin/cc
CMakeFiles/main-static.dir/main.c.o -o main-static
-L/home/georcon/cmake_issue/./lib -L/home/georcon/cmake_issue/./bin -Wl,-rpath,/home/georcon/cmake_issue/./lib:/home/georcon/cmake_issue/./bin -static -static -Wl,-Bstatic -ltestlib -Wl,-Bdynamic -luuid
为什么在这种情况下 CMake 不生成静态链接的可执行文件?
长话短说:您需要告诉 CMake 您 更喜欢静态 linking 库。
这是通过设置 属性 LINK_SEARCH_START_STATIC
来完成的。您还需要告诉 CMake 不要在库列表末尾重置 static linkage 。
这是通过设置 属性 LINK_SEARCH_END_STATIC
:
set_target_properties(main-static PROPERTIES
LINK_SEARCH_START_STATIC ON
LINK_SEARCH_END_STATIC ON
)
另请参阅该问题:CMake and Static Linking。
这是怎么回事
实际上,linker 选项 -static
不仅会禁用 PIE,还会影响命令中列出的其他库...除非指定 -dynamic
。
CMake 有一个关于“default linking type”的概念,它适用于 CMake 适用的每个库(uuid
在你的情况下)无法推断其类型。此外,CMake 维护在每个库添加到linker的命令行后默认的linking类型。并且 CMake 期望用户有相同的行为,用户手动添加 linker 标志。
您的第一个示例 错误 ,但突然 有效 :
您添加 -static
将当前 linkage 类型变为 static。因此,您打破了 CMake 对当前 linkage 类型的期望。
当使用 uuid 为 link 生成 linker 选项时,CMake 期望当前 linkage 是 dynamic。因此,CMake 不会添加 -dynamic
linker 开关。
那时 CMake 的期望与现实不符,而现实又符合您的期望:uuid
是 link 静态编辑的。
但是第二个例子暴露了问题:
当 link 使用 libtestlib.a
库时,CMake 完全知道这是一个静态库,因此在该库之前添加 Wl,-Bstatic
选项。但 CMake 需要在每个选项后保持默认 linkage,因此它在库后添加 -Wl,-Bdynamic
:
-Wl,-Bstatic -ltestlib -Wl,-Bdynamic
有了这样的选项,CMake 对默认动态 linking 的期望 对应于现实:uuid
是动态 linked。但现在现实与你的期望不符。