Пример написания и использования 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;

Makefile

  •    1 GENERATES = prog README
       2 TRASH = *.o *~ o.*
       3 
       4 %.o:    %.c
       5   cc $< -c -o $@
       6 
       7 all:    README prog
       8 
       9 prog:   const.o fun.o prog.o
      10  cc $^ -o $@
      11 
      12 README: prog
      13    ./$< 2> $@
      14 
      15 clean:
      16   rm -f $(TRASH)
      17 
      18 distclean:      clean
      19        rm -rf $(GENERATES)