Как сгенерировать правило Makefile

Я хочу создать правила в Makefile следующим образом:

# $(call cc-defs, ccfiles)
define cc-defs
    $1.files = $(patsubst %.cc,%.proto,$1)
    $1: $1.files
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs, $(ccfile))))

но не удалось с сообщением об ошибке:

Makefile:19: *** commands commence before first target.  Stop.

Вместо этого я могу сделать это:

# $(call cc-defs, ccfiles)
define cc-defs
    $1.files = $(patsubst %.cc,%.proto,$1)
endef

$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs, $(ccfile))))
$(foreach ccfile,$(ccfiles), $(eval $(ccfile):$($(ccfile).files)))

Как заставить работать 1-й способ?


person Huang F. Lei    schedule 09.03.2011    source источник


Ответы (2)


Какую версию make вы используете? $(eval) появился только в 3.80 (и корректно работает только в 3.81 ИМХО).

Для отладки make-файлов часто приходится возвращаться к отладке printf. Чтобы увидеть, что происходит, замените eval на warning. Это показывает, что вы даете, чтобы сделать:

$ make --warn
Makefile:6: warning: undefined variable `ccfiles'
make: *** No targets.  Stop.

(Кроме того, --warn-undefined-variables всегда полезно. Неопределенные переменные неаккуратны.)

Итак, нам нужно определить $ccfiles. Теперь мы получаем запуск цикла for:

$ make --warn ccfiles=1.cc
Makefile:6:    1.c.files = 1.cc
               1.cc:  1.c.files
make: *** No targets.  Stop.

Отлично. Вы не указали ни рецептов, ни цели по умолчанию. Вы также пропустили некоторое раскрытие переменной и имеете лишний пробел в вызове $(for) (непослушно!). Попробуй это:

$ cat Makefile
# $(call cc-defs,ccfiles)
define cc-defs
  $1.files = $(patsubst %.cc,%.proto,$1)
  $1: $$($1.files) ; echo '[$$@]'
endef
$(foreach ccfile,$(ccfiles), $(eval $(call cc-defs,$(ccfile))))

$ make ccfiles=1.cc
make: *** No rule to make target `1.proto', needed by `1.cc'.  Stop.
person bobbogo    schedule 09.03.2011

Обратите внимание: если все, что вы хотите сделать, это чтобы все файлы в переменной зависели от файлов .proto (или были созданы из них), вам не нужен $(eval).

Подойдет шаблонное правило (и оно будет работать и в старых версиях GNU Make):

$(ccfiles): %.cc: %.proto
        echo '[$@]'

Это имеет побочный эффект жалобы, когда переменная ccfiles содержит какие-либо записи, не названные *.cc (хотя в этом случае оно все еще выполняет правило).

$ make ccfiles=hello.cc
make: *** No rule to make target `hello.proto', needed by `hello.cc'.  Stop.
$ touch hello.proto
$ make ccfiles=hello.cc
[hello.cc]
$ make ccfiles=hello.c
Makefile:1: target `hello.c' doesn't match the target pattern
[hello.c]

Если переменная может содержать много вещей, но вы хотите добавить эту обработку только к файлам .cc, просто добавьте фильтр:

$(filter %.cc,$(ccfiles)): %.cc: %.proto
        echo '[$@]'

Затем это приводит к:

$ make ccfiles=hello.cc
[hello.cc]
$ make ccfiles=hello.c
make: *** No targets.  Stop.
person Zastai    schedule 14.05.2014