четверг, 15 октября 2015 г.

Статические библиотеки в QMake в Linux

Статическая сборка — удобное и практичное решение. Особенно в Linux, где бинарная совместимость между разными дистрибутивами и даже между разными версиями одного дистрибутива регулярно нарушается.

Для проектов среды разработки QtCreator, использующих систему сборки qmake (у таких проектов имя файла имеет формат "*.pro"), есть ряд простых рецептов по части статической сборки.

Выборочная линковка: 1-2 в статике, остальное в динамике

Если надо статически прилинковать лишь парочку библиотек, а всё остальное подключать как .so, для QMake можно сделать так:
 # Подключаем библиотеку libminijava.so
!msvc:LIBS += -Wl,-Bstatic -lminijava -Wl,-Bdynamic
Условие "!msvc" заставляет qmake применять эти настройки только для компиляторов G++ и Clang++, но не для Visual C++. Тем самым мы защищаем Visual C++ от непонятных для него флагов.

Линковка библиотеки из /usr/local

Вы установили библиотеку в usr local? Её можно подключить так:
LIBS += /usr/local/lib

Линковка библиотеки в составе проекта

Речь, разумеется, идёт о проектах типа TEMPLATE = subdir, которые содержат внутри себя другие проекты (аналогично файлу .sln в Visual Studio).

Вы собираете библиотеку как часть проекта, и она тоже собирается через QMake? Есть два варианта:
  • подключить либу из теневой папки сбоки
  • перенаправить сборку либы в папку с исходным кодом.

В чём разница? По умолчанию, QtCreator кладёт файлы сборки отдельно от папки с исходниками, причём имя целевой папки зависит от имени выбранного комплекта (Kit). Это явление называется теневой сборкой.

Процесс перенаправления сборки сложнее, так что о нём читайте ниже. Для линковки библиотеки libsuper.so, собираемой в теневой папке как часть проекта QtCreator, есть следующий способ:
LIBS += -L"$$OUT_PWD/../libsuper.so"
LIBS += -lsuper
Строка "$$OUT_PWD" раскроет переменную OUT_PWD.

Я слинковал, а программа не запускается!

Не запускается потому, что Linux, в отличие от Windows, не будет искать .so файлы в той же директории, где лежит исполняемый файл. Такое ограничение действует уже десятки лет ради безопасности.

Библиотеки, необходимые для программы, можно поместить рядом с ней и затем при сборке программы заставить Linux всё-таки искать библиотеки рядом с исполняемым файлом. Это делается с помощью флагов компоновщика ld; в проекте QtCreator, достаточно добавить три строчки:
unix:!macx {
    QMAKE_LFLAGS += '-Wl,-rpath,\'\$$ORIGIN\',-z,origin'
}

Хотите, чтобы библиотеки находились в подпапке libs рядом с исполняемым файлом? Нет проблем:
unix:!macx {
    QMAKE_LFLAGS += '-Wl,-rpath,\'\$$ORIGIN/libs\',-z,origin'
}

Подробнее о том, как создавать распространяемые архивы с программами для Linux без DEB/RPM пакетов, читайте здесь: sergey-shambir.blogspot.ru/2015/06/ubuntu-root.html

Перенаправление пути сборки исполняемых файлов

Здесь потребуется объявить в qmake новую функцию через defineReplace, и использовать её везде, где требуется получить перенаправленный путь к файлу.
В предложенном ниже решении функция поможет разложить собираемые библиотеки и бинарники в подпапки ~build/release-x64, ~build/debug-x64, ~build/release-x32, ~build/debug-x32 внутри папки с исходным кодом проекта в зависимости от конфигурации сборки.

defineReplace(inferTargetDir) {
    buildSubdir = release-x64
    debug {
        equals(QT_ARCH, x86_64) {
            buildSubdir = debug-x64
        } else {
            buildSubdir = debug-x32
        }
    } else {
        !contains($$QT_ARCH, x86_64) {
            buildSubdir = release-x32
        }
    }
    buildDir=$$PWD/../~build/$$buildSubdir
    return($$buildDir)
}

defineReplace(inferTarget) {
    buildTargetPath=$$inferTargetDir()/$$1
    return($$buildTargetPath)
}

Затем в своей статической библиотеке сделайте так:
TARGET = "$$inferTarget(minijava)"

А в каждом использующем её подпроекте - так:
LIBS += -L"$$inferTargetDir()" -Wl,-Bstatic -lminijava -Wl,-Bdynamic
PRE_TARGETDEPS += $$inferTargetDir()/libminijava.a

Последняя строчка обеспечит вам ещё и пересборку бинарника в случае, когда библиотека libminijava.a изменяется. Да-да, сам по себе qmake не будет следить за модификацией данной библиотеки — пока вы не заставите его это делать.

Комментариев нет:

Отправить комментарий