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 位二进制文​​件的一个构建:

    • 通过将 CXXFLAGSCFLAGS 环境变量设置为 -m32(方法 1)
    • 或者通过设置三个环境变量ASCXXCC(方法二)

简化您的示例项目

还要确保添加 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 个在内部应用相同原理的图像。

asgccg++

创建三个包装器脚本

以下是三个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")