2017-01-01 1 views
1

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

фон

Для дальнейшего чтения, а не мне воспроизвести слишком много существующего материала, см this earlier question, который покрывает землю довольно хорошо, и ранее помогли мне при создании этой системы.

Для создания системы, которую я создаю, я хочу определить зависимости между компонентами системы - например. компонент A зависит от компонента B - и затем оставить систему make, чтобы гарантировать, что любые продукты процесса сборки B будут построены до того, как они понадобятся для выполнения шагов сборки для A. Это немного расточительно из-за зернистости (могут быть созданы некоторые ненужные промежуточные продукты), но для моего случая использования он удовлетворяет удобной балансовой точке между простотой использования и производительностью.

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

Чтобы разрешить общие шаблоны применяться ко всем определяющим компонент файлам, каждый компонент использует такие переменные, как: $(component_name)_SRC. Это общее решение для нерекурсивных (но рекурсивно включенных) систем make.

Для получения информации о различных видах переменных GNU make см. the manual. В итоге: просто расширенные переменные (SEV) расширяются по мере чтения make-файла, демонстрируя поведение, подобное языку обязательного программирования; рекурсивно расширенные переменные (REV) расширяются во второй фазе make, после того, как все make-файлы были прочитаны.

Проблема

Конкретная проблема возникает при попытке превратить список зависел навесные детали в список файлов, эти компоненты представляют.

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

rules.mk:

$(c)_src    := $(src) 
$(c)_dependencies  := $(dependencies) 

### This is the interesting line: 
$(c)_dependencies_src := $(foreach dep, $($(c)_dependencies), $($(dep)_src)) 

$(c) : $($(c)_src) $($(c)_dependencies_src) 
     @echo $^ 

Makefile:

.PHONY: foo_a.txt foo_b.txt bar_a.txt hoge_a.txt 

### Bar 
c   := bar 
src   := bar_a.txt 
dependencies := 

include rules.mk 

### Foo 
c   := foo 
src   := foo_a.txt foo_b.txt 
dependencies := bar hoge 

include rules.mk 

### Hoge 
c   := hoge 
src   := hoge_a.txt 
dependencies := bar 

include rules.mk 

Они будут работать, чтобы дать:

$ make foo 
foo_a.txt foo_b.txt bar_a.txt 
$ 

hoge_a.txt не включаются в выходной, потому что на время, которое foo_dependencies определяется как SEV, hoge_src еще не существует.

Расширение после того, как все профайлы были прочитаны, проблема должна быть в состоянии решить, и я попытался определить $(c)_dependencies_src как REV, но это не сработает, потому что $(c) затем расширяется при времени замены, а не в определении время, поэтому оно больше не содержит правильное значение.

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

Я хотел бы знать:

  1. Есть ли решение этой конкретной проблемы? (т. е. есть ли простой способ сделать эту линию достижением того, что я хочу?)
  2. Существует ли более типичный способ создания такой системы? (т. е. один экземпляр make, загружая компоненты из нескольких make-файлов и определяя зависимости между этими компонентами.)
  3. Если существует несколько решений, каковы компромиссы между ними?

Последний комментарий: Поскольку я написал свой вопрос, я начал понимать, что может быть возможно решение с использованием eval для построения определения REV, однако, поскольку я не мог найти эту проблему в любом месте еще на SO Я думал, что стоит задать вопрос в любом случае ради будущих искателей, плюс я хотел бы услышать мысли более опытных пользователей об этом или любых других подходах.

+0

Почему вы используете ': =' вместо просто '=' при установке '$ (c) _dependencies_src'? Если вы использовали '=' здесь, это решило бы вашу проблему. – MadScientist

+0

Лично я никогда бы не создал систему, в которой использовались «глобальные» переменные, такие как присвоение 'c: = foo', включая файлы правил, а затем назначение' c: = bar' и включая файлы правил. Я бы сделал это следующим образом: 'target + = foo', затем' foo_c: = foo', 'foo_src: = foo.x' и т. Д., Тогда' target + = bar', 'bar_c: = bar',' bar_src : = bar.x' и т. д. Это позволяет избежать наличия одинаковых имен переменных и обеспечения их использования до их сброса. – MadScientist

+0

Вы пробовали это с приведенным мной примером? Это, по крайней мере, не работает для меня, как я сказал: «... REVs должны быть в состоянии решить, и я ранее пытался определить $ (c) _dependencies_src как REV ...» –

ответ

0

Короткий ответ: нет хорошего решения вопроса, который вы задаете. Невозможно остановить расширение переменной частичного пути и отложить ее до конца. Не только это, но и потому, что вы используете переменную в списке предварительных условий, даже если вы можете получить значение переменной $(c)_dependencies_src, чтобы содержать только нужные вам ссылки, в самой следующей строке они были бы полностью расширены как часть списка предварительных условий так что это ничего не принесет вам.

Есть только один способ отложить расширение необходимых компонентов и использовать функцию secondary expansion. Вам нужно будет сделать что-то вроде:

$(c)_src    := $(src) 
$(c)_dependencies  := $(dependencies) 

.SECONDEXPANSION 
$(c) : $($(c)_src) $$(foreach dep, $$([email protected]_dependencies), $$($$(dep)_src)) 
     @echo $^ 

(непроверено). Эта проблема устраняет проблему с $(c)_dependencies_src, просто не определяя ее вообще и не помещая ее в список предварительных условий непосредственно, а как вторичное расширение.

Как я уже писал в своем комментарии выше, я лично не создавал бы систему, которая бы так работала. Я предпочитаю иметь системы, в которых все переменные создаются вверх с использованием пространства имен (обычно добавляя целевое имя), а затем в конце, после того, как все переменные определены, включая один «rules.mk» или что-то, что будет использоваться все эти переменные, чтобы построить правила, скорее всего (если все ваши рецепты очень просты), используя eval.

Так, что-то вроде:

targets := 

### Bar 
targets += bar 
bar_c   := bar 
bar_src   := bar_a.txt 
bar_dependencies := 

### Foo 
targets += foo 
foo_c   := foo 
foo_src   := foo_a.txt foo_b.txt 
foo_dependencies := bar hoge 

### Hoge 
targets += hoge 
hoge_c   := hoge 
hoge_src   := hoge_a.txt 
hoge_dependencies := bar 

# Now build all the rules 
include rules.mk 

И тогда в rules.mk вы увидите что-то вроде:

define make_c 
$1 : $($1_src) $(foreach dep, $($1_dependencies), $($(dep)_src)) 
     @echo $$^ 
endif 

$(foreach T,$(targets),$(eval $(call make_c,$T))) 

Вы можете даже избавиться от настроек для target, если вы внимательны о вашей переменной имена, добавив что-то вроде этого в rules.mk:

targets := $(patsubst %_c,%,$(filter %_c,$(.VARIABLES))) 

Чтобы разрешить использование одних и тех же компонентов в разных целях, вам просто нужно добавить больше пространства имен, чтобы различать два разных компонента.

+0

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

Смежные вопросы