Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

Дистрибуция приложения, зависящего от библиотеки, не присутствующей в репозиториях

#windows #cpp #qt #библиотеки #linux


Мое приложение зависит от библиотеки, которая существует только в виде исходников.
Она имеет лицензию LGPL, поэтому я не могу статически прилинковать ее к приложению.
Возможно ли сделать так, чтобы она лежала в папке с приложением и бралась оттуда? Как
это сделать? Приложение и библиотека на Qt и собираются в Linux и Windows.
Я спрашиваю о том, как сказать приложению, что библиотеку надо искать не в /usr/bin,
а в папке с бинарником?    


Ответы

Ответ 1



@Robotex, на Ваш вопрос Т.е. я не имею права скомпилить библиотеку в .so и положить внутрь пакета с моим приложением? я бы ответил отрицательно. Действительно, самыми простыми способами будет: установить .so (например в /usr/local/bin), прописать ее в /etc/ld.so.conf.d и запустить ldconfig (или перезагрузить комп) прописать в LD_LIBRARY_PATH путь к библиотеке (в составе приложения) где-нибудь в ~/.bashrc запускать приложение через shell скрипт, в котором прописать в LD_LIBRARY_PATH путь к библиотеке Но, все эти способы требуют некоторых действий по установке приложения, а иногда хочется что-то вроде tar xvf my-progs.tar export PATH=$PATH:/tram-pam-pam/my-progs и my-bestprog arg1 arg2 Такую штуку можно сделать, написав некую "обертку" к программе, использующей .so, если известно взаимное (относительное) расположение "обертки", программы и оглавления с .so "Обертка" будет вычислять абсолютный путь, откуда ее запустили (в смысле где она лежит), добавлять к environment LD_LIBRARY_PATH путь к библиотеке и запускать основную программу. Например это можно реализовать так: // txaxa.c программа вызывающая функцию xaxa() из shared library #include #include int main (int ac, char *av[]) { int i; for (i = 0; i < ac; i++) printf ("av[%d] = [%s]\n",i,av[i]); xaxa("xa-xa"); exit(0); } А это функция в shared library // xaxa.c эта функция помещается в shared library libxaxa.so #include void xaxa (char *txt) { printf ("shared lib xaxa(): %s\n",txt); } Соберем приложение xaxa avp@avp-ubu1:~/hashcode/tst$ gcc -fPIC -shared xaxa.c -o libxaxa.so avp@avp-ubu1:~/hashcode/tst$ gcc -o xaxa txaxa.c -L. -lxaxa Добавим "обертку" // rxaxa.c добавляет к LD_LIBRARY_PATH ./lib, где в ./lib/libxaxa.so лежит xaxa() // и вызывает "приложение" xaxa (txaxa.c) #include #include int main (int ac, char *av[]) { fprintf (stderr,"%s\n", exewithlibso ("./xaxa","lib",av)? "Can't find path to executable": "execvp error\n"); exit (1); } И соберем в каталоге .prog наше приложение avp@avp-ubu1:~/hashcode/tst$ gcc rxaxa.c guessfrom.c -o rxaxa avp@avp-ubu1:~/hashcode/tst$ mkdir prog avp@avp-ubu1:~/hashcode/tst$ mkdir prog/lib avp@avp-ubu1:~/hashcode/tst$ cp libxaxa.so prog/lib/ avp@avp-ubu1:~/hashcode/tst$ cp xaxa rxaxa prog/ avp@avp-ubu1:~/hashcode/tst$ tar cvf my-prog.tar ./prog/ ./prog/ ./prog/lib/ ./prog/lib/libxaxa.so ./prog/xaxa ./prog/rxaxa Теперь распакуем .tar куда-нибудь и можно вызывать (все работает) avp@avp-ubu1:~/hashcode/tst$ cd /tmp avp@avp-ubu1:/tmp$ tar xf /home/avp/hashcode/tst/my-prog.tar avp@avp-ubu1:/tmp$ ./prog/rxaxa call xaxa av[0] = [./prog/rxaxa] av[1] = [call] av[2] = [xaxa] shared lib xaxa(): xa-xa avp@avp-ubu1:/tmp$ PATH=$PATH:/tmp/prog/ avp@avp-ubu1:/tmp$ cd avp@avp-ubu1:~$ pwd /home/avp avp@avp-ubu1:~$ rxaxa call xaxa and it works av[0] = [rxaxa] av[1] = [call] av[2] = [xaxa] av[3] = [and] av[4] = [it] av[5] = [works] shared lib xaxa(): xa-xa avp@avp-ubu1:~$ А вот функции, которые делают эту штуку // avp 2012 #include #include #include #include #include #include #include #include /* Разбивает путь к файлу (pname) на имя файла и каталоги Каталаги возвращаются в path Имя файла в name */ static void splitpathname(char *pname, char *path, char *name) { char *p = pname+strlen(pname)-1; *path = *name = 0; while ((p >= pname) && (*p != '/')) p--; strcpy(name,p+1); if (p > pname) strncat(path,pname,p-pname); } /* Проверяет можно ли выполнить (exec) путь av0 Если да, возвращает 1 и кладет в pfrom absolute path к программе, а по адресу exe (если не NULL) последний компонент из av0 (имя программы) иначе возвращает 0 */ static int checkexe (char *av0, char *pfrom, char *exe) { struct stat st; if (stat(av0,&st) == 0 && (st.st_mode & S_IFREG) && access(av0,X_OK) == 0) { char name[PATH_MAX], path[PATH_MAX]; splitpathname(av0,path,name); if (path[0]) { char curwd[PATH_MAX]; getcwd(curwd,PATH_MAX); chdir(path); getcwd(pfrom,PATH_MAX); chdir(curwd); } else { strcpy(pfrom,"."); } if (exe) strcpy(exe,name); return 1; } return 0; } static int getpathpart (char **pb, char *p) { if (!*pb) return 0; char *t = *pb; int l = 0; while (*t && *t == ':') t++; if (*t) *pb = t; while (*t && *t != ':') p[l++] = *t++; p[l] = 0; return l; } /* Функция определяет абсолютный путь к запущенной программе по значению argv[0] с учетом envirinment PATH Returns 1 OK 0 установить путь не удалось В случае успеха возвращает в pfrom найденный путь (без имени программы), а в exe имя программы */ int guessfrom (char *av0, char *pfrom, char *exe) { if (!av0 || *av0 == 0) return 0; if (checkexe(av0, pfrom, exe)) return 1; int l; char *beg, pexe[PATH_MAX]; for (beg = getenv("PATH"); l = getpathpart(&beg,pexe); beg += l) { pexe[l] = '/'; strcpy(pexe+l+1,av0); // printf ("pexe: [%s]\n",pexe); if (checkexe(pexe,pfrom,exe)) return 1; } return 0; } /* Добавляет компонент в начало environment переменной типа LD_LIBRARY_PATH, PATH и т.п. */ void addtopath (char *pathname, char *elem) { char *p = getenv(pathname), *t; int l = strlen(elem); if (p && (t = strstr(p,elem))) { if (t[l] == 0 || t[l] == ':') return; } char newpath[l+1+ (p? strlen(p): 0)]; strcpy(newpath,elem); if (p) { newpath[l] = ':'; strcpy(newpath+l+1,p); } setenv (pathname,elem,1); } /* Запускает программу, добавляя компонент к LD_LIBRARY_PATH prog - относительный путь к программе libdir - относительный путь к оглавлению с .so, добавляемому к LD_LIBRARY_PATH av - аргументы (включая argv[0]) запускаемой программы Returns 0 - не удалось выполнить execvp -1 - не удалось определить путь к программе */ int exewithlibso (char *prog, char *libdir, char **av) { char path[PATH_MAX]; if (guessfrom(av[0], path, 0)) { // printf ("I am from: [%s]\n",path); int l = strlen(path); path[l] = '/'; strcpy(path+l+1,libdir); addtopath("LD_LIBRARY_PATH",path); // printf ("ld_path: [%s]\n",path); path[l] = '/'; strcpy(path+l+1,prog); execvp(path,av); return 0; } return -1; } Вроде работает. Собственно, подобная идея (правда в плане чтения конфигурационных файлов) о "сомонастраивающихся" программах была у меня давно, а вот заинтересовался Вашим вопросом и реализовал для .so Если понравилось, пользуйтесь.

Ответ 2



(IANAL) Если вы распространяете приложение, которое линкуется с библиотекой под LGPL, то вы должны предоставить способ пересобрать свое приложение с новой версией используемой библиотеки. В случае статической линковки с этой библиотекой, лично я не знаю такого способа, кроме распространения исходного кода. Наверно, можно распространять только соответствующие obj файлы, но вроде это не практикуется. Почему у вас отпал вариант собрать обсуждаемую LGPL библиотеку как .dll / .so и использовать динамическую линковку?

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

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