Различия между версиями 11 и 12
Версия 11 от 2022-11-24 14:37:11
Размер: 32622
Редактор: ArsenyMaslennikov
Комментарий: prefx -> prefix, "SNAP" -> "Snap", тестиварония, биранрик
Версия 12 от 2022-11-28 12:44:25
Размер: 32729
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 517: Строка 517:
'''TODO''' В этот раз задания нет — сборка библиотеки будет в следующем Д/З

Библиотеки и сторонние исходники

TODO «Установка» не влезает — перенести

Libtool

libtool — инструмент для кроссплатформенной сборки и тестирования библиотек.

Пример:

  • lib.c

       1 #include "lib.h"
       2 
       3 int inc(int var) {
       4   return var + inc_var;
       5 }
    
  • libg.c

       1 #include "lib.h"
       2 int inc_var = 1;
    
  • lib.h

       1 int inc(int);
       2 extern int inc_var;
    
  • main.c

       1 #include <stdio.h>
       2 #include <stdlib.h>
       3 #include "lib.h"
       4 
       5 int main(int argc, char *argv[]) {
       6         int n;
       7 
       8         if(argc < 2) {
       9           fprintf(stderr, "Usage: %s NUMBER\n", argv[0]);
      10           return 1;
      11         }
      12         
      13         n = atoi(argv[1]);
      14         printf("%d\n", inc(n));
      15 
      16  return 0;
      17 }
    
  • Makefile к ним

       1 all:   inc
       2 
       3 libinc.so:     lib.c libg.c
       4        gcc -fPIC -shared $^ -o $@
       5 
       6 inc:   main.c libinc.so
       7        gcc -L. $< -linc -o $@
       8 
       9 check: inc
      10        LD_LIBRARY_PATH=`pwd` ./inc 123
      11 clean:
      12        rm -f *.so inc
    

А теперь с 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, а исходники не соответствуют принятой в вашем сообществе дисциплине, или апстрим не считает багу ошибкой, а вы считаете ☺
  • В любом случае при обновлении апстрима исправления придётся вносить заново

Цикл обновления / внесения изменений:

  1. Первоначальный импорт исходников
    1. Адаптация к вашим нуждам
    2. Оформление серии патчей (см. ниже)

    3. Релиз
  2. Отныне и навеки
    1. Обновление апстримной версии
    2. Применение патчей к обновлённой версии
    3. Адаптация отвалившихся патчей
    4. Релиз

Утилита 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: как из одного файла сделать другой

       1 11a12
       2 >   /* Simulate fopen flags */
       3 13d13
       4 <     case 'b': /* "a+" */
       5 14a15
       6 >     case 'b': /* "a+" */
    
  • diff -e — команды для текстового редактора ed

       1 14a
       2     case 'b': /* "a+" */
       3 .
       4 13d
       5 11a
       6   /* Simulate fopen flags */
       7 .
    
  • 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» и называется патчем

  • А несколько таких патчей в правильном порядке — патчсетом
       1 $ cp ffopen.c ffopen_changed.c
       2 $ diff -u ffopen.c ffopen_new.c > changeset.patch
       3 $ patch ffopen_changed.c < changeset.patch
       4 patching file ffopen_changed.c
       5 $ diff -s  ffopen_new.c ffopen_changed.c
       6 Файлы ffopen_new.c и ffopen_changed.c идентичны
       7 
    

Что гораздо важнее, 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 $ cp ffopen2.c ffopen2_changed.c
       2 $ patch ffopen2_changed.c < changeset.patch
       3 patching file ffopen2_changed.c
       4 Hunk #1 succeeded at 9 with fuzz 1.
       5 Hunk #2 succeeded at 33 (offset 1 line).
       6 
    
    • Контекст первого блока неточен (в патче одна строка контекста слегка другая); тем не менее это очень похожий контекст (с разницей 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 
       2   switch(tolower(mode[0])+(mode[1]=='+')) {
       3     case 'b': /* alias for "a+" */
       4     case 'a': m = O_WRONLY | O_CREAT | O_APPEND; break;
       5 
    
  • Поскольку изменения сделаны прямо в исправляемой строке, такой блок не приложится; второй блок приложится нормально:
       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:

  1. Не устанавливать. Собрать всё в каком-то подкаталоге и запускать оттуда.
    • {o} Просто собрать!

    • Много лишних файлов
    • Не очень понятно как читать man

    • Скорее всего, не будет работать, если мы не сделаем cd этот каталог

    • Надо разбираться с $LD_LIBRARY_PATH

  2. Вариант: собирать один большой статический бинарник со всем, так делает Qt

    • Требует программного решения (данные-«ресурсы»)
    • Требует статической компоновки с большинством библиотек
    • {o} можно запускать его откуда угодно

  3. Установить в специальный отдельный подкаталог системы все нужные файлы
    • Как правило, это /opt/название-приложения

    • Требует прав root

    • Надо разбираться с $LD_LIBRARY_PATH

    • Скорее всего, не будет работать, если мы не сделаем cd этот каталог; но тогда надо отличать два вида файлов

      • RO — те, что установлены в /opt/название-приложения

      • RW — те, которые образуются в процессе работы приложения (например, локальные конфиги в $HOME/.config/название-приложения).

    • Само приложение придётся запускать /opt/название-приложения/bin/название-приложения (или как-то так), либо для каждого добавлять /opt/название-очередного-приложения/bin/ в $PATH

    • {o} Удаление такого приложения — просто удаление каталога /opt/название-приложения

  4. «Крибле! Крабле! Бумс!», оно же «configure - make - make install»
    • Требует прав root
    • Приедет по умолчанию в /usr/local/*, особо никому не мешает, /usr/local/lib можно добавить один раз в системный ld.so.cache, а /usr/local/bin/ — в $PATH

      • Вариант: configure --prefx=/usr — можно попортить систему

    • Проблема деинсталляции
  5. Сформировать пакет / AppImage / Snap / …

    • Установка, удаление
    • Интеграция с системой или, наоборот, изоляция
    • Надо уметь собирать пакеты ☹
    • Если вы не майнтейнер пакета в определённом сообществе, а апстрим, придётся собирать много разных пакетов, что ли??

  6. ( /!\ за это вам уготованы мучения в аду /!\ ) Использовать самописный «инсталлятор»

Вывод:

  • Несвободный софт; или громадный проект, который никто, кроме вас, собирать не будет

    • или /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 ли):

   1 install-data-local:
   2         mkdir -p $(DESTDIR)/$(docdir)
   3         cp -a html $(DESTDIR)/$(docdir)

Д/З

В этот раз задания нет — сборка библиотеки будет в следующем Д/З

LecturesCMC/LinuxApplicationDevelopment2022/10_LibrariesPatch (последним исправлял пользователь FrBrGeorge 2022-11-28 12:44:25)