Makefile компилирует все файлы каждый раз - PullRequest
0 голосов
/ 03 февраля 2019

Мой Makefile компилирует все файлы каждый раз, когда я запускаю его, хотя файлы не были изменены.Я знаю, что этот вопрос задавался несколько раз, но ни одно из предложенных решений, похоже, не работает для меня.Я новичок в Makefile, и в большинстве случаев я не понимаю жаргон, используемый в решении.Кроме того, я хочу сохранить все сгенерированные файлы .o в папке 'obj'

Вот моя структура папок

project (-)
        gen (-)
            display (-)
                .c and .h files
            logic (-)
                .c and .h files
        lib (-)
            include (-)
                .h files
            .lib files  
        man (-)
            .c and .h files
        obj (-)
            want to save all the .o files here

Я запускаю это в ОС Windows, используя MinGW

Вот мой Makefile:

ALL: demo

SRCS:= filename1.o filename2.o filename3.o filename4.o and so on till filename27.o

demo: display.o logic.o man.o
    gcc $(SRCS) -lglut32 -loglx -lopengl32 -Llib -o demo

display.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c

logic.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/logic/*.c -lglut32 -loglx -lopengl32 -Llib -c

man.o:
    gcc -Igen/display -Igen/logic -Iman -Ilib/include man/*.c -lglut32 -loglx -lopengl32 -Llib -c

clean:
    @echo "Cleaning up.."
    -rm -rf *.o
    -rm *.exe

ПРИМЕЧАНИЕ: файлы glut и oglx присутствуют в папке lib.Display.o, lib.o и man.o не имеют соответствующих файлов .c.Это просто имена папок с множеством файлов c.

Я понимаю, что это может быть проблемой.Поскольку файлы display.o, logic.o и man.o не созданы, MAKE соответствует правилу, связанному с ним.Итак, как мне сказать ему проверить фактические .o filename1.o, filename2.o и т. Д. Для отметки времени и перекомпилировать ТОЛЬКО, если они старше соответствующих файлов c и h, возможно, даже файлов lib, от которых они зависят.

Я попробовал следующее, чтобы создать зависимости и избежать компиляции файлов каждый раз.Но это не помогло.

%.d: %.c
     @set -e; rm -f $@; \
     $(CC) -M $(CFLAGS) $< > $@.$$$$; \
     sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
     rm -f $@.$$$$

Ответы [ 2 ]

0 голосов
/ 03 февраля 2019

Проблема в том, что ваши цели двоичных объектов (например, display.o) на самом деле не соответствуют файлам, созданным по их правилам.Если вы скажете make, что нужно сделать цель display.o, он (обычно, за исключением фальшивых целей, но они всегда перезапускаются) ожидает, что соответствующий файл будет создан по рецепту правила, и он может отследить, должна ли цель бытьпеределан.Если такой файл не создается, эта цель всегда оценивается как устаревшая и нуждающаяся в переделке.

Немного глупого примера этого будет следующее дерево:

.
├── Makefile
├── main.c
└── test
    └── file.c

и Makefile:

main: test.o main.o
    $(CC) -o main *.o

test.o:
    $(CC) $(CFLAGX) -c test/*.c

Нет файла test.o, и цель должна быть переделана ... правило запускается, выдает file.o (снова).Поскольку эта цель была переделана и является обязательным условием main ... все всегда переделывается.

Теперь с этой небольшой модификацией:

main: test.o main.o
    $(CC) -o main *.o

test.o:
    $(CC) $(CFLAGX) -o $@ -c test/*.c

test.o цель действительно производит test.oФайл и правило не нуждаются в переделке, если test.c не изменяется ... и с test.o без изменений и main.c, возможно, также, мы получаем:

$ make
make: 'main' is up to date.

Это все еще не совсем правильно, так какэто действительно должно выглядеть так:

main: test.o main.o
    $(CC) -o main $+

test.o: test/*.c
    $(CC) $(CFLAGX) -o $@ -c $^

Где я объявляю зависимые предпосылки test.o и ссылаюсь на них и на цель с помощью автоматической переменной в рецепте правила.И То же самое касается предпосылок для связывания.Конечно, в этом простом примере я мог бы просто положиться на неявные шаблонные правила и сделать это:

main: test/file.o main.c

test/file.o: test/*.c

Что это значит для вашего make-файла?Когда вы компилируете свои объектные файлы, посмотрите, что они на самом деле производят, и сопоставьте вашу цель с этим или (например, с -o $@) скажите им, чтобы они точно создали файл, соответствующий вашей цели.


Я немного расширил глупый пример, и теперь в test/ есть два файла:

.
├── Makefile
├── main.c
└── test
    ├── file.c
    └── other.c

И Makefile может выглядеть примерно так:

main: obj/file.o obj/other.o main.c

obj/%.o: test/%.c
    mkdir -p obj
    $(CC) $(CFLAGS) -c -o $@ $^

Теперь он хранитОбъектные файлы в obj/ и заставляют все еще понимать, что для чего нужно и могут отслеживать изменения.Конечно, ваша установка более сложна и потребует большего количества правил, возможно, также для разделения фактических источников или промежуточных целей из дерева каталогов и определения нескольких переменных для работы с этой информацией, например:

OBJS := $(patsubst test/%.c,obj/%.o,$(wildcard test/*.c))

main: $(OBJS) main.c

obj/%.o: test/%.c
        mkdir -p obj
        $(CC) $(CFLAGS) -c -o $@ $^

Но принципы остаютсято же самое.

0 голосов
/ 03 февраля 2019

На базовом уровне make ищет строки типа:

target: dependency
    command

Если target не существует, он вызывает правило для dependency и затем запускает command.Если target существует, он проверяет, является ли dependency более новым или не существует.Если это так, он вызывает правило для dependency и затем запускает command.В противном случае оно останавливается.

Важно, что правило для dependency будет вызываться только в том случае, если (a) dependency не существует или (b) dependency новее, чем target.

В этом вопросе предположим, что мы запускаем make demo.Затем make ищет строку, которая начинается с demo:, и замечает, что она объявляет зависимости.Таким образом, он просматривает каждую зависимость по очереди, чтобы увидеть, требуют ли они действияСначала он обнаруживает display.o.Он замечает, что display.o: не существует, поэтому он запускает соответствующее правило.Он делает то же самое для других *.o.

Чтобы избежать выполнения правил *.o всегда, поскольку не существует связанного файла, вы можете переписать его так:

ALL: demo

SRCS:= filename1.o filename2.o filename3.o filename4.o and so on till filename27.o

demo: display.ts logic.ts man.ts
    gcc $(SRCS) -lglut32 -loglx -lopengl32 -Llib -o demo

display.ts: gen/display/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/display/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > display.ts

logic.ts: gen/logic/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include gen/logic/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > logic.ts

man.ts: man/*.c
    gcc -Igen/display -Igen/logic -Iman -Ilib/include man/*.c -lglut32 -loglx -lopengl32 -Llib -c
    echo . > man.ts

clean:
    @echo "Cleaning up.."
    -rm -rf *.o *.ts
    -rm *.exe
...