Это пример программы, чтобы упростить объяснение. Имейте это 4 файла, main.c, sum.c и header.h. и, наконец, Makefile.
main.c
#include <stdio.h>
#include "header.h"
int main()
{
int a = 1;
int b = 1;
int c = sum(a, b);
printf("Sum: %i\n",c);
return (0);
}
сумма.с
#include "header.h"
int sum(int a, int b)
{
return (a + b);
}
заголовок.h
int sum(int a, int b);
Makefile
TEST_SRCS = *.c
TEST_OBJS = $(TEST_SRCS:%.c=%.o)
%.o : %.c *.h
gcc -c $^
assemble_test: $(TEST_OBJS);
test: assemble_test
gcc -o test *.o
./test
clean:
@rm -rf *.o *.gch test
@echo "Cleaned"
Действия по воспроизведению:
Вызовите тестовое правило: сделайте тест> все в порядке.
Вводите любую ошибку специально, например, две запятые:
printf("Sum: %i\n",,c);
Правило проверки вызовов: make test > Получение обычной синтаксической ошибки.
Удалите ошибку. Сохранить файл.
Правило вызова теста: make test > Получение ошибки компоновщика, несмотря ни на что.
gcc -o test *.o
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [test] Error 1
Используйте чистое правило, чтобы каждый раз начинать заново.
У меня такое поведение какое-то время, и я не могу понять проблему. Честно говоря, я новичок в Makefiles, поэтому я должен делать что-то очень неправильно.
О тестовом правиле, вызывающем assemble_test. Это потому, что это часть большого Makefile, и мне нужны разные правила для разных целей для компиляции объектов, так что не возражайте.
Не могли бы вы помочь мне понять, что происходит?
Спасибо.
Неправильно использовать подстановочные знаки для объектных файлов. Подстановочные знаки соответствуют файлам, которые уже существуют на диске при запуске make
. Это не помогает, потому что очевидно, что они не будут соответствовать файлам, которых еще не существует, а весь смысл вашего make-файла в том, чтобы создавать несуществующие файлы.
Итак, это:
TEST_SRCS = *.c
TEST_OBJS = $(TEST_SRCS:%.c=%.o)
оставляет значение TEST_SRCS
строки *.c
и значение TEST_OBJS
строки *.o
(возможно, вы думали, что подстановочные знаки каким-то образом сразу расширяются? Нет, это не так).
Затем эта строка:
assemble_test: $(TEST_OBJS);
заставляет цель assemble_test
перечислить *.o
в качестве предварительного условия: это будет расширено на все объектные файлы, которые уже существуют в каталоге. Но, конечно, он не будет распространяться на объектные файлы, которые вы хотите создать, но еще не существуют.
Здесь есть много других проблем: например, вы не хотите компилировать $^
, потому что это распространяется на все предварительные условия, но вы хотите компилировать только исходные файлы, а не файлы заголовков. А добавление assemble_test
кажется странным и усложняет задачу.
Вы, вероятно, захотите этого, предполагая, что используете GNU make:
TEST_SRCS := $(wildcard *.c )
TEST_OBJS = $(TEST_SRCS:%.c=%.o)
%.o : %.c *.h
gcc -c $<
test: $(TEST_OBJS)
gcc -o $@ $^
./$@
Используя функцию wildcard
, вы сначала расширяете значение глобуса источников, прежде чем заменять результаты на .o
.
Функция подстановочных знаков решила проблему, так много нужно узнать ... - Что касается .h как предварительного условия для компиляции, мне нужно сделать, чтобы знать о возможных модификациях .h, и если это так, перекомпилировать объектные файлы. Это работало, как задумано. - Что касается вызова assemble_test, как вы сказали мне, make wil будет жаловаться на то, что делать нечего, я хочу иметь возможность вызывать test любое количество раз. Большое спасибо за решение!
Я не говорил, что вы не должны добавлять заголовок в качестве предварительного условия. Я сказал, что вы не должны передавать его в качестве аргумента компилятору. Если вы используете gcc -c $^
, то $^
расширяется до всех предварительных условий, поэтому, если у вас есть foo.c foo.h bar.h baz.h
в качестве предварительных условий в make, вы вызовете компилятор, используя gcc -c foo.c foo.h bar.h baz.h
, что неверно. Вы хотите использовать $<
, который расширяется только до первого пререквизита, а не до всех пререквизитов, поэтому ваш компилятор вызывается как gcc -c foo.c
, что правильно.
Что касается assemble_test
, если вы хотите создать тест один раз и запустить его несколько раз, вы должны сделать это следующим образом: test: $(TEST_OBJS) ; gcc -o $@ $^
будет построена тестовая программа, затем run: test ; ./$<
будет запущена тестовая программа, затем используйте make run
для запустить его. Вы также можете добавить .PHONY: run
в свой make-файл, чтобы быть более понятным.
Комментарии перемещены в чат ; пожалуйста, не продолжайте обсуждение здесь. Прежде чем публиковать комментарий под этим, пожалуйста, ознакомьтесь с целями комментариев . Комментарии, которые не требуют разъяснений или предложений по улучшению, обычно относятся к ответу , к Meta Stack Overflow или в чату переполнения стека. Комментарии, продолжающие обсуждение, могут быть удалены.