Мое приложение зависит от библиотеки, которая существует только в виде исходников. Она имеет лицензию LGPL, поэтому я не могу статически прилинковать ее к приложению. Возможно ли сделать так, чтобы она лежала в папке с приложением и бралась оттуда? Как это сделать? Приложение и библиотека на Qt и собираются в Linux и Windows. Я спрашиваю о том, как сказать приложению, что библиотеку надо искать не в /usr/bin, а в папке с бинарником?
Ответ
@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
int
main (int ac, char *av[])
{
int i;
for (i = 0; i < ac; i++)
printf ("av[%d] = [%s]
",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
",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
int
main (int ac, char *av[])
{
fprintf (stderr,"%s
",
exewithlibso ("./xaxa","lib",av)?
"Can't find path to executable": "execvp error
");
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
/*
Разбивает путь к файлу (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]
",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]
",path);
int l = strlen(path);
path[l] = '/';
strcpy(path+l+1,libdir);
addtopath("LD_LIBRARY_PATH",path);
// printf ("ld_path: [%s]
",path);
path[l] = '/';
strcpy(path+l+1,prog);
execvp(path,av);
return 0;
}
return -1;
}
Вроде работает.
Собственно, подобная идея (правда в плане чтения конфигурационных файлов) о "сомонастраивающихся" программах была у меня давно, а вот заинтересовался Вашим вопросом и реализовал для .so
Если понравилось, пользуйтесь.
Комментариев нет:
Отправить комментарий