Все технические форумы на одном сайте Удобный поиск информации с популярных форумов в одном месте
Вопрос: Как выполнить статическую линковку библиотек?

Такая ситуация: скомпилировал на ноуте консольное приложение, на уровне хеллоу ворлда, а на персональном компе оно при запуске выдает системную ошибку (отсутствие файла libgcc_s_dw2-1.dll), в свою очередь скомпилированное на персональнике выдает ошибку на ноуте (отсутствие файла libgcc_s_seh-1.dll). Как мне произвести статическую линковку библиотек, чтобы я мог без проблем запускать прогу в других системах?

p.S> gcc 5.0.1 (minGW-w64)
Ответ:
Сообщение от ambrosia
а не подскажете, в какой литературе можно подробнее узнать о линковке?

Как обычно, бесплатную версию можно найти в интернете.
Вопрос: О статической линковке

Приветствую!

Задался вопросом как же правильно строить библиотеки для статической линковки. Вопрос возник после просмотра содержимого результирующего исполняемого файла. Дело в том, что после линковки некоторых библиотек в исполняемый файл попадает много незадействованного кода. Эксперимент оформил шелл-скриптом для *nix:

Скрытый текст
    #!/bin/sh
     
    if [ `whoami` = "root" ]; then
       echo "Под учеткой рута работать отказываюсь."
       exit
    fi
     
    # если clang не установлен - нужно заменить на gcc и g++ соответственно
     
    CC="clang -Wall -pedantic -O3 -c"
    CXX="clang++ -Wall -pedantic -O3 -c"
    LINK1="clang -static -O3"
    LINK2="clang++ -static -O3"
     
    # генерация библиотеки 1 ==================================
    #
    # все функции включаются в одну единицу трансляции
    #
    # =========================================================
     
    printf "#include <stdio.h>\n\n" > testo-1.c
    for i in $(seq 20)
    do
      printf "void SomeFunc_$i() {\n" >> testo-1.c
      printf "  printf(\"SomeFunc_$i\\\n\");\n" >> testo-1.c
      printf "}\n\n" >> testo-1.c
    done
    $CC testo-1.c
    ar rcs libtesto-1.a testo-1.o
    rm -f *.c *.o
     
    # генерация библиотеки 2 ==================================
    #
    # каждая функция включается в отдельную единицу трансляции
    #
    # =========================================================
     
     
    for i in $(seq 20)
    do
      printf "#include <stdio.h>\n\n" > testo-2-$i.c
      printf "void SomeFunc_$i() {\n" >> testo-2-$i.c
      printf "  printf(\"SomeFunc_$i\\\n\");\n" >> testo-2-$i.c
      printf "}\n\n" >> testo-2-$i.c
    done
    $CC testo-2-*.c
    ar rcs libtesto-2.a `ls testo-2-*.o`
    rm -f *.c *.o
     
    # генерация библиотеки 3 ==================================
    #
    # создается класс, где реализация методов включается в одну
    # единицу трансляции
    #
    # =========================================================
     
    printf "#pragma once\n\n" > testo-3.h
    printf "class GodClass {\n" >> testo-3.h
    printf "  public:\n" >> testo-3.h
    for i in $(seq 20)
    do
      printf "    void SomeMethod_$i();\n" >> testo-3.h
    done
    printf "};\n" >> testo-3.h
     
    printf "#include <iostream>\n" > testo-3.cpp
    printf "#include \"testo-3.h\"\n\n" >> testo-3.cpp
    for i in $(seq 20)
    do
      printf "void GodClass::SomeMethod_$i() {\n" >> testo-3.cpp
      printf "  std::cout << \"SomeMethod_$i\" << std::endl;\n" >> testo-3.cpp
      printf "}\n\n" >> testo-3.cpp
    done
    $CXX testo-3.cpp
    ar rcs libtesto-3.a testo-3.o
    rm -f *.cpp *.o
     
    # генерация библиотеки 4 ==================================
    #
    # создается класс, где реализация методов разносится по
    # разным единицам трансляции
    #
    # =========================================================
     
    printf "#pragma once\n\n" > testo-4.h
    printf "class GodClass {\n" >> testo-4.h
    printf "  public:\n" >> testo-4.h
    for i in $(seq 20)
    do
      printf "    void SomeMethod_$i();\n" >> testo-4.h
    done
    printf "};\n" >> testo-4.h
     
    for i in $(seq 20)
    do
      printf "#include <iostream>\n" > testo-4-$i.cpp
      printf "#include \"testo-4.h\"\n\n" >> testo-4-$i.cpp
      printf "void GodClass::SomeMethod_$i() {\n" >> testo-4-$i.cpp
      printf "  std::cout << \"SomeMethod_$i\" << std::endl;\n" >> testo-4-$i.cpp
      printf "}\n\n" >> testo-4-$i.cpp
    done
    $CXX testo-4-*.cpp
    ar rcs libtesto-4.a `ls testo-4-*.o`
    rm -f *.cpp *.o
     
    # линкуем исполняемые файлы ===============================
     
    printf "void SomeFunc_1();\n\n" > testo-1.c
    printf "int main() {\n" >> testo-1.c
    printf "  SomeFunc_1();\n" >> testo-1.c
    printf "}\n" >> testo-1.c
    cp testo-1.c testo-2.c
    $LINK1 testo-1.c -L. -ltesto-1 -o testo-1
    $LINK1 testo-2.c -L. -ltesto-2 -o testo-2
    printf "#include \"testo.h\"\n\n" > testo-3.cpp
    printf "int main() {\n" >> testo-3.cpp
    printf "  GodClass G;\n" >> testo-3.cpp
    printf "  G.SomeMethod_1();\n" >> testo-3.cpp
    printf "}\n" >> testo-3.cpp
    cp testo-3.cpp testo-4.cpp
    mv testo-3.h testo.h
    $LINK2 testo-3.cpp -L. -ltesto-3 -o testo-3
    $LINK2 testo-4.cpp -L. -ltesto-4 -o testo-4
    rm -f *.c* *.h
     
    # анализируем что попало в исполняемые файлы ==============
     
    clear
    echo "Найдено функций в  1-м исполняемом файле: "`nm testo-1 | grep SomeFunc | wc -l`
    echo "Найдено функций во 2-м исполняемом файле: "`nm testo-2 | grep SomeFunc | wc -l`
    echo "Найдено методов в  3-м исполняемом файле: "`nm testo-3 | grep SomeMethod | wc -l`
    echo "Найдено методов в  4-м исполняемом файле: "`nm testo-4 | grep SomeMethod | wc -l`

Результат работы скрипта таков:
    Найдено функций в  1-м исполняемом файле: 20
    Найдено функций во 2-м исполняемом файле: 1
    Найдено методов в  3-м исполняемом файле: 20
    Найдено методов в  4-м исполняемом файле: 1



Как видно, что если разносить функции и методы в разные единицы трансляции, и результат собирать в библиотеку - то "ненужное" при линковке в исполняемый модуль не попадает. Соответственно пара вопросов:

1) Можно ли как-то линкер "упросить" не заносить ненужное в исполняемый файл не используя выше означенный подход?
2) На сколько применим выше означенный подход для С++ (касаемо удаления реализации неиспользуемых методов класса)?

В данной теме прошу не поднимать вопросы проектирования, вопрос именно по линковке.
Ответ:
Цитата cppasm @
В GCC ещё есть LTO
Тоже помогает лишний код выбрасывать.

Это я сразу попробовал, слышал об этом. Но чет не исключало неиспользуемый код.
Вопрос: Статическая линковка OpenAL

Собрал данную библиотеку статической через CMake. Все ОК.
Собираю проект и вижу ошибки следующего вида:
Код:

obj\Debug\sound.o||In function `ZN5SoundD2Ev':|
F:\Projects (C++)\SD\SD\sound.cpp|18|undefined reference to `_imp__alcMakeContextCurrent'|
F:\Projects (C++)\SD\SD\sound.cpp|20|undefined reference to `_imp__alcDestroyContext'|
F:\Projects (C++)\SD\SD\sound.cpp|22|undefined reference to `_imp__alcCloseDevice'|
obj\Debug\sound.o||In function `ZN5Sound13CheckALCErrorEv':|
F:\Projects (C++)\SD\SD\sound.cpp|41|undefined reference to `_imp__alcGetError'|
F:\Projects (C++)\SD\SD\sound.cpp|43|undefined reference to `_imp__alcGetString'|
obj\Debug\sound.o||In function `ZN5Sound4initEv':|
F:\Projects (C++)\SD\SD\sound.cpp|62|undefined reference to `_imp__alcOpenDevice'|
F:\Projects (C++)\SD\SD\sound.cpp|70|undefined reference to `_imp__alcCreateContext'|
F:\Projects (C++)\SD\SD\sound.cpp|75|undefined reference to `_imp__alcMakeContextCurrent'|
F:\Projects (C++)\SD\SD\sound.cpp|79|undefined reference to `_imp__alListenerfv'|
F:\Projects (C++)\SD\SD\sound.cpp|81|undefined reference to `_imp__alListenerfv'|
F:\Projects (C++)\SD\SD\sound.cpp|83|undefined reference to `_imp__alListenerfv'|
obj\Debug\sound_stream.o||In function `ZN11SoundStreamD2Ev':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|15|undefined reference to `_imp__alDeleteBuffers'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream12CheckALErrorEv':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|22|undefined reference to `_imp__alGetError'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|24|undefined reference to `_imp__alGetString'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream4loadEP6Streamb':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|35|undefined reference to `_imp__alGenSources'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|45|undefined reference to `_imp__alSourcef'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|46|undefined reference to `_imp__alSourcef'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|47|undefined reference to `_imp__alSourcefv'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|48|undefined reference to `_imp__alSourcefv'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|50|undefined reference to `_imp__alSourcef'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream8load_oggEP6Stream':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|125|undefined reference to `_imp__alSourcei'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|136|undefined reference to `_imp__alGetSourcei'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|143|undefined reference to `_imp__alGenBuffers'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|167|undefined reference to `_imp__alSourcei'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|170|undefined reference to `_imp__alSourcei'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream12ReadOggBlockEjj':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|215|undefined reference to `_imp__alBufferData'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|222|undefined reference to `_imp__alSourceQueueBuffers'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream4playEv':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|230|undefined reference to `_imp__alSourcePlay'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream4stopEv':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|235|undefined reference to `_imp__alSourceStop'|
obj\Debug\sound_stream.o||In function `ZN11SoundStream5closeEv':|
F:\Projects (C++)\SD\SD\sound_stream.cpp|240|undefined reference to `_imp__alSourceStop'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|241|undefined reference to `_imp__alIsSource'|
F:\Projects (C++)\SD\SD\sound_stream.cpp|242|undefined reference to `_imp__alDeleteSources'|
||=== Build finished: 32 errors, 0 warnings (0 minutes, 0 seconds) ===|

Раньше собирал с shared-библиотекой (то есть в DLL) - все линковалось и работало. Но решил сделать статическую линковку для упрощения программы (что бы не таскать кучу библиотек выгоном).
Посмотрел файл libOpenAL32.a и вижу там объявление всех этих функций:
Код:

FIRST LINKER MEMBER
	Symbols	: 0000012A

	MbrOffs 	Name
	-------------------- 	--------------------
	000018D8  	_alDeleteAuxiliaryEffectSlots
	000018D8  	_alIsAuxiliaryEffectSlot
	000018D8  	_alAuxiliaryEffectSlotf
	000018D8  	_alAuxiliaryEffectSlotfv
	000018D8  	_alGetAuxiliaryEffectSloti
	000018D8  	_alGetAuxiliaryEffectSlotiv
	000018D8  	_alGetAuxiliaryEffectSlotf
	000018D8  	_alGetAuxiliaryEffectSlotfv
	000018D8  	_NoneCreate
	000018D8  	_InitializeEffect
и .т.д.

Всё отличие - в отсутствии "_imp_" в именах
Ответ: Она не обязательна. Новые версии спокойно собираются и работают...
Вопрос: Статическая линковка?

Куку, хотелось бы узнать, можно ли как то упаковать программу(1.exe) и библиотеку (.dll), которую использует программа и которая лежит рядом с ней, в один файл. Долго гуглил, даже дошел до адского названия статическая линковка, но конкретной инфы не удалось найти.

Проект C# (visual studio 2015)
Ответ: салома, качаешь , распаковываешь, включаешь нужные части в проект.
Вопрос: VS2013 LNK1104 - ошибка статической линковки библиотек в консольном приложении

Visual Studio 2013.
В решении три проекта (C++): консольное приложение и две статических библиотеки.
Указываю компоновщику дополнительные библиотеки при помощи директивы #pragma comment(lib, "mylib.lib").
В проекте статической библиотеки это работает. В проекте консольного приложения не работает - получаю LNK1104.
Если указать полный путь, то все линкуется успешно и в проекте консольного приложения. Но прописывать полные пути для каждой конфигурации не удобно и не правильно.

В VS2008, точно помню, все работало без полного пути.

Кто-нибудь знает как это победить в VS2013?
Ответ: Если кому-то интересно, то решение добавить путь в свойствах проекта. То есть в свойствах проекта: Configuration Properties -> VC++ Diretories -> Library Directories=$(OutDir);$(LibraryPath)
После этого проект консольного приложения находит локальные статические библиотеки.
Вопрос: GNU/Linux Qt5.* - Линкуем статически

В связи с отсутствием в данной теме мануала по статической линковке приложений Qt, решил исправить данный недостаток
Оговорюсь сразу: приветствуется беспощадная критика, особенно, что касается скриптов)
Итак:
1) Как обычно, нужно пересобрать фреймвор статически. Для этого скачиваем архив с исходными текстами отсюда:
На данный момент имя тар-бола - qt-everywhere-opensource-src-5.2.0.tar.gz

2) Переходим в каталог с тар-болом. Разархивируем и распаковываем. Потом нужно сконфигурировать, собрать и установить тонну исходников. Делаем это так ( за дополнительными параметрами, если нужно, обращайтесь ./configure --help):
Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tar -xzvf qt-everywhere-opensource-src-5.2.0.tar.gz && cd qt-everywhere-opensource-src-5.2.0 \
&& sudo mkdir -p /usr/lib/Qt5_static && ./configure -platform linux-g++ \
-release \
-static \
-fontconfig \
-opensource \
-confirm-license \
-nomake examples \
-c++11 \
-nomake tests \
-qt-zlib \
-qt-libpng \
-qt-libjpeg \
-prefix /usr/local/Qt5_static \
&& make -j3 && make install
Код
Объяснение опций:
-platform linux-g++ \ # Ваша платформа и компилятор
-release \ # Собираем для релиза
-static \ # Собственно, наша цель собрать статически
-fontconfig \ # Поддержка собственного fontconfig
-opensource \ # СПО версия
-confirm-license \ # Сразу согласиться с лицензией
-nomake examples \ # Не собирать учебные примеры
-c++11 \ # Поддержка последнего стандарта
-nomake tests \ # Не проводить тесты
-qt-zlib \ # Внедрение собственной библиотеки <libname>
-qt-libpng \ # ...
-qt-libjpeg \ # ...
-prefix /usr/local/Qt5_static \ # Относительно этого пути будет производиться установка (можете свой указать)
make -j3 # количество ядер процессора +1
3) Так, теперь у нас есть все необходимые инструменты (почти )
Заходим в QtCreator, Tools > Options > Build & Run > Qt Versions
Добавляем путь к новому qmake ( в нашем случае /usr/local/Qt5_static/bin/qmake ).
Затем идем на вкладку Kits и добавляем новый набор инструментов, только укзываем ему qmake, только что добавленный на предыдущем шаге. Озаглавим этот набор Qt5_staticKit, например. Нажимаем Ok.
4) Ctrl+5, выбираем вверху проект, который мы хотим собрать статически, и щелкаем по новому Kit'y Qt5_staticKit. Выбираем Release конфигурацию. Тааак, здесь пока все.
5) Перелазим в .pro file. Добавляем туда
Код
QMAKE_LFLAGS += -s # Убрать все таблицы символов из результирующего бинарника ( man gcc )
# 3-rd party библиотеки, (boost, gmp, ... ) если есть статический вариант добавляем так:
LIBS += -Wl, -Bstatic, -lboost_regex, -lgmp, -lgmpxx, -Bdynamic
Теперь все Qt и 3-rd party библиотеки сидят в бинарнике, НО дело еще не закончено.
Если посмотреть на вывод ldd, то можно увидеть, что приложение зависит еще от целой кучи не Qt-ных библиотек. Они могут быть бинарно-несовместимыми со своими предыдущими версиями (особенно, если написаны на С++) даже если они имеются на чужом компьютере.
Например:
Кликните здесь для просмотра всего текста
Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
ldd traditional
        linux-vdso.so.1 (0x00007fffd05fe000)
        libgobject-2.0.so.0 => lib/libgobject-2.0.so.0 (0x00007fe1a298a000)
        libX11-xcb.so.1 => lib/libX11-xcb.so.1 (0x00007fe1a2788000)
        libXi.so.6 => lib/libXi.so.6 (0x00007fe1a2578000)
        libxcb-render-util.so.0 => lib/libxcb-render-util.so.0 (0x00007fe1a2375000)
        libSM.so.6 => lib/libSM.so.6 (0x00007fe1a216d000)
        libICE.so.6 => lib/libICE.so.6 (0x00007fe1a1f51000)
        libxcb-glx.so.0 => lib/libxcb-glx.so.0 (0x00007fe1a1d39000)
        libxcb-render.so.0 => lib/libxcb-render.so.0 (0x00007fe1a1b2f000)
        libxcb-keysyms.so.1 => lib/libxcb-keysyms.so.1 (0x00007fe1a192d000)
        libdbus-1.so.3 => lib/libdbus-1.so.3 (0x00007fe1a16e5000)
        libxcb.so.1 => lib/libxcb.so.1 (0x00007fe1a14c5000)
        libxcb-image.so.0 => lib/libxcb-image.so.0 (0x00007fe1a12c1000)
        libxcb-icccm.so.4 => lib/libxcb-icccm.so.4 (0x00007fe1a10bd000)
        libxcb-sync.so.1 => lib/libxcb-sync.so.1 (0x00007fe1a0eb7000)
        libxcb-xfixes.so.0 => lib/libxcb-xfixes.so.0 (0x00007fe1a0cb0000)
        libxcb-shm.so.0 => lib/libxcb-shm.so.0 (0x00007fe1a0aad000)
        libxcb-randr.so.0 => lib/libxcb-randr.so.0 (0x00007fe1a089f000)
        libxcb-shape.so.0 => lib/libxcb-shape.so.0 (0x00007fe1a069b000)
        libxcb-xkb.so.1 => lib/libxcb-xkb.so.1 (0x00007fe1a0480000)
        libxkbcommon.so.0 => lib/libxkbcommon.so.0 (0x00007fe1a0220000)
        libfontconfig.so.1 => lib/libfontconfig.so.1 (0x00007fe19ffe4000)
        libfreetype.so.6 => lib/libfreetype.so.6 (0x00007fe19fd3f000)
        libXrender.so.1 => lib/libXrender.so.1 (0x00007fe19fb35000)
        libXext.so.6 => lib/libXext.so.6 (0x00007fe19f923000)
        libX11.so.6 => lib/libX11.so.6 (0x00007fe19f5e8000)
        libudev.so.1 => lib/libudev.so.1 (0x00007fe19f3d6000)
        libicui18n.so.52 => lib/libicui18n.so.52 (0x00007fe19efce000)
        libicuuc.so.52 => lib/libicuuc.so.52 (0x00007fe19ec55000)
        libpcre16.so.0 => lib/libpcre16.so.0 (0x00007fe19e9f6000)
        libdl.so.2 => lib/libdl.so.2 (0x00007fe19e7f2000)
        libgthread-2.0.so.0 => lib/libgthread-2.0.so.0 (0x00007fe19e5f0000)
        libglib-2.0.so.0 => lib/libglib-2.0.so.0 (0x00007fe19e2f0000)
        librt.so.1 => lib/librt.so.1 (0x00007fe19e0e8000)
        libGL.so.1 => lib/libGL.so.1 (0x00007fe19ddb5000)
        libpthread.so.0 => lib/libpthread.so.0 (0x00007fe19db98000)
        libstdc++.so.6 => lib/libstdc++.so.6 (0x00007fe19d894000)
        libm.so.6 => lib/libm.so.6 (0x00007fe19d591000)
        libgcc_s.so.1 => lib/libgcc_s.so.1 (0x00007fe19d37b000)
        libc.so.6 => lib/libc.so.6 (0x00007fe19cfd1000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fe1a2bdb000)
        libpcre.so.1 => lib/libpcre.so.1 (0x00007fe19cd6a000)
        libffi.so.6 => lib/libffi.so.6 (0x00007fe19cb62000)
        libuuid.so.1 => lib/libuuid.so.1 (0x00007fe19c95d000)
        libXau.so.6 => lib/libXau.so.6 (0x00007fe19c759000)
        libXdmcp.so.6 => lib/libXdmcp.so.6 (0x00007fe19c553000)
        libxcb-util.so.1 => lib/libxcb-util.so.1 (0x00007fe19c34e000)
        libexpat.so.1 => lib/libexpat.so.1 (0x00007fe19c124000)
        libz.so.1 => lib/libz.so.1 (0x00007fe19bf0e000)
        libbz2.so.1.0 => lib/libbz2.so.1.0 (0x00007fe19bcfe000)
        libpng16.so.16 => lib/libpng16.so.16 (0x00007fe19bac9000)
        libicudata.so.52 => lib/libicudata.so.52 (0x00007fe19a25e000)
        libnvidia-tls.so.331.38 => lib/libnvidia-tls.so.331.38 (0x00007fe19a05b000)
        libnvidia-glcore.so.331.38 => lib/libnvidia-glcore.so.331.38 (0x00007fe19784d000)

Пересобирать каждую из них, в случае, когда нет статической, довольно проблематично и долго и не всегда возможно ( кто хочет пусть попробует ), поэтому есть вариант поставлять их вместе со своим приложением. Большая вероятность того, что большинство этих библиотек будут на чужой машине, но выполнив еще немного действий, вы застрахуете и себя, и пользователя от лишней головной боли.
Вот что делаем:
1) Нужно скопировать симлинк, который выводит ldd, и библиотеку на которую он указывает в каталог с нашим приложением. ( в нашем случае /path/to/app/lib ). Симлинк использует динамический загрузчик при загрузке библиотеки для приложения во время выполнения, ну а библиотека, на которую он указывает это собственно данные.

Вот наваял такой скрипт (еще раз напомню, что правки и улучшения очень приветствуются c башем знаком 2 дня )

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/bin/sh
LIB_DIR=lib/
SONAME_LIST=soname.lst
 
if [ "$#" -lt 1 ]; then
    echo "Usage: ./$(basename ${0}) < executable >"
    exit 1;
fi
 
# slc.c: На входе - вывод ldd, на выходе - имена symlink'ов, для динамич. загрузчика
cat<<'EOF' > slc.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <locale.h>
    #include <string.h>
 
    #define MAX_LENGTH 256
    #define LINE_CNT 200
    int first_nonspace(const char *s) {
    for(size_t i=0; *s; ++i, ++s)
        if( !isspace(*s) )
        return i;
    return -1; // Нет значимых символов
    }
    int main() {
    setlocale(LC_ALL, "C");
    char** lines = malloc( LINE_CNT * sizeof(char*) );
    for(int i=0; i < LINE_CNT; ++i)
        lines[i] = malloc( MAX_LENGTH * sizeof(char) );
    char  line[MAX_LENGTH];
    size_t currLineNumb = 0;
 
    while( fgets(line, MAX_LENGTH, stdin) != NULL && currLineNumb < LINE_CNT) {
        int fns = first_nonspace(line);
        if(fns == -1)
        continue;
        else {
        char lib_prefix[4] = {'\0',}; // "lib" + '\0'
        strncpy(lib_prefix, line+fns, 3);
        lib_prefix[3] = '\0';
        if( !strcmp(lib_prefix, "lib")) // Если имя начинается с 'lib',
            strcpy(lines[currLineNumb++], line+fns); // сохраняем эту строчку
        }
    }
    // Выводим сохраненную строчку до первого пробела,
    // что и будет составлять наш soname.
    for(size_t i=0; i < currLineNumb; ++i)
        printf("%.*s\n", (int)(strchr(lines[i], ' ') - lines[i]), lines[i]);
    for(int i=0; i < LINE_CNT; ++i) free(lines[i]);
    free(lines);
    return 0;
    }
EOF
cc -std=c99 slc.c -o slc
mkdir -p ${LIB_DIR}
ldd $1 | ./slc > $SONAME_LIST
for name in `cat $SONAME_LIST`; do
    libname=$(readlink /usr/lib/$name)   # Получим имя библиотеки
    cp /usr/lib/${libname} -t $LIB_DIR;    # Копируем библиотеку
    cp -P /usr/lib/${name} -t $LIB_DIR;    # Копируем симлинк, нужный для ldd
done
 
# Чистимся
rm -f ${SONAME_LIST} slc{,.c}
exit 0;
Сохраняем это под именем, например, s.sh, и ложим в каталог с исполняемым файлом приложения.
Выполняем:
Bash
1
chmod +x s.sh && ./s.sh name_of_the_executable
В текущем каталоге появится каталог ./lib/ , в котором будут лежать все необходимые ссылки и библиотеки.
Последний штрих: в .pro файл нужно добавить -rpath опцию линковщику, чтобы загрузчик потом знал, что поставляемые нами вместе с приложением библиотеки лежат в папке lib/ (в данном случае):
Код
LIBS += -Wl,-rpath lib/
Теперь все это дело в .tar.gz и можете делиться с друзьями своим приложением

Как из этого сделать программный пакет, смотрите документацию к своему дистрибутиву GNU/Linux.

Ps. На MacOS процедура практически не должна отличаться, но у меня нет возможности проверить.
Pss. Если ваше приложени базируется на плагинах, то они по своей природе не могут линковаться статически..
Смотрите документацию и все вытекающие перекрестные ссылки по плагинам.
___________________________________________________________________________________________________________
Вроди бы ничего не забыл...
Надеюсь, статейка кому-нибудь поможет разобраться.
Спасибо и удачи
Ответ:
Сообщение от Dmitriy_M
RHEL 5 еще актуален
Ради полноты надо вспомнить о LSB, блаодаря которой уже не один год любители совместимых бинарников (в основном проприетарщики и авторы вирусов) компилируют их в RHEL4.
Сообщение от gromo
Что касается libc.a и libstdc++.a, они, безусловно, присутствуют, но их, насколько я знаю, небезопасно линковать статически
Здесь проблема, как заметил уважаемый Dmitriy_M, больше в ABI, но не в статической компановке.

Добавлено через 5 минут
Сообщение от gromo
Не совсем понимаю, проприетарная или LGPL ? Насколько я знаю, это разные вещи, ведь Qt распространяется в 3 видах лицензии: GPL, LGPL, Commercial.
1. GPL - ваша программа тоже должна быть GPL незавимо от способа линковки.
2. LGPL - ваша программа может быть проприетарной при динамической линковке либо при соблюдении вышеописанного условия.
3.Commercial - покупаете и линкуете как хотите.
Вопрос: Статическая линковка библиотеки GLEW (qmake, mingw)

Для использования GLEW в Qt Creator с компилятором MinGW скачал исходники GLEW () и собрал через консоль, скоприровав это:
Кликните здесь для просмотра всего текста
gcc -DGLEW_NO_GLU -O2 -Wall -W -Iinclude -DGLEW_BUILD -o src/glew.o -c src/glew.c
gcc -shared -Wl,-soname,libglew32.dll -Wl,--out-implib,lib/libglew32.dll.a -o lib/glew32.dll src/glew.o -L/mingw/lib -lglu32 -lopengl32 -lgdi32 -luser32 -lkernel32
ar cr lib/libglew32.a src/glew.o

В pro файле проекта прописываю необходимый путь до заголовочников (INCLUDEPATH) и до библиотек (QMAKE_LIBDIR и LIBS += -lglew32 соответственно). И конечно же путь до dll в переменную PATH настроек среды запуска приложения. Таким образом, с динамической линковкой живётся без проблем и без ошибок.
Проблемы начинаются со статической сборкой. По идее, это должно решаться прописыванием "#define GLEW_STATIC" перед включением заголовочника glew.h. Ещё был вариант добавить флаг компиляции в pro файл: "QMAKE_CXXFLAGS += -DGLEW_STATIC". Тем не менее, ни после первого, ни после второго вариантов (ни после их комбинации) приложение не перестаёт требовать dll. Что не так, и что, может быть, нужно сделать дополнительно? Заранее спасибо за ответ.
Ответ: Без ошибок. Причём мелькало, что libglew.a он собирает с флагом -DGLEW_STATIC.
Вопрос: Нюанс статической линковки

Не ради какой-то практической пользы (коммерческой лицензии нет), а ради интереса - попытался собрать свой проект, использующий Qt, в статике.
Произошла ошибка ликновки, "мол такой-то символ неопределен". И тут дело даже не в использовании Qt, т.к. линкер не находил символы из libssl. Хотя странно, в строке сборки подключение этой библиотеки присутствует. Вобщем, как часто бывает, методом "научного тыка" изменил порядок в строке флагов сборки. А именно, в Makefile.Release перенес часть ключей "-lssl -lcrypto -lcrypt32 -lz -lws2_32" из начала в конец строки флагов сборки ... И все собралось :-?

Я понимаю, это выход, но это костыль - хочется делать все это всеж из IDE (QtCreator), ибо повторная генерация make-файлов, в случае полной пересборки с очисткой - все опять приводит к ошибке сборки.

Вообщем, нужны мысли, как решить проблему.

И еще вот прям щя вспомнилось ... когда собирал тулчейн x86_64-w64-mingw32.static, да и равно как и все остальные, мною во всю использовался и настраивался инструмент pkgconfig, а в проектах в QtCreator я его не использую. Может это быть причиной?
Сообщение отредактировано: JoeUser -
Ответ:
Цитата HardRock @
Порядок библиотек при статической линковки имеет значение потому что линкер "заглядывает" в библиотеку один раз, вытаскивая из неё только функции, которые уже знает (нашел ранее) и которые при этом помечены как undefined.

А можно где-то про это почитать? А то очень уж не очевидно это звучит.
Вопрос: Статическая линковка Qt 5.6

Решил я обновить Qt до 5.6. После чего надо было собрать комплект статической сборки. Решил собирать по этой инструкции так как еще с 5.0.2 процес не менялся (не считая ключей конфигуратора). Но вот незадача ни папки Src, ни configure.bat в папке (и подпапках) Qt не было обнаружено. Как их добавить?
Ответ:
Сообщение от ASKMAN
Что вы под этим подразумеваете?
Когда устанавливали QT надо было ставить галоччку напротив Sources(или, что то в этом роде) и тогда у вас были бы исходники.
Вопрос: GNU/Linux Qt5.* - Линкуем статически

В связи с отсутствием в данной теме мануала по статической линковке приложений Qt, решил исправить данный недостаток
Оговорюсь сразу: приветствуется беспощадная критика, особенно, что касается скриптов)
Итак:
1) Как обычно, нужно пересобрать фреймвор статически. Для этого скачиваем архив с исходными текстами отсюда: http://qt-project.org/downloads
На данный момент имя тар-бола - qt-everywhere-opensource-src-5.2.0.tar.gz

2) Переходим в каталог с тар-болом. Разархивируем и распаковываем. Потом нужно сконфигурировать, собрать и установить тонну исходников. Делаем это так ( за дополнительными параметрами, если нужно, обращайтесь ./configure --help):
Код Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tar -xzvf qt-everywhere-opensource-src-5.2.0.tar.gz && cd qt-everywhere-opensource-src-5.2.0 \
&& sudo mkdir -p /usr/lib/Qt5_static && ./configure -platform linux-g++ \
-release \
-static \
-fontconfig \
-opensource \
-confirm-license \
-nomake examples \
-c++11 \
-nomake tests \
-qt-zlib \
-qt-libpng \
-qt-libjpeg \
-prefix /usr/local/Qt5_static \
&& make -j3 && make install
Код Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Объяснение опций:
-platform linux-g++ \ # Ваша платформа и компилятор
-release \ # Собираем для релиза
-static \ # Собственно, наша цель собрать статически
-fontconfig \ # Поддержка собственного fontconfig
-opensource \ # СПО версия
-confirm-license \ # Сразу согласиться с лицензией
-nomake examples \ # Не собирать учебные примеры
-c++11 \ # Поддержка последнего стандарта
-nomake tests \ # Не проводить тесты
-qt-zlib \ # Внедрение собственной библиотеки <libname>
-qt-libpng \ # ...
-qt-libjpeg \ # ...
-prefix /usr/local/Qt5_static \ # Относительно этого пути будет производиться установка (можете свой указать)
make -j3 # количество ядер процессора +1
3) Так, теперь у нас есть все необходимые инструменты (почти )
Заходим в QtCreator, Tools > Options > Build & Run > Qt Versions
Добавляем путь к новому qmake ( в нашем случае /usr/local/Qt5_static/bin/qmake ).
Затем идем на вкладку Kits и добавляем новый набор инструментов, только укзываем ему qmake, только что добавленный на предыдущем шаге. Озаглавим этот набор Qt5_staticKit, например. Нажимаем Ok.
4) Ctrl+5, выбираем вверху проект, который мы хотим собрать статически, и щелкаем по новому Kit'y Qt5_staticKit. Выбираем Release конфигурацию. Тааак, здесь пока все.
5) Перелазим в .pro file. Добавляем туда
Код Code
1
2
3
QMAKE_LFLAGS += -s # Убрать все таблицы символов из результирующего бинарника ( man gcc )
# 3-rd party библиотеки, (boost, gmp, ... ) если есть статический вариант добавляем так:
LIBS += -Wl, -Bstatic, -lboost_regex, -lgmp, -lgmpxx, -Bdynamic 
Теперь все Qt и 3-rd party библиотеки сидят в бинарнике, НО дело еще не закончено.
Если посмотреть на вывод ldd, то можно увидеть, что приложение зависит еще от целой кучи не Qt-ных библиотек. Они могут быть бинарно-несовместимыми со своими предыдущими версиями (особенно, если написаны на С++) даже если они имеются на чужом компьютере.
Например:
Кликните здесь для просмотра всего текста
Код Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
ldd traditional
        linux-vdso.so.1 (0x00007fffd05fe000)
        libgobject-2.0.so.0 => lib/libgobject-2.0.so.0 (0x00007fe1a298a000)
        libX11-xcb.so.1 => lib/libX11-xcb.so.1 (0x00007fe1a2788000)
        libXi.so.6 => lib/libXi.so.6 (0x00007fe1a2578000)
        libxcb-render-util.so.0 => lib/libxcb-render-util.so.0 (0x00007fe1a2375000)
        libSM.so.6 => lib/libSM.so.6 (0x00007fe1a216d000)
        libICE.so.6 => lib/libICE.so.6 (0x00007fe1a1f51000)
        libxcb-glx.so.0 => lib/libxcb-glx.so.0 (0x00007fe1a1d39000)
        libxcb-render.so.0 => lib/libxcb-render.so.0 (0x00007fe1a1b2f000)
        libxcb-keysyms.so.1 => lib/libxcb-keysyms.so.1 (0x00007fe1a192d000)
        libdbus-1.so.3 => lib/libdbus-1.so.3 (0x00007fe1a16e5000)
        libxcb.so.1 => lib/libxcb.so.1 (0x00007fe1a14c5000)
        libxcb-image.so.0 => lib/libxcb-image.so.0 (0x00007fe1a12c1000)
        libxcb-icccm.so.4 => lib/libxcb-icccm.so.4 (0x00007fe1a10bd000)
        libxcb-sync.so.1 => lib/libxcb-sync.so.1 (0x00007fe1a0eb7000)
        libxcb-xfixes.so.0 => lib/libxcb-xfixes.so.0 (0x00007fe1a0cb0000)
        libxcb-shm.so.0 => lib/libxcb-shm.so.0 (0x00007fe1a0aad000)
        libxcb-randr.so.0 => lib/libxcb-randr.so.0 (0x00007fe1a089f000)
        libxcb-shape.so.0 => lib/libxcb-shape.so.0 (0x00007fe1a069b000)
        libxcb-xkb.so.1 => lib/libxcb-xkb.so.1 (0x00007fe1a0480000)
        libxkbcommon.so.0 => lib/libxkbcommon.so.0 (0x00007fe1a0220000)
        libfontconfig.so.1 => lib/libfontconfig.so.1 (0x00007fe19ffe4000)
        libfreetype.so.6 => lib/libfreetype.so.6 (0x00007fe19fd3f000)
        libXrender.so.1 => lib/libXrender.so.1 (0x00007fe19fb35000)
        libXext.so.6 => lib/libXext.so.6 (0x00007fe19f923000)
        libX11.so.6 => lib/libX11.so.6 (0x00007fe19f5e8000)
        libudev.so.1 => lib/libudev.so.1 (0x00007fe19f3d6000)
        libicui18n.so.52 => lib/libicui18n.so.52 (0x00007fe19efce000)
        libicuuc.so.52 => lib/libicuuc.so.52 (0x00007fe19ec55000)
        libpcre16.so.0 => lib/libpcre16.so.0 (0x00007fe19e9f6000)
        libdl.so.2 => lib/libdl.so.2 (0x00007fe19e7f2000)
        libgthread-2.0.so.0 => lib/libgthread-2.0.so.0 (0x00007fe19e5f0000)
        libglib-2.0.so.0 => lib/libglib-2.0.so.0 (0x00007fe19e2f0000)
        librt.so.1 => lib/librt.so.1 (0x00007fe19e0e8000)
        libGL.so.1 => lib/libGL.so.1 (0x00007fe19ddb5000)
        libpthread.so.0 => lib/libpthread.so.0 (0x00007fe19db98000)
        libstdc++.so.6 => lib/libstdc++.so.6 (0x00007fe19d894000)
        libm.so.6 => lib/libm.so.6 (0x00007fe19d591000)
        libgcc_s.so.1 => lib/libgcc_s.so.1 (0x00007fe19d37b000)
        libc.so.6 => lib/libc.so.6 (0x00007fe19cfd1000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fe1a2bdb000)
        libpcre.so.1 => lib/libpcre.so.1 (0x00007fe19cd6a000)
        libffi.so.6 => lib/libffi.so.6 (0x00007fe19cb62000)
        libuuid.so.1 => lib/libuuid.so.1 (0x00007fe19c95d000)
        libXau.so.6 => lib/libXau.so.6 (0x00007fe19c759000)
        libXdmcp.so.6 => lib/libXdmcp.so.6 (0x00007fe19c553000)
        libxcb-util.so.1 => lib/libxcb-util.so.1 (0x00007fe19c34e000)
        libexpat.so.1 => lib/libexpat.so.1 (0x00007fe19c124000)
        libz.so.1 => lib/libz.so.1 (0x00007fe19bf0e000)
        libbz2.so.1.0 => lib/libbz2.so.1.0 (0x00007fe19bcfe000)
        libpng16.so.16 => lib/libpng16.so.16 (0x00007fe19bac9000)
        libicudata.so.52 => lib/libicudata.so.52 (0x00007fe19a25e000)
        libnvidia-tls.so.331.38 => lib/libnvidia-tls.so.331.38 (0x00007fe19a05b000)
        libnvidia-glcore.so.331.38 => lib/libnvidia-glcore.so.331.38 (0x00007fe19784d000)

Пересобирать каждую из них, в случае, когда нет статической, довольно проблематично и долго и не всегда возможно ( кто хочет пусть попробует ), поэтому есть вариант поставлять их вместе со своим приложением. Большая вероятность того, что большинство этих библиотек будут на чужой машине, но выполнив еще немного действий, вы застрахуете и себя, и пользователя от лишней головной боли.
Вот что делаем:
1) Нужно скопировать симлинк, который выводит ldd, и библиотеку на которую он указывает в каталог с нашим приложением. ( в нашем случае /path/to/app/lib ). Симлинк использует динамический загрузчик при загрузке библиотеки для приложения во время выполнения, ну а библиотека, на которую он указывает это собственно данные.

Вот наваял такой скрипт (еще раз напомню, что правки и улучшения очень приветствуются c башем знаком 2 дня )

Код Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/bin/sh
LIB_DIR=lib/
SONAME_LIST=soname.lst
 
if [ "$#" -lt 1 ]; then
    echo "Usage: ./$(basename ${0}) < executable >"
    exit 1;
fi
 
# slc.c: На входе - вывод ldd, на выходе - имена symlink'ов, для динамич. загрузчика
cat<<'EOF' > slc.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <locale.h>
    #include <string.h>
 
    #define MAX_LENGTH 256
    #define LINE_CNT 200
    int first_nonspace(const char *s) {
    for(size_t i=0; *s; ++i, ++s)
        if( !isspace(*s) )
        return i;
    return -1; // Нет значимых символов
    }
    int main() {
    setlocale(LC_ALL, "C");
    char** lines = malloc( LINE_CNT * sizeof(char*) );
    for(int i=0; i < LINE_CNT; ++i)
        lines[i] = malloc( MAX_LENGTH * sizeof(char) );
    char  line[MAX_LENGTH];
    size_t currLineNumb = 0;
 
    while( fgets(line, MAX_LENGTH, stdin) != NULL && currLineNumb < LINE_CNT) {
        int fns = first_nonspace(line);
        if(fns == -1)
        continue;
        else {
        char lib_prefix[4] = {'\0',}; // "lib" + '\0'
        strncpy(lib_prefix, line+fns, 3);
        lib_prefix[3] = '\0';
        if( !strcmp(lib_prefix, "lib")) // Если имя начинается с 'lib',
            strcpy(lines[currLineNumb++], line+fns); // сохраняем эту строчку
        }
    }
    // Выводим сохраненную строчку до первого пробела,
    // что и будет составлять наш soname.
    for(size_t i=0; i < currLineNumb; ++i)
        printf("%.*s\n", (int)(strchr(lines[i], ' ') - lines[i]), lines[i]);
    for(int i=0; i < LINE_CNT; ++i) free(lines[i]);
    free(lines);
    return 0;
    }
EOF
cc -std=c99 slc.c -o slc
mkdir -p ${LIB_DIR}
ldd $1 | ./slc > $SONAME_LIST
for name in `cat $SONAME_LIST`; do
    libname=$(readlink /usr/lib/$name)   # Получим имя библиотеки
    cp /usr/lib/${libname} -t $LIB_DIR;    # Копируем библиотеку
    cp -P /usr/lib/${name} -t $LIB_DIR;    # Копируем симлинк, нужный для ldd
done
 
# Чистимся
rm -f ${SONAME_LIST} slc{,.c}
exit 0;
Сохраняем это под именем, например, s.sh, и ложим в каталог с исполняемым файлом приложения.
Выполняем:
Код Bash
1
chmod +x s.sh && ./s.sh name_of_the_executable
В текущем каталоге появится каталог ./lib/ , в котором будут лежать все необходимые ссылки и библиотеки.
Последний штрих: в .pro файл нужно добавить -rpath опцию линковщику, чтобы загрузчик потом знал, что поставляемые нами вместе с приложением библиотеки лежат в папке lib/ (в данном случае):
Код Code
1
LIBS += -Wl,-rpath lib/
Теперь все это дело в .tar.gz и можете делиться с друзьями своим приложением

Как из этого сделать программный пакет, смотрите документацию к своему дистрибутиву GNU/Linux.

Ps. На MacOS процедура практически не должна отличаться, но у меня нет возможности проверить.
Pss. Если ваше приложени базируется на плагинах, то они по своей природе не могут линковаться статически..
Смотрите документацию http://qt-project.org/doc/qt-4.8/deployment-x11.html и все вытекающие перекрестные ссылки по плагинам.
___________________________________________________________________________________________________________
Вроди бы ничего не забыл...
Надеюсь, статейка кому-нибудь поможет разобраться.
Спасибо и удачи
Ответ: Никак не получается собрать библиотеки. Выскакивает ошибка:
Код Code
1
2
3
4
Running configuration tests...
The test for linking against libxcb and support libraries failed!
 You might need to install dependency packages, or pass -qt-xcb.
 See src/plugins/platforms/xcb/README.
Пробовал добавлять ключ -qt-xcb, выдало:
Код Code
1
2
3
4
NOTE: libxkbcommon and libxkbcommon-x11 0.4.1 or higher not found on the system, will use 
the bundled version from 3rd party directory.
Could not find qmake configuration file linux-g++.
Error processing project file: /home/oleg/Загрузки/qt-everywhere-opensource-src-5.3.2/qt.pro
А с -no-xcb:
Код Code
1
2
Could not find qmake configuration file linux-g++.
Error processing project file: /home/oleg/Загрузки/qt-everywhere-opensource-src-5.3.2/qt.pro
В чем может быть проблема? Версия Qt 5.3.2

Добавлено через 1 час 22 минуты
kUbuntu 14.10

Добавлено через 3 часа 44 минуты
Все установилось, надо было выполнять команды из под рута

Добавлено через 27 минут
В общем все прописал, скомпилировал, а прога все равно не запускается на компе без Qt. Требует шрифты, которрые лежат в папке с Qt библиотеками. Как это исправить?