Использование --whole-archive в CMake для создаваемой вами библиотеки

У меня есть проект CMake, когда я создаю статическую библиотеку и связываю ее с другим кодом в исполняемый файл. По причинам, в которые я не буду вдаваться, я хочу, чтобы это связывание происходило с флагом компоновщика --whole-archive.

Теперь этот флаг хитрый, так как вы не можете просто добавить его куда угодно — вам нужно переключить его, затем перечислить библиотеки, для которых вы хотите его применить, а затем отключить его.

Я где-то читал (URL ускользает от меня), что если у вас есть уже существующая библиотека, вы можете эффективно добавить эти флаги компоновщика, выполнив следующие действия:

# Just an example, find_library calls should really be isolated to separate find modules
find_library(FOO_LIBRARY foo)
set(FOO_LIBRARY "-Wl,--whole-archive ${FOO_LIBRARY} -Wl,--no-whole-archive")

add_executable(hello main.c)
target_link_libraries(hello ${FOO_LIBRARY})

и это нормально. Но что, если вы создаете статическую библиотеку, для которой у вас нет уже существующей переменной (т. е. что-то, для чего у вас есть команда add_library() CMake)? Вам нужно вручную указать его путь вместо ${FOO_LIBRARY}? Или есть какой-то другой трюк, который вы можете использовать, чтобы получить путь, который CMake укажет для него в командной строке?

Кроме того, если бы я использовал какую-то ${FOO_LIBRARY}-подобную строку вместо моего целевого идентификатора статической библиотеки - я полагаю, что CMake мог бы пропустить зависимость, т. е. он мог бы не повторно связываться с измененной библиотекой (или даже не создавать ее), потому что Команда target_link_libraries увидит странную строку, а не идентификатор другой цели.


person einpoklum    schedule 16.04.2017    source источник


Ответы (1)


Это можно сделать с помощью:

add_dependencies(the_exe the_static_lib)
set_target_properties(the_exe LINK_FLAGS
    "-L/path/to/the_static_lib -Wl,--whole-archive,-lthe_static_lib,--no-whole-archive")

Значение -L/path/to/the_static_lib прозрачно следует из того, что вы указываете в качестве выходного каталога цели, или выходного каталога цели по умолчанию, если вы этого не сделаете. Например. если the_static_lib просто выводится в каталог сборки, то -L/path/to/the_static_lib будет -L.

Вот проект для иллюстрации в достаточно общем виде, где у нас есть структурированные подкаталоги для источников и заголовков и отдельные обычные выходные каталоги, bin, lib

$ ls -R
.:
app  build  CMakeLists.txt  foobar  inc

./app:
main.c

./build:

./foobar:
bar.c  foo.c

./inc:
foobar.h

С файлами:

приложение/main.c

#include <foobar.h>

int main(void)
{
    foo();
    bar();
    return 0;
}

foobar/foo.c

#include <foobar.h>
#include <stdio.h>

void foo(void)
{
    puts(__func__);
}

foobar/bar.c

#include <foobar.h>
#include <stdio.h>

void bar(void)
{
    puts(__func__);
}

inc/foobar.h

#ifndef FOOBAR_H
#define FOOBAR_H

void foo(void);
void bar(void);

#endif

CMakeLists.txt (1)

cmake_minimum_required(VERSION 3.0)
project(app)
include_directories(inc)
add_library(foobar STATIC foobar/foo.c foobar/bar.c)
set_target_properties(foobar
    PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
add_executable(app app/main.c)
add_dependencies(app foobar)
set_target_properties(app PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
    LINK_FLAGS "-Llib -Wl,--whole-archive,-lfoobar,--no-whole-archive")

Мы хотим сделать ./build/lib/libfoobar.a из ./foobar/foo.c и ./foobar/bar.c. И мы хотим сделать ./build/bin/app из ./app/main.c, связав весь архив ./build/lib/libfoobar.a

Сгенерируйте, соберите и запустите:

$ cd build; cmake ..; make; ./bin/app
-- The C compiler identification is GNU 8.2.0
-- The CXX compiler identification is GNU 8.2.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- 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: /home/imk/develop/so/cmake_prob/build
Scanning dependencies of target foobar
[ 20%] Building C object CMakeFiles/foobar.dir/foobar/foo.c.o
[ 40%] Building C object CMakeFiles/foobar.dir/foobar/bar.c.o
[ 60%] Linking C static library lib/libfoobar.a
[ 60%] Built target foobar
Scanning dependencies of target app
[ 80%] Building C object CMakeFiles/app.dir/app/main.c.o
[100%] Linking C executable bin/app
[100%] Built target app
foo
bar

Аналогично, если, например. вместо этого CMakeLists.txt имеем:

CMakeLists.txt (2)

cmake_minimum_required(VERSION 3.0)
project(app)
include_directories(inc)
add_subdirectory(foobar)
add_executable(app app/main.c)
add_dependencies(app foobar)
set_target_properties(app PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
    LINK_FLAGS "-Llib -Wl,--whole-archive,-lfoobar,--no-whole-archive")

и отдельно:

foobar/CMakeLists.txt (1)

add_library(foobar STATIC foo.c bar.c)
set_target_properties(foobar
    PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

И с тем же разделением, но с выходными каталогами по умолчанию, мы бы имели:

CMakeLists.txt (3)

cmake_minimum_required(VERSION 3.0)
project(app)
include_directories(inc)
add_subdirectory(foobar)
add_executable(app app/main.c)
add_dependencies(app foobar)
set_target_properties(app PROPERTIES
    LINK_FLAGS "-Lfoobar -Wl,--whole-archive,-lfoobar,--no-whole-archive")

foobar/CMakeLists.txt (2)

add_library(foobar STATIC foo.c bar.c)

что будет выглядеть так:

$ cd build; cmake ..; make; ./app
-- The C compiler identification is GNU 8.2.0
-- The CXX compiler identification is GNU 8.2.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- 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: /home/imk/develop/so/cmake_prob/build
Scanning dependencies of target foobar
[ 20%] Building C object foobar/CMakeFiles/foobar.dir/foo.c.o
[ 40%] Building C object foobar/CMakeFiles/foobar.dir/bar.c.o
[ 60%] Linking C static library libfoobar.a
[ 60%] Built target foobar
Scanning dependencies of target app
[ 80%] Building C object CMakeFiles/app.dir/app/main.c.o
[100%] Linking C executable app
[100%] Built target app
foo
bar
person Mike Kinghan    schedule 02.03.2019
comment
Это немного хакерски, но, похоже, это работает. - person einpoklum; 02.03.2019
comment
Это не работает 100% add_dependencies(app foobar) говорит, строить app после foobar. Но нет: если foobar нужно перестроить, app тоже нужно перестроить! См. документацию. Лучше еще добавить target_link_libraries(app foobar), даже если по этой ссылке ничего или только части. - person kuga; 25.03.2020