10 червня 2016 р.

Розповсюдження готових програм створених за допомогою Qt у Ubuntu

Нова нотатка для себе.
Після компіляції програми у середовищі Qt  5.6 з використанням операційної системи Ubuntu 16.04 x64, стала задача перемістити програму на інший комп'ютер споживача, а не розробника.
Головна проблема що створити статичний файл програми не вийде у безкоштовній версії Qt, або може так я зрозумів. Для цього потрібно мати статично скомпільовану версію Qt. Тема про створення програм описана у документації Qt for Linux/X11 - Deployment.
Для роботи виконавчого файлу програми знадобляться динамічно завантажувальні бібліотеки. І головна задача зібрати усі бібліотеки що потрібні для роботи програми, на допомогу цьому прийде програма ldd (List Dynamic Dependencies). Вона покаже залежність, наприклад:
ldd ./Player 
 libesets_pac.so => /usr/lib/libesets_pac.so (0x00007f663b5ff000)
 libQtAV.so.1 => /home/lex/project-C/build/.../lib_linux_x86_64/libQtAV.so.1 (0x00007f663b221000)
 libQtAVWidgets.so.1 => /home/lex/project-C/build/.../libQtAVWidgets.so.1 (0x00007f663aff9000)
 libQt5Widgets.so.5 => /home/lex/Qt5.6.0/5.6/gcc_64/lib/libQt5Widgets.so.5 (0x00007f663a787000)
 libQt5Gui.so.5 => /home/lex/Qt5.6.0/5.6/gcc_64/lib/libQt5Gui.so.5 (0x00007f6639f8f000)
 libQt5Sql.so.5 => /home/lex/Qt5.6.0/5.6/gcc_64/lib/libQt5Sql.so.5 (0x00007f6639d4b000)
 libQt5Core.so.5 => /home/lex/Qt5.6.0/5.6/gcc_64/lib/libQt5Core.so.5 (0x00007f6639639000)
 libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f66392b6000)
...  

Тут ми можемо бачити різні типи залежностей від бібліотек:
  • системних бібліотек -  /usr/lib/libesets_pac.so, libstdc++.so.6
  • власних бібліотек програми - .../libQtAV.so.1
  • Qt бібліотек - .../Qt5.6.0/5.6/gcc_64/lib/libQt5Gui.so.5
  • додаткових модулів що використовуються у проекті
В ідеалі ми можемо зібрати до однієї теки, і перед запуском програми визначити змінну оточення  LD_LIBRARY_PATH, після чого програма буде шукати бібліотеки згідно послідовності визначених шляхів. Наприклад,
export LD_LIBRARY_PATH=`pwd`/libs:${LD_LIBRARY_PATH}, визначить що шукати буде програма у поточній підтеці libs,  та потім попередньо визначених змінною LD_LIBRARY_PATH.

Для автоматизації цього процесу, існує проект qtscripts автора William Hallatt (http://goblincoding.com/).
За використанням скрипту deployqtapp.sh, збираються усі файли зовнішніх бібліотек до однієї теки libs, створюються скрипт запуску де формується змінна LD_LIBRARY_PATH, і запускається програма, усі дані стискаються до архівного файлу.

Так після переносу усіх бібліотек програма не хотіла засукатися на іншому комп'ютері.
This application failed to start because it could not find or load the Qt platform plugin "xcb" in "".

Але до уваги треба зауважити що існує тека plugins, де завантажуються додаткові бібліотеки під час виконання програми.

З документації: Усі Qt програми котрі використовують графічний інтерфейс потребують плагіни що реалізують шар Qt Platform Abstraction (QPA) у Qt 5. Для Linux/X11, ім'я цього плагіну є libqxcb.so. Файл повинен бути у спеціальній підтеці  (за замовчуванням це platforms) у теці розробленого додатку. В якості альтернативи, можна налаштувати шлях пошуку для Qt, щоб знайти плагіни.

Для вирішення проблеми потрібно визначити де заходитися тека platform plugin з libqxcb.so, за звичай ця тека там де інстальована система Qt. Змінна оточення QT_QPA_PLATFORM_PLUGIN_PATH може допомогти Qt програмі  визначити це розміщення.

Визначити цю змінну можна у файлі що підготував скрипт з проекту qtscripts.
export QT_QPA_PLATFORM_PLUGIN_PATH=/home/lex/Qt5.6.0/5.6/gcc_64/plugins/platforms
Або створивши у теці де виконується програма файл налаштувань - qt.conf, наступного змісту з вказуванням шляху до усіх плагінів.
[Paths]
Plugins=/home/lex/Qt5.6.0/5.6/gcc_64/plugins
Але це за умови що, на комп'ютері інстальований пакунок програм Qt.
Якщо ні, то потрібно визначити які використовуються додаткові плагіни.

Так я визначив що потрібна бібліотека libqxcb.so для платформи xcb.
Скопіював файл libqxcb.so до теки platforms.

Але цього не достатньо, програма не працює.
Available platform plugins are: xcb.
Reinstalling the application may fix this problem.
Aborted (core dumped)
Визначаю залежність libqxcb.so:
LD_LIBRARY_PATH=../libs ldd libqxcb.so |grep -i "not found"
./libqxcb.so: /usr/lib/x86_64-linux-gnu/libQt5XcbQpa.so.5: \
   version `Qt_5_PRIVATE_API' not found (required by ./libqxcb.so)
Бачу що знайдено системний файл від не поточної версії Qt. Копіюю libQt5XcbQpa.so.5 до збірнох теки libs з lib теки пакунку Qt.

Повторюю, пошук залежностей, знаходжу необхідність у libQt5DBus.so.5, копіюю також з lib теки пакунку Qt. Повторюю, і не знаходжу проблем.

P.S. У теці platforms існує скрипт fixdep.sh котрий може задовільними залежності усіх зовнішніх бібліотек у теці platforms, зі репозиторії Ubuntu. Рекомендую визначити змінну LD_LIBRARY_PATH, з посиланням на теку де ми зібрали усі бібліотеки. Додатково створено буде скрипт removedep.sh, котрий також може видалити ці встановленні залежності. 

Наступна програма, запустилася, і при виконані програми де є робота з OpenGL отримую наступне повідомлення:
QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled
Визначаю що програмі потрібен plugin QXcbIntegration.

З документації: Ваш додаток може також залежати від одного або декількох модулів Qt, таких як модуль формату зображення JPEG або модуль драйвера SQL. Обов'язково потрібно поширювати будь-які плагіни Qt, які вам потрібні з вашим додатком. Подібно плагіну для платформи, кожен тип модуля повинен знаходитися в межах певної підтеки (наприклад, imageformats або sqldrivers) в вашій теці. 


Створюю теку plugins, і копіюю зміст теки QXcbIntegration з теки пакунку Qt. Перевіряю переписані бібліотеки на залежність:
LD_LIBRARY_PATH=../../libs ldd *.so |grep -i "not found"
Для визначення програмі де шукати теку з plugins, можна використати змінну оточення QT_PLUGIN_PATH, або файл налаштувань - qt.conf,
Визначаю змінну оточення:
export QT_PLUGIN_PATH=`pwd`/plugins
Проблем не має,програма запускається.

Оптимізувати структуру тек можна перемістив теку platforms до теки plugins, і тоді не потрібно визначати змінну QT_QPA_PLATFORM_PLUGIN_PATH, а досить визначити змінну QT_PLUGIN_PATH, або досить створити файл qt.conf:
[Paths]
Plugins=plugins
Або можна додати до коду програми:
#ifdef unix 
QCoreApplication::addLibraryPath("plugins");
#endif 

Немає коментарів:


Коли забув ти рідну мову, біднієш духом ти щодня...
When you forgot your native language you would become a poor at spirit every day ...

Д.Білоус / D.Bilous
Рабів до раю не пускають. Будь вільним!

ipv6 ready