сделать установку, только если файл изменился

При написании хорошо продуманного Makefile, который сохраняет правильные зависимости и выполняет только необходимый минимум, если файл был изменен, кажется, что цель install: часто упускается из виду. Чаще всего не цель установки выглядит примерно так:

TRG := /trg
BIN_TRG := $(TRG)/bin
ETC_TRG := $(TRG)/etc
BIN_FILES := b1 b2 b3
ETC_FILES := e1 e2 e3

install:
    install $(BIN_FILES) $(BIN_TRG)
    install $(ETC_FILES) $(ETC_TRG)

.PHONY: install

То есть фальшивая цель без какой-либо проверки зависимостей.

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

Возникает вопрос: Как лучше всего настроить правило установки, которое отслеживает зависимости?


person Chen Levy    schedule 24.07.2010    source источник


Ответы (3)


Я не знаком с вашей функцией «установить», и ваши два решения используют ее по-разному, но как насчет этого:

TRG := /trg
BIN_TRG := $(TRG)/bin
ETC_TRG := $(TRG)/etc
BIN_FILES := b1 b2 b3
ETC_FILES := e1 e2 e3
INSTALLS := $(addprefix $(BIN_TRG)/,$(BIN_FILES)) \
        $(addprefix $(ETC_TRG)/,$(ETC_FILES))

install: $(INSTALLS)

$(BIN_TRG)/% $(ETC_TRG)/%: %
    install $< $@

.PHONY: install

РЕДАКТИРОВАНИЕ: P.S. если вы хотите, чтобы шаг install заполнил промежуточную область или что-то еще, сделайте промежуточную область отдельной целью с собственным правилом.

person Beta    schedule 24.07.2010

Один подход, который я нашел работающим, заключается в специальном описании зависимостей правила установки. Для каждого файла нам нужно построить правило, такое как:

trg_dir/my_file: my_file
    install $< $@

Однако для этого требуется некоторая магия gmake:

TRG := /trg
BIN_TRG := $(TRG)/bin
ETC_TRG := $(TRG)/etc
BIN_FILES := b1 b2 b3
ETC_FILES := e1 e2 e3
INSTALLS := $(addprefix $(BIN_TRG)/,$(BIN_FILES)) \
            $(addprefix $(ETC_TRG)/,$(ETC_FILES))

install: $(INSTALLS)

define install_rule
$(1)/$(2): $(2)
    install $(2) $(1)/$(2)
endef

$(foreach file,$(BIN_FILES),$(eval $(call install_rule,$(BIN_DIR),$(file))))
$(foreach file,$(ETC_FILES),$(eval $(call install_rule,$(ETC_DIR),$(file))))

.PHONY: install

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

Цель install: остается фальшивой, но теперь она будет использоваться только для проверки актуальности отдельных правил установки.

Правила генерируются с оценкой результата вызова макроопределения отдельного правила. Мы используем функцию $(foreach ...) для перебора каждого файла и задаем правильные параметры при $(call ...)обработке файла install_rule.


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

person Chen Levy    schedule 24.07.2010

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

.PHONY: install

install: dummy

dummy: $(FILES_THAT_WERE_CHANGED)  
       install $(BIN_FILES) $(BIN_TRG)  
       install $(ETC_FILES) $(ETC_TRG)  
       touch dummy
person abirvalg    schedule 11.02.2012