Создание цели устаревшей, если параметры компилятора отличаются от ранее использованных - PullRequest
0 голосов
/ 10 декабря 2018

Рассмотрим следующее дерево каталогов для примера готовой игрушки, иллюстрирующего решаемую проблему:

- Makefile
- lib.c
+ foo/
   |-- config.mk
+ bar/
   |-- config.mk
  • Содержимое foo/config.mk:

    CFLAGS += -DFOO
    
  • Содержание bar/config.mk:

    CFLAGS += -DBAR
    
  • Вызов make с Makefile для целей foo и bar приводит квключаемые файлы foo/config.mk и bar/config.mk (посредством директивы include ) соответственно и строящиеся lib.o, то есть:

    # build lib.o with the macro FOO defined
    $ make foo
    
    # build lib.o with the macro BAR defined
    $ make bar
    
    # build lib.o with both the macros FOO and BAR defined
    $ make foo bar
    $ make bar foo
    

Правило по умолчанию для построения lib.o использует переменную COMPILE.c, которая определяется (в соответствии с выводом, полученным при вызове make с параметром --print-data-base) как:

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

Расширение COMPILE.c зависит, среди прочего, от значения переменной CFLAGS, которое, в свою очередь, зависит от того, были ли foo/config.mk или bar/config.mkвключены, так как эти make-файлы изменяют переменную CFLAGS.


Что я хотел бы достичь, так это обработать цель lib.o как устаревшую target, если расширение используемой в данный момент переменной COMPILE.c отличается от того, которое использовалось для предыдущей сборки lib.o.Например:

$ make foo

# It shouldn't rebuild anything since lib.o should be up-to-date
$ make foo

# It should rebuild lib.o since it should be out-of-date
$ make bar

# It should rebuild lib.o since it is again out-of-date
$ make foo bar

# It shouldn't rebuild lib.o since it is up-to-date
$ make bar foo   

Это решение объясняет, как я реализовал это поведение до сих пор.Любое предложение приветствуется.

Ответы [ 3 ]

0 голосов
/ 10 декабря 2018

Пока что мой подход заключается в создании файла cc-options-dump с содержимым раскрытия переменной COMPILE.c каждый раз, когда создается lib.o.

Хеш MD5, полученный в результатерасширение текущей переменной COMPILE.c сравнивается с той, которая использовалась для предыдущей сборки, т. е. той, чье содержимое хранится в файле cc-options-dump, если таковое имеется (то есть, если файл существует).

.PHONY: foo bar require-rebuild cc-options-dump

# include the xxx/config.mk files
# sort built-in function to make it independent of the order (i.e., "foo bar" or "bar foo")
$(foreach opt,$(sort $(MAKECMDGOALS)),$(eval include $(opt)/config.mk))

# obtain the MD5 hash of the previously used flags
ifneq (,$(wildcard cc-options-dump))
prev-cc-options-hash := $(shell md5sum cc-options-dump | cut -f1 -d' ')
endif

# obtain the MD5 hash of the current flags
curr-cc-options-hash := $(shell echo "$(COMPILE.c)" | md5sum | cut -f1 -d' ')

# Do these hashes differ?
ifneq ($(prev-cc-options-hash),$(curr-cc-options-hash))
# keep track of the fact that a rebuilt needs to be triggered
does-need-rebuild := require-rebuild
# just for displaying information
$(info Rebuild required)
else
$(info Rebuild not required)
endif

# make both targets foo and bar dependent on the file with the flags
# so that it has to be generated, since it is a phony target as well 
foo bar: cc-options-dump

# lib.o has now an additional prerequisite for determining whether it need to be rebuilt
lib.o: $(does-need-rebuild)

# save the used compiler flags for comparing them with the future flags
cc-options-dump: lib.o
    @echo '$(COMPILE.c)' >$@

Поведение этого make-файла при вызове make для целей foo и / или bar соответствует, насколько я вижу, желаемому:

$ make foo
Rebuild required
cc -DFOO   -c -o lib.o lib.c

$ make foo
Rebuild not required

$ make bar
Rebuild required
cc -DBAR   -c -o lib.o lib.c

$ make bar
Rebuild not required

$ make foo bar
Rebuild required
cc -DBAR -DFOO   -c -o lib.o lib.c

$ make bar foo
Rebuild not required

Использование встроенной функции sort крайне важно для правильной работы последних двух приведенных выше случаев.

Было бы замечательно, если бы кто-то мог прийти с болееэлегантное решение.

0 голосов
/ 10 декабря 2018

Вот мой грубый макет, который кодирует флаги компилятора / компоновщика непосредственно в именах файлов.Это просто идея, фактическая реализация должна быть немного более надежной.

empty:=
space:= $(empty) $(empty)
comma:= ,

LDFLAGS_NS=$(subst $(space),$(comma),$(LDFLAGS))
CFLAGS_NS=$(subst $(space),$(comma),$(CFLAGS))

EXEDIR=./test-exedir-cflags=$(CFLAGS_NS)-ldflags=$(LDFLAGS_NS)
EXEDIR_PAT=./test-exedir-*
OBJDIR=./test-objdir-cflags=$(CFLAGS_NS)
OBJDIR_PAT=./test-objdir-*

test.exe: $(EXEDIR)/test.exe
    rm -f test
    ln -s $< $@

$(EXEDIR)/test.exe: test.o
    rm -rf $(EXEDIR_PAT)
    mkdir $(EXEDIR)
    cc $(LDFLAGS) -o $@ $<

test.o: $(OBJDIR)/test.o
    rm -f test.o
    ln -s $< $@

$(OBJDIR)/test.o: test.c
    rm -rf $(OBJDIR_PAT)
    mkdir $(OBJDIR)
    cc -c $(CFLAGS) -o $@ $<

Протестировано с помощью файла hello world test.c и следующих команд:

make test.exe
make test.exe
make test.exe CFLAGS="-g -Wall"
make test.exe CFLAGS="-g -Wall"
make test.exe
0 голосов
/ 10 декабря 2018

Я бы сбросил значение переменной в другой включенный make-файл и проверил, отличается ли текущее значение от значения из включенного make-файла.Что-то вроде:

ifeq ($(filter foo,$(MAKECMDGOALS)),foo)
include foo/config.mk
endif
ifeq ($(filter bar,$(MAKECMDGOALS)),bar)
include bar/config.mk
endif
-include old_compile.mk

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

ifneq ($(COMPILE.c),$(OLD_COMPILE.c))
FORCE := force
endif

lib.o: lib.c $(FORCE)
    $(COMPILE.c) $< -o $@
    echo 'OLD_COMPILE.c := $(COMPILE.c)' > old_compile.mk

.PHONY: foo bar all force

foo bar all: lib.o

Демо:

$ make foo
cc -DFOO   -c lib.c -o lib.o
echo 'OLD_COMPILE.c := cc -DFOO   -c' > old_compile.mk
$ make foo
make: Nothing to be done for 'foo'.
$ make foo bar
cc -DFOO -DBAR   -c lib.c -o lib.o
echo 'OLD_COMPILE.c := cc -DFOO -DBAR   -c' > old_compile.mk
make: Nothing to be done for 'bar'.
$ make bar foo
make: Nothing to be done for 'bar'.
make: Nothing to be done for 'foo'.
...