Различия между версиями 1 и 2
Версия 1 от 2020-10-05 16:04:34
Размер: 4893
Редактор: FrBrGeorge
Комментарий:
Версия 2 от 2020-10-05 17:24:32
Размер: 4852
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 3: Строка 3:
Сначала про настройку окружения
 * `.dircolors`; grep; man; `PS1`
 * `vim`: цветовые схемы, `*~`-файлы, `vimbackup`
 * `zsh`/`bash`: раскраска командной строки, алиасы
 * велосипедный парк
 * ???

== Сборка из нескольких файлов ==
 * Зачем много файлов?
  * Быстрее компилировать не все
  * Проще ориентироваться
  * Си: пространства имён!
  * (!) пример
 * Скомпилировать сразу все файлы?
 * (пере)компиляция только изменённых
  1. Компиляция до `.o`
  1. [[ FrBrGeorge/LinkingC |Компоновка]]
   * (повтор: сложность компоновки вручную с помощью `ld`)
== Make ==
Проблемы многофайловой сборки: вручную муторно, сценарий делает много лишнего
 1. Перекомпиляция только обновлённых исходников
  * Построение графа зависимостей и подграфа пересборки
 1. Удаление генератов
  * В том числе того, что ''разработчик'' считает генератом
 1. Скриптование / code reuse приёмов сборки
  * в т. ч. уникальных для проекта

{{{#!wiki caution
ТАБУЛЯЦИИ!
}}}
=== Gnu make ===
(есть много, но этот популярнее всех остальных, вместе взатых)
 * Цели и рецепты
  * Как правило «как создать файл из других файлов» (например, `prog.o` из `prog.c`)
  * Вариант: «как создать файл ''определённого типа'' из других файлов ''другого определённого типа''» (например `.o` из `.c`
 * Командный интерпретатор ОС в качестве языка сценариев (как правило, shell)
  * ⇒ нужен shell под системы без shell
 * Переменные
  * Специальные переменные
  * Подстановка
  * Отложенное и немедленное присваивание
 * Много правил по умолчанию
 * … (что успеем)


=== Литература по GNU Make ===
 * [[http://grep.cs.msu.ru/BESTA/ruprog/make.doc.txt|Очень старый]] учебник по старому Make (зато подробный и с объяснениями)
 * [[https://www.oreilly.com/library/view/managing-projects-with/0596006101/|Managing Projects with GNU Make, 3rd Edition]]
 * [[https://www.gnu.org/software/make/manual/make.html|Documentation]]
  * [[http://rus-linux.net/nlib.php?name=/MyLDP/algol/gnu_make/gnu_make_3-79_russian_manual.html|Русский перевод]]
 * Книга Владимира Игнатова [[https://www.opennet.ru/docs/RUS/gnumake/|Эффективное использование GNU Make]]
== Пример ==
Строка 57: Строка 5:
|| <<MiniPage({{{#!highlight c\n#include "outlib.h"\nint main(int argc, char *argv[]) {\n Count = argc;\n output("<INIT>");\n output(argc>1 ? argv[1] : "<NOPE>");\n output("<DONE>");\n return 0;\n}\n}}})>> || <<MiniPage({{{#!highlight c\n#include <stdio.h>\n#include "outlib.h"\nvoid output(char *str) {\n printf("%d: %s\n", Count++, str);\n}\n}}})>> || || <<MiniPage({{{#!highlight c\n#include "outlib.h"\nint main(int argc, char *argv[]) {\n Count = argc;\n output("<INIT>");\n output(argc>1 ? argv[1] : "<NOPE>");\n output("<DONE>");\n return 0;\n}\n}}})>> || <<MiniPage({{{#!highlight c\n#include <stdio.h>\n#include "outlib.h"\nvoid output(char *str) {\n printf("%d: %s\012", Count++, str);\n}\n}}})>> ||
Строка 89: Строка 37:
  * Кстати, вот альтернативная форма, в которой нет табуляций.   * Раздельная компиляция работает:
  {{{#!highlight console
$ make clean
rm -f prog a.out *.o
$ make prog
cc const.c -c -o const.o
cc fun.c -c -o fun.o
cc prog.c -c -o prog.o
cc const.o fun.o prog.o -o prog
$ touch fun.c
$ make prog
cc fun.c -c -o fun.o
cc const.o fun.o prog.o -o prog
$ touch fun.o
$ make prog
cc const.o fun.o prog.o -o prog
  }}}
  * Кстати, вот альтернативная форма, в которой нет табуляций. Она малочитаема, не будем ей пользоваться:
  {{{#!highlight makefile
prog: const.o fun.o prog.o ; cc const.o fun.o prog.o -o prog

fun.o: fun.c ; cc fun.c -c -o fun.o

prog.o: prog.c ; cc prog.c -c -o prog.o

const.o: const.c ; cc const.c -c -o const.o

clean: ; rm -f prog a.out *.
 }}}
   (а табуляции и пробелы становятся обычными разделителями)
  * И ещё одна. На этот раз полями рецепта определяются не табуляции, а тильды (а табуляции и пробелы становятся обычными разделителями):
  {{{#!highlight makefile
.RECIPEPREFIX = ~
prog: const.o fun.o prog.o
~ cc const.o fun.o prog.o -o prog

fun.o: fun.c
~ cc fun.c -c -o fun.o

prog.o: prog.c
~ cc prog.c -c -o prog.o

const.o: const.c
~ cc const.c -c -o const.o

clean:
~ rm -f prog a.out *.o
  }}}
 1. Хорошо бы задать правило по умолчанию, как делать `.o` файлы из `.c`.
 {{{#!highlight makefile
%.o: %.c
 cc $< -c -o $@

prog: const.o fun.o prog.o
 cc $^ -o $@

clean:
 rm -f prog a.out *.o
 }}}
  * Конструкции, начинающиеся с `$` — подстановки значения некоторых переменных Make:
   * `$@` означает цель (похожа на цель в тире)
   * `$<` означает первую из зависимостей
   * `$^` означает список всех зависимостей
 1. В Make есть и нормальные переменные, только их подстановка должна заключаться в круглые скобки. Заодно усложним и нашу программу.
|| prog.c || fun.c ||
|| <<MiniPage({{{#!highlight c\n#include <stdio.h>\n#include "outlib.h"\nint main(int argc, char *argv[]) {\n int i;\n if((Count = argc)>1) {\n output("<INIT>");\n for(i=1; i<argc; i++)\n output(argv[i]);\n output("<DONE>");\n }\n else\n usage(argv[0]);\n return 0;\n}\n}}})>> || <<MiniPage({{{#!highlight c\n#include <stdio.h>\n#include "outlib.h"\nvoid output(char *str) {\n printf("%d: %s\012", Count++, str);\n}\n\nvoid usage(char *prog) {\n fprintf(stderr, "%s: Print all arguments\012\t"\\n "Usage: %s arg1 [arg2 […]]\012", prog, prog);\n}\n}}})>> ||
|| const.c || outlib.h ||
|| <<MiniPage({{{#!highlight c\nint Count=0;\n}}})>> || <<MiniPage({{{#!highlight c\nvoid output(char *);\nvoid usage(char *);\nextern int Count;\n}}})>> ||

Пример написания и использования Makefile

Начнём с таких вот файлов:

prog.c

fun.c

   1 #include "outlib.h"
   2 int main(int argc, char *argv[]) {
   3        Count = argc;
   4  output("<INIT>");
   5      output(argc>1 ? argv[1] : "<NOPE>");
   6   output("<DONE>");
   7      return 0;
   8 }

   1 #include <stdio.h>
   2 #include "outlib.h"
   3 void output(char *str) {
   4         printf("%d: %s\012", Count++, str);
   5 }

const.c

outlib.h

   1 int Count=0;

   1 void output(char *);
   2 extern int Count;

  1. Их можно собрать в один файл просто с помощью cc *.c -o prog

  2. Напишем простейший Makefile для этого:
       1 prog:   const.c fun.c prog.c
       2         cc const.c fun.c prog.c -o prog
       3 
       4 clean:
       5         rm -f prog a.out 
    
    • Помните о табах!
    • Заодно сделаем цель clean. Пробуем make, make clean.

  3. Так можно и скриптом было сделать. Обеспечим раздельную компиляцию и компоновку.
       1 prog:   const.o fun.o prog.o
       2         cc const.o fun.o prog.o -o prog
       3 
       4 fun.o:  fun.c 
       5         cc fun.c -c -o fun.o
       6 
       7 prog.o: prog.c 
       8         cc prog.c -c -o prog.o
       9 
      10 const.o:        const.c 
      11         cc const.c -c -o const.o
      12 
      13 clean:
      14         rm -f prog a.out *.o
      15  
    
    • Раздельная компиляция работает:
         1 $ make clean
         2 rm -f prog a.out *.o
         3 $ make prog
         4 cc const.c -c -o const.o
         5 cc fun.c -c -o fun.o
         6 cc prog.c -c -o prog.o
         7 cc const.o fun.o prog.o -o prog
         8 $ touch fun.c 
         9 $ make prog  
        10 cc fun.c -c -o fun.o
        11 cc const.o fun.o prog.o -o prog
        12 $ touch fun.o
        13 $ make prog  
        14 cc const.o fun.o prog.o -o prog
        15 
      
    • Кстати, вот альтернативная форма, в которой нет табуляций. Она малочитаема, не будем ей пользоваться:
         1 prog:   const.o fun.o prog.o ; cc const.o fun.o prog.o -o prog
         2 
         3 fun.o:  fun.c  ; cc fun.c -c -o fun.o
         4 
         5 prog.o: prog.c  ; cc prog.c -c -o prog.o
         6 
         7 const.o:        const.c  ; cc const.c -c -o const.o
         8 
         9 clean: ; rm -f prog a.out *. 
      
      • (а табуляции и пробелы становятся обычными разделителями)
    • И ещё одна. На этот раз полями рецепта определяются не табуляции, а тильды (а табуляции и пробелы становятся обычными разделителями):
         1 .RECIPEPREFIX = ~
         2 prog:   const.o fun.o prog.o
         3 ~       cc const.o fun.o prog.o -o prog
         4 
         5 fun.o:  fun.c
         6 ~       cc fun.c -c -o fun.o
         7 
         8 prog.o: prog.c
         9 ~       cc prog.c -c -o prog.o
        10 
        11 const.o:        const.c
        12 ~       cc const.c -c -o const.o
        13 
        14 clean:
        15 ~       rm -f prog a.out *.o
      
  4. Хорошо бы задать правило по умолчанию, как делать .o файлы из .c.

       1 %.o:    %.c
       2         cc $< -c -o $@
       3 
       4 prog:   const.o fun.o prog.o
       5         cc $^ -o $@
       6 
       7 clean:
       8         rm -f prog a.out *.o
    
    • Конструкции, начинающиеся с $ — подстановки значения некоторых переменных Make:

      • $@ означает цель (похожа на цель в тире)

      • $< означает первую из зависимостей

      • $^ означает список всех зависимостей

  5. В Make есть и нормальные переменные, только их подстановка должна заключаться в круглые скобки. Заодно усложним и нашу программу.

prog.c

fun.c

   1 #include <stdio.h>
   2 #include "outlib.h"
   3 int main(int argc, char *argv[]) {
   4         int i;
   5         if((Count = argc)>1) {
   6                 output("<INIT>");
   7                 for(i=1; i<argc; i++)
   8                         output(argv[i]);
   9                 output("<DONE>");
  10         }
  11         else
  12                 usage(argv[0]);
  13         return 0;
  14 }

   1 #include <stdio.h>
   2 #include "outlib.h"
   3 void output(char *str) {
   4         printf("%d: %s\012", Count++, str);
   5 }
   6 
   7 void usage(char *prog) {
   8         fprintf(stderr, "%s: Print all arguments\012\t"\
   9                 "Usage: %s arg1 [arg2 […]]\012", prog, prog);
  10 }

const.c

outlib.h

   1 int Count=0;

   1 void output(char *);
   2 void usage(char *);
   3 extern int Count;

FrBrGeorge/MakefileExample (последним исправлял пользователь FrBrGeorge 2022-10-02 00:18:06)