Мой make-файл продолжает компилироваться сам по себе;Что я делаю неправильно? - PullRequest
0 голосов
/ 06 февраля 2019

Я не уверен, есть ли какая-то встроенная переменная или правило, о которых я не знаю, или что-то не так с make, или я просто сумасшедший.

Для одногоиз моих проектов у меня есть make-файл следующим образом:

CC=g++
CFLAGS=-O3 `libpng-config --cflags`
LFLAGS=-lm `libpng-config --ldflags`

OBJS=basic_render.o render.o mandel.o
BINS=basic_render

.PHONY: all clean

all: $(BINS)

clean:
    rm -f $(BINS) $(OBJS)

%.o: %.cpp
    $(CC) $(CFLAGS) -c -o $@ $<

%: $(OBJS)
    $(CC) $(LFLAGS) -o $@ $(OBJS)

При сборке я хочу просто иметь возможность запустить

make clean
make

, чтобы собрать все в списке BINS.

Сначала это работает нормально, но по какой-то причине поведение меняется после того, как я редактирую исходный файл.

Перед редактированием исходного файла:

$ make clean
rm -f basic_render basic_render.o render.o mandel.o
$ make
g++ -O3 `libpng-config --cflags` -c -o basic_render.o basic_render.cpp
g++ -O3 `libpng-config --cflags` -c -o render.o render.cpp
g++ -O3 `libpng-config --cflags` -c -o mandel.o mandel.cpp
g++ -lm `libpng-config --ldflags` -o basic_render basic_render.o render.o mandel.o
rm mandel.o basic_render.o render.o

Я могу сделать этоснова и снова, и это работает просто отлично.После того, как я внес изменения в basic_render.cpp (буквально просто меняя пару констант), он неожиданно изменится на следующее:

$ make clean
g++ -O3 `libpng-config --cflags` -c -o basic_render.o basic_render.cpp
g++ -O3 `libpng-config --cflags` -c -o render.o render.cpp
g++ -O3 `libpng-config --cflags` -c -o mandel.o mandel.cpp
g++ -lm `libpng-config --ldflags` -o makefile basic_render.o render.o mandel.o
rm mandel.o basic_render.o render.o
makefile:1: warning: NUL character seen; rest of line ignored
makefile:1: *** missing separator.  Stop.

не только make clean просто попытался скомпилировать программу, но и скомпилировал basic_render с выводом, установленным в Makefile, перезаписывая сам Makefile.

После редактирования basic_render.cpp я посмотрел в Makefile, и он не изменился, так что мой редактор неизменение make-файла или что-то в этом роде.

Итак, что я здесь не так делаю?

Ответы [ 2 ]

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

Вот MCVE вашей проблемы:

$ ls -R
.:
bar.c  main.c  Makefile

$ cat main.c
extern int bar(void);

int main(void)
{
    bar();
    return 0;
}

$ cat bar.c
int bar(void)
{
    return 42;
}

$ cat Makefile
OBJS := main.o bar.o
BINS := prog

.PHONY: all clean

all: $(BINS)

%: $(OBJS)
    $(CC) -o $@ $(OBJS)

clean:
    $(RM) $(OBJS) $(BINS)

Выполните в первый раз:

$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o prog main.o bar.o
rm bar.o main.o

Пауза, чтобы заметить нежелательные последствия 10.4 Цепи неявных правил :

rm bar.o main.o

Все объектные файлы автоматически удаляются после связывания программы, что отрицательно сказывается на цели Make.Неявные правила, в которых мы виноваты, это наше собственное неявное правило:

%: $(OBJS)
    $(CC) -o $@ $(OBJS)

плюс встроенное неявное правило 1 :

%.o: %.c
#  recipe to execute (built-in):
    $(COMPILE.c) $(OUTPUT_OPTION) $<

, которые вместе составляютнеявная цепочка правил, которая приводит к тому, что все объектные файлы становятся промежуточными файлами .

Продолжая, давайте обновим исходный файл:

$ touch main.c

и сделаем второй раз:

$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o Makefile main.o bar.o
rm bar.o main.o
Makefile:1: warning: NUL character seen; rest of line ignored
Makefile:1: *** missing separator. Stop.

Наш Makefile перекрыт связующим звеном:

cc -o Makefile main.o bar.o

Этот snafu объяснен в руководстве 3.5 Как восстанавливаются Makefile :

Иногда make-файлы могут быть переделаны из других файлов, таких как файлы RCS или SCCS.Если make-файл может быть переделан из других файлов, вы, вероятно, захотите, чтобы make получал актуальную версию make-файла для чтения.

С этой целью после чтения во всех make-файлах выполнитебудет рассматривать каждый как цель цели и попытаться обновить его.Если в make-файле есть правило, в котором указано, как его обновить (находится либо в этом самом make-файле, либо в другом), либо если к нему применяется неявное правило (см. «Использование неявных правил»), оно будет обновлено при необходимости .После того, как все make-файлы были проверены, если они действительно были изменены, make запускается с чистого листа и снова читает все make-файлы.(Он также попытается обновить каждый из них снова, но обычно это не изменит их снова, поскольку они уже обновлены.)

(выделение мое).Существует ли неявное правило, применимое к Makefile, которое считается целью?Да, это:

%: $(OBJS)
    $(CC) -o $@ $(OBJS)

, поскольку целевой шаблон % соответствует любому файлу независимо от того.Если мы восстановим наш сжатый Makefile и попробуем ту же самую экспериментальную вещь снова, на этот раз с отладкой:

make -d >debug.log 2>&1

, результат покажет нам:

...
Reading makefiles...
Reading makefile 'Makefile'...
Updating makefiles....
 Considering target file 'Makefile'.
  Looking for an implicit rule for 'Makefile'.
  ...
  ...
  Found an implicit rule for 'Makefile'.
  ...
  ...
  Finished prerequisites of target file 'Makefile'.
  Prerequisite 'main.o' is newer than target 'Makefile'.
  Prerequisite 'bar.o' is newer than target 'Makefile'.
 Must remake target 'Makefile'.
cc -o Makefile main.o bar.o
...

Мы можем избежать этого результата, итакже саморазрушающееся автоудаление наших объектных файлов, не используя неявное правило match-everything для выполнения нашей связи.Обычным делом является создание программы из ее объектных файлов по явному правилу, например

Makefile (2)

OBJS := main.o bar.o
BIN := prog

.PHONY: all clean

all: $(BIN)

$(BIN): $(OBJS)
    $(CC) -o $@ $(OBJS)

clean:
    $(RM) $(OBJS) $(BIN)

Похоже, что вы дорожите опциейиметь BINS список нескольких программ:

Я хочу просто иметь возможность запускать

make clean

make

чтобы построить все в списке BINS.

Но учтите это с:

BINS := prog1 prog2

и рецептом:

%: $(OBJS)
    $(CC) $(LFLAGS) -o $@ $(OBJS)

как способ сделать всев списке BINS вы просто создадите одну и ту же программу дважды с двумя разными именами.И даже если вы хотели сделать это, способ сделать это будет:

Makefile (3)

OBJS := main.o bar.o
BINS := prog1 prog2

.PHONY: all clean

all: $(BINS)

$(BINS): $(OBJS)
    $(CC) -o $@ $(OBJS)

clean:
    $(RM) $(OBJS) $(BIN)

, который выполняетсянапример:

$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o prog1 main.o bar.o
cc -o prog2 main.o bar.o

[1] Вы можете заставить GNU Make показать вам все встроенные правила и все остальные правила для конкретной сборки, набрав make --print-data-base ...

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

Я бы предположил, что цель % как-то совпадает с файлом makefile и, следовательно, использует его в качестве цели (1) .

Мой совет будет изменить этострока к:

$(BINS): $(OBJS)

, и это, мы надеемся, не позволит make думать, что это должно создать новый makefile при изменении объектов.


(1) Помимо предоставленных вами явных правил, make также содержит довольно много неявных правил.

Если хотя бы одно из этих правил решит, что онозависит от makefile (это часто настраивается, поскольку изменение на makefile обычно означает, что необходимо выполнить полное перестроение, поскольку правила вполне могли измениться), тогда цель % может затем использоваться для makefile.

И, поскольку объекты изменились, makefile, который зависит от них, будет восстановлен.

У меня лично никогда не было %цель, так как я считаю, что это означает, что правило может соответствовать любой цели, включая исходные файлы, которыеich вы, вероятно, не хотите перезаписать.

Если вам интересно увидеть все эти неявные правила, make -d должно дать вам совсем немного информации, такой как используемые правила иусловия проверены на необходимость перестроения файлов - просто будьте готовы к большому выводу.

...