Статическое связывание SDL2 / SD2_image с CMake - PullRequest
3 голосов
/ 17 апреля 2020

Я пытаюсь добавить SDL2 в качестве библиотеки в мой проект. Я хочу связать это статически. Я новичок в c ++.

1 - Почему веб-сайт SDL рекомендует динамически ссылаться, когда это возможно?

Я понимаю преимущества динамических c библиотек. Однако предположить, что пользователи будут иметь все необходимые библиотеки, уже установленные и готовые к go в своей системе, - довольно большое предположение IMO. Единственный случай, когда динамическое связывание звучит для меня как хорошая идея, это когда вы используете хорошо известные библиотеки, которые поставляются с ОС / платформой. https://wiki.libsdl.org/Installation

2 - динамическое связывание, по-видимому, автоматически находит зависимости c от (SDL2 и SDL2_image). Связывать статически нет. Почему это так? Вот мой FindSDL2_image.cmake файл


find_path(SDL2_IMAGE_INCLUDE_DIR SDL_image.h)
include_directories(${SDL2_IMAGE_INCLUDE_DIR})

# PREFER STATIC LIBRARIES ########
# cmake respects the order of extensions when looking for libraries
SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
# ------------------- ########

find_library(SDL2_IMAGE_LIBRARY NAMES SDL2_image PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX})
set(SDL2_IMAGE ${SDL2_IMAGE_LIBRARY})

Статически связывает sdl2_image. Это не связывает должным образом, потому что Undefined symbols:

  "_png_set_strip_16", referenced from:
      _IMG_LoadPNG_RW in libSDL2_image.a(IMG_png.o)
  "_png_set_write_fn", referenced from:
      _IMG_SavePNG_RW_libpng in libSDL2_image.a(IMG_png.o)
  "_png_write_png", referenced from:
      _IMG_SavePNG_RW_libpng in libSDL2_image.a(IMG_png.o)

Если я удаляю секцию ### PREFER STATIC LIBRARIES ## в файле cmake. Он связывает динамически, и все работает, как ожидалось. Почему при динамическом связывании разрешаются зависимости intrinsi c, но не при статическом связывании?

---- ОБНОВЛЕНИЕ ----

Мне удалось связать sdl2_image статически, явно включив его зависимости

find_library(PNGLIB png)
find_library(JPEG jpeg)
find_library(TIFF tiff)
find_library(WEBP webp)
find_library(LZ z)

target_link_libraries(smb ${SDL2} ${PNGLIB} ${JPEG} ${TIFF} ${WEBP} ${SDL2_IMAGE} ${LZ})

Однако, это не будет хорошо масштабироваться для меня. Выяснение того, что эти зависимости заняли немного догадок и поиска в Google. В идеале я бы хотел, чтобы CMAKE автоматически их вытянул.

Ответы [ 4 ]

1 голос
/ 28 апреля 2020

похоже, есть несколько вопросов, я постараюсь ответить на вопрос шаг за шагом:

Почему веб-сайт SDL рекомендует динамически устанавливать ссылки по возможности

Одной из причин динамического связывания приложения с библиотекой является разделение приложения с библиотекой (в данном случае это называется разделяемая библиотека / .so). Вы можете обновить свою библиотеку без необходимости перекомпиляции кода приложения. Например, если вы завершили свой проект и на вашем клиенте запущено ваше приложение, я полагаю, вы вряд ли захотите перекомпилировать код приложения, как только обнаружится ошибка в используемой вами базовой библиотеке.

С другой стороны, связывая ваше приложение statically, вы связываете свое приложение с этой библиотекой (.lib или .a форма). Значит, каждое изменение в библиотеке заставит вас перекомпилировать ваш код. Иногда это требуется, например, вы предоставляете вашему клиенту гарантию вашего приложения, обычно вы хотите быть уверены, что никакие будущие проблемы с вашей библиотекой не приведут к сбою вашего приложения.

У меня есть короткий пример кода, чтобы лучше это понять: CMakeLists.txt:

cmake_minimum_required (VERSION 3.0)
project(linkageLibrary)

set(STATIC_LIB lib_static)
set(SHARE_LIB lib_share)

set(STATIC_OTHER_LIB lib_otherstatic)
set(SHARE_OTHER_LIB lib_othershare)


add_library(${STATIC_LIB} STATIC ${STATIC_LIB}.cpp)
add_library(${SHARE_LIB} SHARED ${SHARE_LIB}.cpp)

# not yet in usage...
add_library(${STATIC_OTHER_LIB} STATIC ${STATIC_OTHER_LIB}.cpp)
add_library(${SHARE_OTHER_LIB} SHARED ${SHARE_OTHER_LIB}.cpp)

add_executable(${CMAKE_PROJECT_NAME} main.cpp)
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${${CMAKE_PROJECT_NAME}_SOURCE_DIR})
target_link_libraries(${CMAKE_PROJECT_NAME} ${STATIC_LIB} ${SHARE_LIB})

file(WRITE ${CMAKE_BINARY_DIR}/exchangeShareLibrary.sh "
 echo \"before exchange the static library\"
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME} &&
 mv ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so.bk &&
 cp ${CMAKE_BINARY_DIR}/lib${SHARE_OTHER_LIB}.so ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so &&
 echo \"after the shared library has been changed\" &&
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}")

file(WRITE ${CMAKE_BINARY_DIR}/exchangeStaticLibrary.sh "
 echo \"before exchange the static library\"
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME} &&
 mv ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}.a ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}a.bk &&
 cp ${CMAKE_BINARY_DIR}/lib${STATIC_OTHER_LIB}.a ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}.a &&
 echo \"after the static library has been changed\" &&
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}")

main. cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

int main() {
    printStaticLib();
    printShareLib();
    return 0;
}

lib.hpp:

#pragma ONCE

void printStaticLib();
void printShareLib();

lib_stati c. cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printStaticLib() {
    cout << "linkage of lib_static" << endl;
}

lib_share. cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printShareLib() {
    cout << "linkage of lib_share" << endl;
}

lib_otherstati c. cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printStaticLib() {
    cout << "linkage of the other lib_static with other text" << endl;
}

lib_othershare. cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printShareLib() {
    cout << "linkage of the other lib_share with other text" << endl;
}

если вы запустите сгенерированные сценарии, вы заметите, что printShareLib() будет выводиться по-другому после обмена .so get, но не printStaticLib(). (Попробуйте еще раз make без очистки, если вы сейчас выполните ./linkageLibrary, на этот раз вы получите и другие выходные данные для printStaticLib(). Вы можете догадаться, почему?)

с точностью до секунды проблема:

2 - динамическое связывание, по-видимому, автоматически находит зависимости intrinsi c (SDL2 и SDL2_image). Связывать статически нет. Почему это так?

, не зная настройки вашей системы, я полагаю, библиотека .a stati c не может быть найдена вашей системой сборки. Попытайтесь найти, где это находится, и в конце концов верните find_library(SDL2_IMAGE_LIBRARY NAMES SDL2_image HINTS ${_YOUR_SDL2_INSTALLATION_PATH}) link

, теперь возвращаясь к вашему вопросу, почему SDL2 предложил динамически связывать, ну, это решение разработчика библиотеки, как вы могли прочитать в его README

Пожалуйста, обратитесь также к этому блогу о том, как использовать SDL2 с CMake

1 голос
/ 28 апреля 2020

Библиотека Stati c - это просто набор скомпилированных объектных файлов. Он не имеет никакой дополнительной информации о своих зависимых библиотеках. Вы можете использовать команду ar -t &ltstatic_lib&gt для просмотра содержимого библиотеки stati c. Поэтому, если вам нужно связать свой исполняемый файл с библиотекой stati c, вы должны указать имена и пути всех зависимых от нее библиотек.

Например: рассмотрим две stati c libs, A и B и предположим, что A зависит от B. Тогда, если вы хотите, чтобы ваш exe C связывался с A, тогда ваш оператор link должен содержать как A, так и B. Тогда только все символы будут определены правильно. Линкер: C ===> A и B

Динамическая c библиотека отличается и более интеллектуальна, чем stati c. Он имеет формат ELF, а его заголовки содержат информацию о зависимых общих библиотеках. Это можно увидеть, введя команду ldd &ltdynamic_lib&gt. Кроме того, динамический c загрузчик знает во время выполнения, чтобы выбрать зависимые библиотеки из стандартных расположений. Если он не может найти, он выдаст ошибку. В вашем случае вы найдете информацию о зависимых библиотеках вашей динамической библиотеки c в выходных данных ldd и, вероятно, все эти библиотеки будут находиться в стандартном месте. Вот почему вы не находите ошибку, когда пытаетесь динамически связать.

Вот несколько полезных ссылок, чтобы узнать больше об этом, https://renenyffenegger.ch/notes/development/dynamic-loader https://amir.rachum.com/blog/2016/09/17/shared-libraries/

1 голос
/ 17 апреля 2020

Когда вы делаете find_package в CMake, будет искать файл Findxxx.cmake в некоторых CMAKE-определенных путях.

Команда имеет два режима поиска пакетов: режим «Модуль» и режим «Конфигурация». Режим модуля доступен, когда команда вызывается с приведенной выше сокращенной подписью. CMake ищет файл с именем Find.cmake в CMAKE_MODULE_PATH с последующей установкой CMake.

(https://cmake.org/cmake/help/latest/command/find_package.html)

Итак, вам нужно определить свой собственный FindSDL2.cmake, который скажет, где находится библиотека. (https://cmake.org/cmake/help/v3.17/manual/cmake-developer.7.html)

И вам нужно сказать find_package, чтобы найти свой FindSDL2.cmake. Для этого вы можете передать путь к find_package.

Если вы заставите CMake использовать ваш файл, переменные ${SDL2_INCLUDE_DIRS} и ${SDL2_LIBRARIES} будут теми, которые вы определили в своем файле.

0 голосов
/ 22 апреля 2020

Здесь есть несколько возможностей ..

Самое простое объяснение состоит в том, что у вас установлен динамический c libpng.so.x где-нибудь, где ваш компоновщик динамического c может его найти (/ usr / lib , / usr / local / lib, et c), но не имеют статуса c libpng.a. В этом случае ответом будет установить stati c libpng.a и посмотреть, сможет ли магия cmake c разрешить его.

Следующее простое объяснение состоит в том, что в вашей системе нет libpng. *. и происходит некоторое позднее динамическое связывание c. Это может произойти, если, например, программное обеспечение, которое вы создаете, также собирает libpng. * В этом случае позднее связывание позволит вам успешно динамически связать с ним ваше приложение, даже если libpng еще не был установлен в / usr / lib или / usr / local / lib или где бы программное обеспечение вы не создавали, оно будет в конечном итоге размещено. Это что-то вроде ситуации с курицей и яйцом. Вы можете понять это, введя «ldd foo», где «foo» - это имя вашего приложения. Вывод будет представлять собой список всех вещей, которые динамический компоновщик c должен получить для вашего исполняемого файла, libpng должен быть в списке, и для него должен быть указан разрешенный путь, если нет, возможно, ваше приложение может запуститься. но впоследствии cra sh, если, например, в какой-то момент вызывается png_write_png (). Как обойти эту проблему можно было бы установить библиотеки libpng. *, Которые ожидает ваше приложение, перед сборкой.

Надеюсь, это немного поможет:)

...