#c #cmake #static-libraries #static-linking
Вопрос:
У меня есть простой проект (для Zephyr RTOS, но я думаю, что мой вопрос общий)
│ CMakeLists.txt
│ prj.conf
│ README.rst
│ sample.yaml
│
└───src
│ lib_os_glue.c
│ main.c
│
└───lib
lib.c
lib.h
lib_os_glue.h
Идея заключается в том, что lib
папка становится статической библиотекой, которая может использовать вызовы ядра Zephyr. Поэтому я хотел бы скомпилировать эти вызовы ядра в клеевой слой ОС, чтобы библиотека не зависела от него.
В настоящее время проект строится и может запускаться, но в результате liblib.a
я вижу, что lib_os*
вызовы не определены. (например UND lib_os_msleep
, в readelf -aW liblib.a
выводе.
В CMakeLists.txt:
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_world)
target_sources(app PRIVATE src/main.c)
target_sources(app PRIVATE src/lib_os_glue.c)
add_library(lib STATIC src/lib/lib.c)
target_include_directories(lib PUBLIC src/lib)
target_link_libraries(app PRIVATE lib)
Остальные файлы
главная.c:
#include <zephyr.h>
#include <sys/printk.h>
#include "lib.h"
void main(void)
{
for (;;)
{
printk("Hello World! %sn", CONFIG_BOARD);
lib_msleep(1000);
}
}
lib_os_glue.h:
#ifndef LIB_OS_GLUE_H
#define LIB_OS_GLUE_H
#include <stdint.h>
void lib_os_msleep (uint32_t ms_to_sleep);
#endif // LIB_OS_GLUE_H
lib_os_glue.c:
#include "lib_os_glue.h"
#include <stdint.h>
#include <zephyr.h>
void lib_os_msleep (uint32_t ms_to_sleep)
{
k_msleep(ms_to_sleep);
}
lib/lib.h:
#ifndef LIB_H
#define LIB_H
#include <stdint.h>
void lib_msleep (uint32_t ms_to_sleep);
#endif // LIB_H
lib/lib.c:
#include "lib.h"
#include "lib_os_glue.h"
#include <stdint.h>
void lib_msleep (uint32_t ms_to_sleep)
{
lib_os_msleep(ms_to_sleep);
}
Я надеюсь, что я близок, но я просто не вижу последнего шага(ов).
Я добавляю клей не к той цели? Неправильные интимные отношения? Неправильный порядок ссылок? Я перепробовал множество комбинаций и вариантов.
Комментарии:
1. Это похоже на построение абстракции над абстракцией над абстракцией над абстракцией. Просто используй
k_msleep
.2. @KamilCuk, только это не так. Цель состоит в том, чтобы научиться создавать статическую библиотеку без зависимостей от ОС (я не хочу, чтобы они были связаны), поэтому мне нужен слой клея для ОС (и, конечно, сама библиотека).
3. У вас уже есть клеевой слой OS —
k_msleep
. И все остальные функции Zephyr. Но ладно, один слой все еще в порядке — иногда вам нужна пользовательская функциональность. Но двое?lib_msleep -> lib_os_msleep -> k_sleep -> actual sleep
? Именноlib_os_msleep
тогда.4. Это просто операционная система, а не клеевой слой. Предположим, я хочу создать сложную библиотеку (поэтому не включаю ее в качестве исходного кода), которая использует множество различных вызовов ОС, мне понадобится этот клеевой слой (в противном случае он не может использоваться повторно), и его нужно будет связать с библиотекой для разных проектов на разных архитектурах, чтобы он работал на них.
Ответ №1:
lib_os_msleep
определяется в lib_os_glue.c
. (Он также объявлен в lib_os_glue.h
каждом файле, который включает это, но это всего лишь объявление, а не определение.) Если вы прочтете свой CMafefiles.txt внимательно, вы можете видеть, что вы не компилируете lib_os_glue.c
в свою lib
цель, а вместо этого вы компилируете ее в app
цель. Поэтому определение этой функции не будет помещено в liblib.a
. Когда компоновщик свяжет вашу конечную программу, ему нужно будет найти определение этой функции в другом месте, поскольку она не определена в liblib.a
. Я надеюсь, что это объясняет, почему эта функция отображается как «неопределенная» при проверке liblib.a
.
Похоже, ваша программа успешно работает, и я не понимаю, какова ваша конечная цель, поэтому я не могу сказать вам, следует ли вам делать что-то по-другому. Может быть, все в порядке. Если вы хотите, чтобы эта функция была частью библиотеки, просто переместите файл, который ее определяет, в библиотеку.
Комментарии:
1. Спасибо за ваш ответ! Цель состоит в том, чтобы научиться создавать статическую библиотеку, которая должна использовать вызовы ОС, но иметь возможность компилировать ее самостоятельно. Отсюда и клеевой слой ОС. В конце концов, я хотел бы иметь возможность импортировать
liblib.a
и включатьlib_os_glue.c
в сборку конкретную ОС, в зависимости от целевой ОС. В настоящее время он успешно строит/связывает, ноlib_os_msleep()
функция не вызывается. Что кажется странным..2. Это странно. Существует только одно определение
lib_os_msleep
в поле зрения, поэтому, если ваша программа правильно ссылается, я бы ожидал, что все вызовы этой функции будут соответствовать правильному определению. Статическая библиотека вполне может ссылаться на некоторые внешние функции. Возможно, у вашего компилятора есть какие-то странные причуды.
Ответ №2:
Нашел его! Спасибо тебе, Дэвид Грейсон, ты направил меня в правильном направлении.
Я добавил одну строку, чтобы убедиться, что реализация клея ОС приложения связана с библиотекой: target_link_libraries(lib PRIVATE app)
Полное CMakeLists.txt
:
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_world)
target_sources(app PRIVATE src/main.c)
target_sources(app PRIVATE src/lib_os_glue.c)
add_library(lib STATIC src/lib/lib.c)
target_include_directories(lib PUBLIC src/lib)
target_link_libraries(lib PRIVATE app)
target_link_libraries(app PRIVATE lib)
И я также могу использовать библиотеку в качестве IMPORTED
:
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_world)
target_sources(app PRIVATE src/main.c)
target_sources(app PRIVATE src/lib_os_glue.c)
add_library(lib STATIC IMPORTED)
set_property(TARGET lib PROPERTY
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/liblib.a)
set_property(TARGET lib PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src/lib)
target_link_libraries(app PUBLIC lib)