Библиотеки и сторонние исходники
TODO «Установка» не влезает — перенести
Libtool
libtool — инструмент для кроссплатформенной сборки и тестирования библиотек.
Пример:
lib.c
libg.c
lib.h
main.c
Makefile к ним
А теперь с Libtool:
Makefile:
1 CFLAGS = -g -O 2 LTFLAGS = --tag=CC 3 4 all: inc 5 6 %.lo: %.c 7 libtool --mode=compile $(LTFLAGS) $(CC) -c $< 8 9 libinc.la: lib.lo libg.lo 10 libtool --mode=link $(LTFLAGS) $(CC) -o $@ $^ -rpath /usr/lib64 11 12 inc: main.o libinc.la 13 libtool --mode=link $(LTFLAGS) $(CC) -o $@ $^ 14 15 check: inc 16 ./$< 123 17 18 clean: 19 rm -rf *.so inc .libs *.l? *.o
Особенности:
- Сборка двух вариантов библиотек
Использование RPATH
Использование shell-обёрток для подгрузки .so
Версионирование библиотек
-version-info current:revision:age
current — +1 при любом вмешательстве в ABI
revision — +1 при каждом выпуске, не меняющем ABI, 0 при вмешательстве в ABI (т. е. изменении current)
age — +1 при добавлении в ABI, 0 при изменении или удалении
- SONAME: version.minor.subminor
Особенность библиотек: обратно совместимы при добавлении ABI, не совместимы при удалении/изменении
⇒ current=version; age=minor, revision=subminor
Попробуем добавить -version-info 8:3:1
Это копипаста аналогичного материала прошлых лет; возможно, её надо отредактировать
Использование сторонних исходников:
- Old school. Скопировать и поддерживать самим
- + если апстрим умер/заснул, а ты не готов сам становиться апстримом, но твою задачу код решает
- + если требуется модификация, которую апстрим не примет
- - поддерживать самим
- New school: взаимодействовать с апстримом
- Linux way: если это библиотека — зачем исходники, она ж в пакете есть!
- + собирать один раз
- - выпиливать из апстрима
- (а пакет кто делает?)
Patch / diff
Рассмотрим ситуацию, когда сторонние исходники используются, но нужно их немного поправить:
- Потому что это правка, необходимая только вашему проекту
- Потому что вы — майнтейнер пакета в Linux, а исходники не соответствуют принятой в вашем сообществе дисциплине, или апстрим не считает багу ошибкой, а вы считаете ☺
- …
В любом случае при обновлении апстрима исправления придётся вносить заново
Цикл обновления / внесения изменений:
- Первоначальный импорт исходников
- Адаптация к вашим нуждам
Оформление серии патчей (см. ниже)
- Релиз
- Отныне и навеки
- Обновление апстримной версии
- Применение патчей к обновлённой версии
- Адаптация отвалившихся патчей
- Релиз
Утилита diff — построчное сравнение и демонстрация изменений двух текстов
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <ctype.h>
6
7 /* open()->fopen() wrapper */
8 static FILE *ffopen(const char *pathname, const char *mode, int flags)
9 {
10 int m;
11
12 switch(tolower(mode[0])+(mode[1]=='+')) {
13 case 'b': /* "a+" */
14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break;
15 case 'r': m = O_RDONLY; break;
16 case 's': m = O_RDWR; break; /* "r+" */
17 case 'x': /* "w+" */
18 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break;
19 default: m = O_RDONLY; break;
20 }
21
22 int fd = open(pathname, flags, m);
23 return fd<0? NULL: fdopen(fd, mode);
24 }
25
26 int main(int argc, char *argv[]) {
27 FILE *fp;
28
29 fp = ffopen(argv[1], "r", O_NOFOLLOW);
30 if(fp == NULL) {
31 perror(argv[1]);
32 return 1;
33 }
34
35 return 0;
36 }
|
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <ctype.h>
6
7 /* open()->fopen() wrapper */
8 static FILE *ffopen(const char *pathname, const char *mode, int flags)
9 {
10 int m;
11
12 /* Simulate fopen flags */
13 switch(tolower(mode[0])+(mode[1]=='+')) {
14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break;
15 case 'b': /* "a+" */
16 case 'r': m = O_RDONLY; break;
17 case 's': m = O_RDWR; break; /* "r+" */
18 case 'x': /* "w+" */
19 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break;
20 default: m = O_RDONLY; break;
21 }
22
23 int fd = open(pathname, flags, m);
24 return fd<0? NULL: fdopen(fd, mode);
25 }
26
27 int main(int argc, char *argv[]) {
28 FILE *fp;
29
30 fp = ffopen(argv[1], "r", O_NOFOLLOW);
31 if(fp == NULL) {
32 perror(argv[1]);
33 return 1;
34 }
35
36 return 0;
37 }
|
Просто diff: как из одного файла сделать другой
diff -e — команды для текстового редактора ed
diff -c — разница вместе с окружающим её контекстом
1 *** ffopen.c 2020-06-13 20:58:46.702703936 +0300 2 --- ffopen_new.c 2020-12-08 19:53:02.082341887 +0300 3 *************** 4 *** 9,17 **** 5 { 6 int m; 7 8 switch(tolower(mode[0])+(mode[1]=='+')) { 9 - case 'b': /* "a+" */ 10 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 11 case 'r': m = O_RDONLY; break; 12 case 's': m = O_RDWR; break; /* "r+" */ 13 case 'x': /* "w+" */ 14 --- 9,18 ---- 15 { 16 int m; 17 18 + /* Simulate fopen flags */ 19 switch(tolower(mode[0])+(mode[1]=='+')) { 20 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 21 + case 'b': /* "a+" */ 22 case 'r': m = O_RDONLY; break; 23 case 's': m = O_RDWR; break; /* "r+" */ 24 case 'x': /* "w+" */
diff -u — наиболее удобный формат
1 --- ffopen.c 2020-06-13 20:58:46.702703936 +0300 2 +++ ffopen_new.c 2020-12-08 19:53:02.082341887 +0300 3 @@ -9,9 +9,10 @@ 4 { 5 int m; 6 7 + /* Simulate fopen flags */ 8 switch(tolower(mode[0])+(mode[1]=='+')) { 9 - case 'b': /* "a+" */ 10 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 11 + case 'b': /* "a+" */ 12 case 'r': m = O_RDONLY; break; 13 case 's': m = O_RDWR; break; /* "r+" */ 14 case 'x': /* "w+" */
@@ -9,9 +9,10 @@ означает: «в строчке 9 исходного файла взять 9 строк и превратить их в 10 строк целевого файла»
Есть варианты -C размер контекста и -U размер контекста
- Вариант с несколькими изменениями (chunks):
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <ctype.h> 6 7 /* open()->fopen() wrapper */ 8 static FILE *ffopen(const char *pathname, const char *mode, int flags) 9 { 10 int m; 11 12 switch(tolower(mode[0])+(mode[1]=='+')) { 13 case 'b': /* "a+" */ 14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 15 case 'r': m = O_RDONLY; break; 16 case 's': m = O_RDWR; break; /* "r+" */ 17 case 'x': /* "w+" */ 18 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break; 19 default: m = O_RDONLY; break; 20 } 21 22 int fd = open(pathname, flags, m); 23 return fd<0? NULL: fdopen(fd, mode); 24 } 25 26 int main(int argc, char *argv[]) { 27 FILE *fp; 28 29 fp = ffopen(argv[1], "r", O_NOFOLLOW); 30 if(fp == NULL) { 31 perror(argv[1]); 32 return 1; 33 } 34 35 return 0; 36 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <ctype.h> 6 7 /* open()->fopen() wrapper */ 8 static FILE *ffopen(const char *pathname, const char *mode, int flags) 9 { 10 int m; 11 12 /* Simulate fopen flags */ 13 switch(tolower(mode[0])+(mode[1]=='+')) { 14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 15 case 'b': /* "a+" */ 16 case 'r': m = O_RDONLY; break; 17 case 's': m = O_RDWR; break; /* "r+" */ 18 case 'x': /* "w+" */ 19 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break; 20 default: m = O_RDONLY; break; 21 } 22 23 int fd = open(pathname, flags, m); 24 return fd<0? NULL: fdopen(fd, mode); 25 } 26 27 int main(int argc, char *argv[]) { 28 FILE *fp; 29 30 fp = ffopen(argv[1], "r", O_NOFOLLOW); 31 if(fp == NULL) { 32 perror(argv[1]); 33 return 1; 34 } 35 else 36 fclose(fp); 37 38 return 0; 39 }
- ⇒
1 --- ffopen.c 2020-12-08 20:29:07.452395896 +0300 2 +++ ffopen_new.c 2020-12-08 20:12:38.004914230 +0300 3 @@ -9,9 +9,10 @@ 4 { 5 int m; 6 7 + /* Simulate fopen flags */ 8 switch(tolower(mode[0])+(mode[1]=='+')) { 9 - case 'b': /* "a+" */ 10 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 11 + case 'b': /* "a+" */ 12 case 'r': m = O_RDONLY; break; 13 case 's': m = O_RDWR; break; /* "r+" */ 14 case 'x': /* "w+" */ 15 @@ -31,6 +32,8 @@ 16 perror(argv[1]); 17 return 1; 18 } 19 + else 20 + fclose(fp); 21 22 return 0; 23 }
Утилита patch умеет применять результат diff к исходному файлу, при этом получается целевой файл
Этот-то «результат diff» и называется патчем
- А несколько таких патчей в правильном порядке — патчсетом
Что гораздо важнее, patch умеет находить перемещение контекста и даже определять приблизительное совпадение контекста:
Возьмём слегка изменённый файл ffopen2.c (в нём изменена строка case 'x': и добавлен комментарий перед main()):
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <ctype.h> 6 7 /* open()->fopen() wrapper */ 8 static FILE *ffopen(const char *pathname, const char *mode, int flags) 9 { 10 int m; 11 12 switch(tolower(mode[0])+(mode[1]=='+')) { 13 case 'b': /* "a+" */ 14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 15 case 'r': m = O_RDONLY; break; 16 case 's': m = O_RDWR; break; /* "r+" */ 17 case 'x': /* alias for "w+" */ 18 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break; 19 default: m = O_RDONLY; break; 20 } 21 22 int fd = open(pathname, flags, m); 23 return fd<0? NULL: fdopen(fd, mode); 24 } 25 26 /* just ordinary main() */ 27 int main(int argc, char *argv[]) { 28 FILE *fp; 29 30 fp = ffopen(argv[1], "r", O_NOFOLLOW); 31 if(fp == NULL) { 32 perror(argv[1]); 33 return 1; 34 } 35 36 return 0; 37 }
и применим к нему патч, предназначенный для исходного файла:
Контекст первого блока неточен (в патче одна строка контекста слегка другая); тем не менее это очень похожий контекст (с разницей 1, что бы это ни значило), так что блок применён
- Контекст второго блока точен, но найден со смещением в одну строку, блок применён
Получившийся файл имеет свойства как общего предка (ffopen.c), так и обоих родителей (ffopen2.c и ffopen_changed.c)
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <ctype.h> 6 7 /* open()->fopen() wrapper */ 8 static FILE *ffopen(const char *pathname, const char *mode, int flags) 9 { 10 int m; 11 12 switch(tolower(mode[0])+(mode[1]=='+')) { 13 case 'b': /* "a+" */ 14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 15 case 'r': m = O_RDONLY; break; 16 case 's': m = O_RDWR; break; /* "r+" */ 17 case 'x': /* "w+" */ 18 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break; 19 default: m = O_RDONLY; break; 20 } 21 22 int fd = open(pathname, flags, m); 23 return fd<0? NULL: fdopen(fd, mode); 24 } 25 26 int main(int argc, char *argv[]) { 27 FILE *fp; 28 29 fp = ffopen(argv[1], "r", O_NOFOLLOW); 30 if(fp == NULL) { 31 perror(argv[1]); 32 return 1; 33 } 34 35 return 0; 36 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <ctype.h> 6 7 /* open()->fopen() wrapper */ 8 static FILE *ffopen(const char *pathname, const char *mode, int flags) 9 { 10 int m; 11 12 /* Simulate fopen flags */ 13 switch(tolower(mode[0])+(mode[1]=='+')) { 14 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 15 case 'b': /* "a+" */ 16 case 'r': m = O_RDONLY; break; 17 case 's': m = O_RDWR; break; /* "r+" */ 18 case 'x': /* alias for "w+" */ 19 case 'w': m = O_WRONLY | O_CREAT | O_TRUNC; break; 20 default: m = O_RDONLY; break; 21 } 22 23 int fd = open(pathname, flags, m); 24 return fd<0? NULL: fdopen(fd, mode); 25 } 26 27 /* just ordinary main() */ 28 int main(int argc, char *argv[]) { 29 FILE *fp; 30 31 fp = ffopen(argv[1], "r", O_NOFOLLOW); 32 if(fp == NULL) { 33 perror(argv[1]); 34 return 1; 35 } 36 else 37 fclose(fp); 38 39 return 0; 40 }
В случае, когда какие-то блоки применяются, а какие-то — нет, patch создаёт reject-файл — патч, содержащий только неприложившиеся блоки.
Например, модифицируем в ffopen.c строчку case 'b'::
- Поскольку изменения сделаны прямо в исправляемой строке, такой блок не приложится; второй блок приложится нормально:
1 $ patch --verbose ffopen3_changed.c < changeset.patch 2 Hmm... Looks like a unified diff to me... 3 The text leading up to this was: 4 -------------------------- 5 |--- ffopen.c 2020-12-08 20:29:07.452395896 +0300 6 |+++ ffopen_new.c 2020-12-08 20:12:38.004914230 +0300 7 -------------------------- 8 patching file ffopen3_changed.c 9 Using Plan A... 10 Hunk #1 FAILED at 9. 11 Hunk #2 succeeded at 31. 12 1 out of 2 hunks FAILED -- saving rejects to file ffopen3_changed.c.rej 13 done 14
- Этот reject файл предполагается открыть редактором и ручками применить
Использование git
Команда git diff порождает не что иное, как diff -u. Предположим, мы отредактировали файл ffopen.c в git-репозитории. Вот что покажет git diff:
1 diff --git a/ffopen.c b/ffopen.c 2 index 7ac8822..bf6ba89 100644 3 --- a/ffopen.c 4 +++ b/ffopen.c 5 @@ -9,9 +9,10 @@ static FILE *ffopen(const char *pathname, const char *mode, int flags) 6 { 7 int m; 8 9 + /* Simulate fopen flags */ 10 switch(tolower(mode[0])+(mode[1]=='+')) { 11 - case 'b': /* "a+" */ 12 case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break; 13 + case 'b': /* "a+" */ 14 case 'r': m = O_RDONLY; break; 15 case 's': m = O_RDWR; break; /* "r+" */ 16 case 'x': /* "w+" */ 17 @@ -31,6 +32,8 @@ int main(int argc, char *argv[]) { 18 perror(argv[1]); 19 return 1; 20 } 21 + else 22 + fclose(fp); 23 24 return 0; 25 }
Поскольку git сравнивает две версии одного и того же файла, для большей понятности исходный файл считается лежащим в воображаемом каталоге a/, а целевой — в воображаемом каталоге b/.
В заголовке патча присутствуют (урезанные) идентификаторы сравниваемых объектов (7ac8822 и bf6ba89)
Умный git догадался, что это файл на Си (?) и в описании контекста добавил сигнатуру функции, в которой он встретился (это комментарий)
Если сейчас сделать commit, тот же формат будет у git log -p (+загловок коммита)
- Из последовательности коммитов можно сделать патчсет (в примере патч один)
1 $ git format-patch HEAD^ 2 0001-FFopen-updated.patch 3 $ head -20 0001-FFopen-updated.patch 4 From e3522f55a58d7f3ea6d85d518ef55a9d36ce887e Mon Sep 17 00:00:00 2001 5 From: "George V. Kouryachy (Fr. Br. George)" <george@altlinux.ru> 6 Date: Tue, 8 Dec 2020 21:11:51 +0300 7 Subject: [PATCH] FFopen updated 8 9 --- 10 ffopen.c | 5 ++++- 11 1 file changed, 4 insertions(+), 1 deletion(-) 12 13 diff --git a/ffopen.c b/ffopen.c 14 index 7ac8822..bf6ba89 100644 15 --- a/ffopen.c 16 +++ b/ffopen.c 17 @@ -9,9 +9,10 @@ static FILE *ffopen(const char *pathname, const char *mode, int flags) 18 { 19 int m; 20 21 + /* Simulate fopen flags */ 22 switch(tolower(mode[0])+(mode[1]=='+')) { 23 - case 'b': /* "a+" */ 24
Это патч можно применить к старой версии файла!
С помощью patch -p1 < 0001-FFopen-updated.patch
Если не указывать, какой файл патчим, patch возьмёт её из заголовка
Поэтому надо с помощью -p удалить один уровень воображаемых каталогов a/ или b/
С помощью git apply 0001-FFopen-updated.patch
С помощью git am 0001-FFopen-updated.patch (при этом произведётся также и commit с данными из заголовка)
Важно: правила наложения патчей в git очень строгие: fuzz не допускается. Возможно, накладывать старый патч на обновлённые исходники лучше всё-таки patch-ем
Сочетание git и patch
Если никакого git-репозитория нет, всё равно удобно использовать git:
- Создаём git-репозиторий
- Накатываем патч
- Часть блоков прикладывается, част отваливается
- Коммитим то, что приложилось
- Глядя в reject-файлы применяем вручную то, что не приложилось, проверяем сборку, и тоже коммитим, возможно, несколько раз
Можно таким образом превратить исходный патч в патчсет, а можно слить всё обратно в один коммит с помощью git rebase -i состояние_до_накладывания_патча и последующего fixup
Установка
Есть много способов установить ПО под Linux:
- Не устанавливать. Собрать всё в каком-то подкаталоге и запускать оттуда.
Просто собрать!
- Много лишних файлов
Не очень понятно как читать man
Скорее всего, не будет работать, если мы не сделаем cd этот каталог
Надо разбираться с $LD_LIBRARY_PATH
Вариант: собирать один большой статический бинарник со всем, так делает Qt
- Требует программного решения (данные-«ресурсы»)
- Требует статической компоновки с большинством библиотек
можно запускать его откуда угодно
- Установить в специальный отдельный подкаталог системы все нужные файлы
Как правило, это /opt/название-приложения
Требует прав root
Надо разбираться с $LD_LIBRARY_PATH
Скорее всего, не будет работать, если мы не сделаем cd этот каталог; но тогда надо отличать два вида файлов
RO — те, что установлены в /opt/название-приложения
RW — те, которые образуются в процессе работы приложения (например, локальные конфиги в $HOME/.config/название-приложения).
Само приложение придётся запускать /opt/название-приложения/bin/название-приложения (или как-то так), либо для каждого добавлять /opt/название-очередного-приложения/bin/ в $PATH
Удаление такого приложения — просто удаление каталога /opt/название-приложения
- «Крибле! Крабле! Бумс!», оно же «configure - make - make install»
- Требует прав root
Приедет по умолчанию в /usr/local/*, особо никому не мешает, /usr/local/lib можно добавить один раз в системный ld.so.cache, а /usr/local/bin/ — в $PATH
Вариант: configure --prefx=/usr — можно попортить систему
- Проблема деинсталляции
Сформировать пакет / AppImage / Snap / …
- Установка, удаление
- Интеграция с системой или, наоборот, изоляция
- …
- Надо уметь собирать пакеты ☹
Если вы не майнтейнер пакета в определённом сообществе, а апстрим, придётся собирать много разных пакетов, что ли??
( за это вам уготованы мучения в аду ) Использовать самописный «инсталлятор»
Вывод:
Несвободный софт; или громадный проект, который никто, кроме вас, собирать не будет
или /opt/что-там,
- или Qt-like тушка,
или пакет под ваш любимый, хорошо известный вам дистрибутив (или систему дистрибуции, вроде AppImage, Flatpak и т. п.)
- Свободный софт:
- Процедура сборки под любое вменяемое окружение, пускай майнтейнеры сами разбираются
- Qt-like тушка
Поддержка установки в automake
В сформированном скрипте configure присутствует много настроек именно каталогов установки:
$ ./configure --help `configure' configures L10n gettext example 0.0 to adapt to many kinds of systems. … Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [/usr/local] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, `make install' will install all the files in `/usr/local/bin', `/usr/local/lib' etc. You can specify an installation prefix other than `/usr/local' using `--prefix', for instance `--prefix=$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] …
--prefix указывает, в каком каталоге будет создана uniх-подобная иерархия подкаталогов, и где запущенная программа должна искать свои файлы
По умолчанию это /usr/local, т. е. самое бессмысленное место в Linux ☺
- …зато безопасное
Установка
В gettext поддерживается цель install и монжество её подцелей
- Дерево нужных каталогов формируется автоматически
Для установки напрямую в систему и удаления оттуда с правами root поддерживаются цели install и uninstall
см. выше замечания, декорированные подобными иконками ☺
- (никогда так не делайте, да)
Проект, собранный для работы в определённом prefix, можно установить в произвольный каталог с помощью make install DESTDIR=произвольный_каталог
При этом в этом каталоге будет создан prefix и развёрнуто дерево соответствующих подкаталогов
Сама программа, если её запустить из произвольный_каталог/usr/bin скорее всего, правильно не заработает (не найдёт переводов, например)
Это нужно для безопасного формирования двоичной дистрибуции вашей программы (например, из этого можно сделать пакет)
Как узнать параметры установки внутри самой программы?
Autoconf умеет генерировать название-приложения из его полного описания, но это немножко искусственный мозг, поэтому имеет смысл использовать четвёртый параметр AC_INIT — т. н. «tarname»:
AC_INIT([L10n gettext example], [0.0], [george@altlinux.org], [ahello])
Если поискать этот «tarname» в генератах, окажется что он присутствует config.h в виде аж двух макросов — PACKAGE и PACKAGE_TARNAME
- ⇒ хотите знать это название, просто инклюдьте этот файл
Также оно присутствует в configure и Makefilea под именем PACKAGE
А вот каталог, куда предполагается устанавливать переводы, присутствует только в configure и Makefile под именем localedir
⇒ хотите знать этот путь, добавьте в Makefile.am что-то вроде AM_CPPFLAGS = -D LOCALEDIR='"$(localedir)"'
Установка неотслеживаемых файлов
Например, картинок, сопроводительных текстов и т. п. ( проверить, docdir ли):
Д/З
В этот раз задания нет — сборка библиотеки будет в следующем Д/З