CMake 创建和 link 32 位和 64 位版本的库
CMake create and link 32bit and 64bit versions of library
我正在尝试编译以下代码两次,一次使用 -m32
,一次不使用:
// File mylib.cc
#include <iostream>
void print_int_size() {
std::cout << sizeof(int*) << std::endl;
}
// File main.cc
void print_int_size();
int main() {
print_int_size();
return 0;
}
我的 CMakeLists.txt 中有以下内容:
project (Link32b VERSION 0.91 LANGUAGES CXX)
add_library ( mylib STATIC mylib.cc )
add_library ( mylib_32b STATIC mylib.cc )
target_compile_options ( mylib_32b PUBLIC -m32 )
add_executable ( main main.cc )
add_executable ( main_32b main.cc )
target_compile_options ( main_32b PRIVATE -m32 )
target_link_libraries ( main PRIVATE mylib )
target_link_libraries ( main_32b PRIVATE mylib_32b )
我在编译时得到以下输出(类似于 gcc):
Scanning dependencies of target mylib
[ 12%] Building CXX object CMakeFiles/mylib.dir/mylib.cc.o
[ 25%] Linking CXX static library libmylib.a
[ 25%] Built target mylib
Scanning dependencies of target main
[ 37%] Building CXX object CMakeFiles/main.dir/main.cc.o
[ 50%] Linking CXX executable main
[ 50%] Built target main
Scanning dependencies of target mylib_32b
[ 62%] Building CXX object CMakeFiles/mylib_32b.dir/mylib.cc.o
[ 75%] Linking CXX static library libmylib_32b.a
[ 75%] Built target mylib_32b
Scanning dependencies of target main_32b
[ 87%] Building CXX object CMakeFiles/main_32b.dir/main.cc.o
[100%] Linking CXX executable main_32b
ld: warning: ignoring file CMakeFiles/main_32b.dir/main.cc.o, file was built for i386 which is not the architecture being linked (x86_64): CMakeFiles/main_32b.dir/main.cc.o
ld: warning: ignoring file libmylib_32b.a, file was built for archive which is not the architecture being linked (x86_64): libmylib_32b.a
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [main_32b] Error 1
make[1]: *** [CMakeFiles/main_32b.dir/all] Error 2
make: *** [all] Error 2
我在这里错过了什么?
===
更新:奇怪的是,设置 CMAKE_CXX_FLAGS
以包含 -m32
会使示例工作。但是,我想在不设置变量的情况下完成它,即遵循现代基于目标的方法。
为了简化项目的维护,我建议您保持构建系统尽可能简单,而不是配置和构建项目
两次:
- 64 位二进制文件的一个构建
32 位二进制文件的一个构建:
- 通过将
CXXFLAGS
和 CFLAGS
环境变量设置为 -m32
(方法 1)
- 或者通过设置三个环境变量
AS
、CXX
和CC
(方法二)
简化您的示例项目
还要确保添加 cmake_minimum_required
,否则会出现错误 VERSION not allowed unless CMP0048 is set to NEW
。
cmake_minimum_required(VERSION 3.10)
project (Link VERSION 0.91 LANGUAGES CXX)
add_library ( mylib STATIC mylib.cc )
add_executable ( main main.cc )
target_link_libraries ( main PRIVATE mylib )
通过拥有一个不对工具链进行硬编码假设的更简单的构建系统,您隐式地启用了对跨平台和不同环境(如 ARM 等)的支持……它还可以使持续集成更容易。例如,在 CircleCI 上,您将有两个构建作业(一个用于 64 位,一个用于 32 位)都构建一个简单的项目。
安装所需的 i386 库
在 Ubuntu 上,可以这样做
sudo apt-get install \
gcc-multilib \
g++-multilib \
libc6:i386 \
libstdc++6:i386
其他依赖项将使用 packageName:i386
安装
循序渐进:方法 1
假设我们有以下目录结构:
<root>
|
|-src
| |--- CMakeLists.txt
| |--- main.cc
| |--- mylib.cc
|
|-build
| |-- ...
|
|-build-32
|-- ...
您只需执行以下操作即可编译 32 位版本:
CFLAGS=-m32 CXXFLAGS=-m32 cmake -Hsrc -Bbuild-32
循序渐进:方法 2
方法二的目标是引入交叉编译的思想。
在最后一节中,您将了解 dockcross/linux-32
docker 个在内部应用相同原理的图像。
为 as
、gcc
和 g++
创建三个包装器脚本
以下是三个shell脚本的内容:
- i686-linux-gnu-as
#!/bin/bash
exec as -m32 "$@"
- i686-linux-gnu-gcc
#!/bin/bash
exec gcc -m32 "$@"
- i686-linux-gnu-g++
#!/bin/bash
exec g++ -m32 "$@"
编译
假设我们有以下目录结构:
<root>
|-bin
| |- i686-linux-gnu-as
| |- i686-linux-gnu-g++
| |- i686-linux-gnu-gcc
|
|-src
| |--- CMakeLists.txt
| |--- main.cc
| |--- mylib.cc
|
|-build
| |-- ...
|
|-build-32
|-- ...
你会分别做
64位
$ cmake -Hsrc -Bbuild; cmake --build ./build
-- The CXX compiler identification is GNU 5.2.1
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/scratch/build
[...]
[100%] Built target main
$ file ./build/main
./build/main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e28e610f85cd4a2ab29e38ed58c1cb928f4aaf33, not stripped
$ ./build/main
4
32位
$ CXX=$(pwd)/bin/i686-linux-gnu-g++ \
CC=$(pwd)/bin/i686-linux-gnu-gcc \
AS=$(pwd)/bin/i686-linux-gnu-as linux32 \
bash -c "cmake -Hsrc -Bbuild-32; cmake --build ./build-32/"
-- The CXX compiler identification is GNU 5.2.1
-- Check for working CXX compiler: /tmp/scratch/bin/i686-linux-gnu-g++
-- Check for working CXX compiler: /tmp/scratch/bin/i686-linux-gnu-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
[...]
[100%] Built target main
$ file ./build-32/main
./build-32/main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b7f5781f66a28e28d28eda2b798b671c1e89e22a, not stripped
$ ./build-32/main
4
现在要了解为什么 sizeof(int)
在 64 位和 32 位版本上相同,请考虑阅读 C/C++: sizeof(short), sizeof(int), sizeof(long), sizeof(long long), etc... on a 32-bit machine versus on a 64-bit machine
使用 dockcross 简化编译
现在,要轻松编译为 32 位,您还可以使用 dockcross 映像 dockcross/linux-x86
。参见 https://github.com/dockcross/dockcross#readme
docker pull dockcross/linux-x86
docker run -ti --rm dockcross/linux-x86 > dockcross-linux-x86
chmod u+x dockcross-linux-x86
然后编译,你会做:
dockcross-linux-x86 bash -c "cmake -Hsrc -Bbuild-32; cmake --build ./build-32/"
-m32
标志不是 "inherited" 用于链接目的:
target_compile_options ( <lib> PUBLIC -m32 )
target_link_libraries ( <target> PRIVATE <lib> ) // Does not link with `-m32`.
请注意,由于 target_link_libraries
"inherits" [=16=,上述导致 <target>
编译 -m32
] 来自 <lib>
的编译选项。但是,该标志是 而不是 传递给链接器的。
而且没有target_link_options
命令,所以无法插入target_link_options ( <link> PUBLIC -m32 )
行来解决问题。
相反,根据 this answer(稍作修改),正确的方法是
target_compile_options ( <lib> PUBLIC -m32 )
set_target_properties ( <target> PROPERTIES LINK_FLAGS -m32 )
target_link_libraries ( <target> PRIVATE <lib> )
添加到: Cmake 3.13+ has target_link_options
命令因此你可以写:
target_link_options(<target> PRIVATE "-m32")
我正在尝试编译以下代码两次,一次使用 -m32
,一次不使用:
// File mylib.cc
#include <iostream>
void print_int_size() {
std::cout << sizeof(int*) << std::endl;
}
// File main.cc
void print_int_size();
int main() {
print_int_size();
return 0;
}
我的 CMakeLists.txt 中有以下内容:
project (Link32b VERSION 0.91 LANGUAGES CXX)
add_library ( mylib STATIC mylib.cc )
add_library ( mylib_32b STATIC mylib.cc )
target_compile_options ( mylib_32b PUBLIC -m32 )
add_executable ( main main.cc )
add_executable ( main_32b main.cc )
target_compile_options ( main_32b PRIVATE -m32 )
target_link_libraries ( main PRIVATE mylib )
target_link_libraries ( main_32b PRIVATE mylib_32b )
我在编译时得到以下输出(类似于 gcc):
Scanning dependencies of target mylib
[ 12%] Building CXX object CMakeFiles/mylib.dir/mylib.cc.o
[ 25%] Linking CXX static library libmylib.a
[ 25%] Built target mylib
Scanning dependencies of target main
[ 37%] Building CXX object CMakeFiles/main.dir/main.cc.o
[ 50%] Linking CXX executable main
[ 50%] Built target main
Scanning dependencies of target mylib_32b
[ 62%] Building CXX object CMakeFiles/mylib_32b.dir/mylib.cc.o
[ 75%] Linking CXX static library libmylib_32b.a
[ 75%] Built target mylib_32b
Scanning dependencies of target main_32b
[ 87%] Building CXX object CMakeFiles/main_32b.dir/main.cc.o
[100%] Linking CXX executable main_32b
ld: warning: ignoring file CMakeFiles/main_32b.dir/main.cc.o, file was built for i386 which is not the architecture being linked (x86_64): CMakeFiles/main_32b.dir/main.cc.o
ld: warning: ignoring file libmylib_32b.a, file was built for archive which is not the architecture being linked (x86_64): libmylib_32b.a
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [main_32b] Error 1
make[1]: *** [CMakeFiles/main_32b.dir/all] Error 2
make: *** [all] Error 2
我在这里错过了什么?
===
更新:奇怪的是,设置 CMAKE_CXX_FLAGS
以包含 -m32
会使示例工作。但是,我想在不设置变量的情况下完成它,即遵循现代基于目标的方法。
为了简化项目的维护,我建议您保持构建系统尽可能简单,而不是配置和构建项目 两次:
- 64 位二进制文件的一个构建
32 位二进制文件的一个构建:
- 通过将
CXXFLAGS
和CFLAGS
环境变量设置为-m32
(方法 1) - 或者通过设置三个环境变量
AS
、CXX
和CC
(方法二)
- 通过将
简化您的示例项目
还要确保添加 cmake_minimum_required
,否则会出现错误 VERSION not allowed unless CMP0048 is set to NEW
。
cmake_minimum_required(VERSION 3.10)
project (Link VERSION 0.91 LANGUAGES CXX)
add_library ( mylib STATIC mylib.cc )
add_executable ( main main.cc )
target_link_libraries ( main PRIVATE mylib )
通过拥有一个不对工具链进行硬编码假设的更简单的构建系统,您隐式地启用了对跨平台和不同环境(如 ARM 等)的支持……它还可以使持续集成更容易。例如,在 CircleCI 上,您将有两个构建作业(一个用于 64 位,一个用于 32 位)都构建一个简单的项目。
安装所需的 i386 库
在 Ubuntu 上,可以这样做
sudo apt-get install \
gcc-multilib \
g++-multilib \
libc6:i386 \
libstdc++6:i386
其他依赖项将使用 packageName:i386
循序渐进:方法 1
假设我们有以下目录结构:
<root>
|
|-src
| |--- CMakeLists.txt
| |--- main.cc
| |--- mylib.cc
|
|-build
| |-- ...
|
|-build-32
|-- ...
您只需执行以下操作即可编译 32 位版本:
CFLAGS=-m32 CXXFLAGS=-m32 cmake -Hsrc -Bbuild-32
循序渐进:方法 2
方法二的目标是引入交叉编译的思想。
在最后一节中,您将了解 dockcross/linux-32
docker 个在内部应用相同原理的图像。
为 as
、gcc
和 g++
创建三个包装器脚本
以下是三个shell脚本的内容:
- i686-linux-gnu-as
#!/bin/bash
exec as -m32 "$@"
- i686-linux-gnu-gcc
#!/bin/bash
exec gcc -m32 "$@"
- i686-linux-gnu-g++
#!/bin/bash
exec g++ -m32 "$@"
编译
假设我们有以下目录结构:
<root>
|-bin
| |- i686-linux-gnu-as
| |- i686-linux-gnu-g++
| |- i686-linux-gnu-gcc
|
|-src
| |--- CMakeLists.txt
| |--- main.cc
| |--- mylib.cc
|
|-build
| |-- ...
|
|-build-32
|-- ...
你会分别做
64位$ cmake -Hsrc -Bbuild; cmake --build ./build
-- The CXX compiler identification is GNU 5.2.1
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/scratch/build
[...]
[100%] Built target main
$ file ./build/main
./build/main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e28e610f85cd4a2ab29e38ed58c1cb928f4aaf33, not stripped
$ ./build/main
4
32位
$ CXX=$(pwd)/bin/i686-linux-gnu-g++ \
CC=$(pwd)/bin/i686-linux-gnu-gcc \
AS=$(pwd)/bin/i686-linux-gnu-as linux32 \
bash -c "cmake -Hsrc -Bbuild-32; cmake --build ./build-32/"
-- The CXX compiler identification is GNU 5.2.1
-- Check for working CXX compiler: /tmp/scratch/bin/i686-linux-gnu-g++
-- Check for working CXX compiler: /tmp/scratch/bin/i686-linux-gnu-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
[...]
[100%] Built target main
$ file ./build-32/main
./build-32/main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b7f5781f66a28e28d28eda2b798b671c1e89e22a, not stripped
$ ./build-32/main
4
现在要了解为什么 sizeof(int)
在 64 位和 32 位版本上相同,请考虑阅读 C/C++: sizeof(short), sizeof(int), sizeof(long), sizeof(long long), etc... on a 32-bit machine versus on a 64-bit machine
使用 dockcross 简化编译
现在,要轻松编译为 32 位,您还可以使用 dockcross 映像 dockcross/linux-x86
。参见 https://github.com/dockcross/dockcross#readme
docker pull dockcross/linux-x86
docker run -ti --rm dockcross/linux-x86 > dockcross-linux-x86
chmod u+x dockcross-linux-x86
然后编译,你会做:
dockcross-linux-x86 bash -c "cmake -Hsrc -Bbuild-32; cmake --build ./build-32/"
-m32
标志不是 "inherited" 用于链接目的:
target_compile_options ( <lib> PUBLIC -m32 )
target_link_libraries ( <target> PRIVATE <lib> ) // Does not link with `-m32`.
请注意,由于 target_link_libraries
"inherits" [=16=,上述导致 <target>
编译 -m32
] 来自 <lib>
的编译选项。但是,该标志是 而不是 传递给链接器的。
而且没有target_link_options
命令,所以无法插入target_link_options ( <link> PUBLIC -m32 )
行来解决问题。
相反,根据 this answer(稍作修改),正确的方法是
target_compile_options ( <lib> PUBLIC -m32 )
set_target_properties ( <target> PROPERTIES LINK_FLAGS -m32 )
target_link_libraries ( <target> PRIVATE <lib> )
添加到target_link_options
命令因此你可以写:
target_link_options(<target> PRIVATE "-m32")