Связывание абстракции ОС со статической библиотекой с помощью CMake, но абстракция ОС остается неопределенной в библиотеке?

#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)