GNU Make - Trailing "/" сбрасывается? - PullRequest
0 голосов
/ 10 января 2019

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

Вот минимальный пример, который демонстрирует проблему, с которой я сталкиваюсь:

.PHONY: default
default: dir/file.dat dir/other.dat

# In order to create these files, their parent directory must exist
dir/%.dat: | dir/
    touch "$@"

# To create a directory, use mkdir -p
%/:
    mkdir -p "$@"

Когда я запускаю make-файл, я получаю сообщение об ошибке, что не существует правила для создания dir. Отладочный прогон показывает, что make отбрасывает завершающий "/" из "dir /":

root@69654136a2ae:~# make -rdf dirs.mk
GNU Make 3.81
[snipped]

Considering target file `default'.
 File `default' does not exist.
  Considering target file `dir/file.dat'.
   File `dir/file.dat' does not exist.
    Considering target file `dir'.
     File `dir' does not exist.
     Looking for an implicit rule for `dir'.
     No implicit rule found for `dir'.
     Finished prerequisites of target file `dir'.
    Must remake target `dir'.
make: *** No rule to make target `dir', needed by `dir/file.dat'.  Stop.

Кстати, запрос целевого каталога в командной строке работает просто отлично:

root@69654136a2ae:~# make -rdf dirs.mk /some/dir/
GNU Make 3.81
[snipped]

Updating goal targets....
Considering target file `/some/dir/'.
 File `/some/dir/' does not exist.
 Looking for an implicit rule for `/some/dir/'.
 Trying pattern rule with stem `/some/dir'.
 Found an implicit rule for `/some/dir/'.
 Finished prerequisites of target file `/some/dir/'.
Must remake target `/some/dir/'.
mkdir -p "/some/dir/"

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

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Ваше правило %/ соответствует только целевым именам с конечным /. Так как, в зависимости от используемой вами версии марки, конечный символ / может быть удален или не задан из имен, прежде чем они будут считаться целевыми именами, это не всегда будет работать.

Для одного отдельного каталога, просто не используйте этот конечный / и не используйте шаблонное правило:

dir/%.dat: | dir
    touch "$@"

dir:
    mkdir -p "$@"

Для более общего решения, решение leiyc подойдет. Есть и другие:

  1. Если вы можете получить список всех каталогов для создания, вы можете создать их все сразу с помощью:

    DIRS := dir ...
    $(shell mkdir -p $(DIRS) > /dev/null)
    
    <any-target>: <prerequisites>
        <recipe>
    
  2. Если вы можете получить список всех каталогов для создания, но предпочитаете предварительные условия только для заказа, вы можете использовать вторичное расширение и правило для создания каталогов:

    DIRS := dir ...
    
    .SECONDEXPANSION:
    
    <any-target>: <prerequisites> | $$(@D)
        <recipe>
    
    $(DIRS):
        mkdir -p $@
    

И, возможно, есть и другие возможности ...

0 голосов
/ 10 января 2019

Да, / отбрасывается в цель dir/ (проблему можно воспроизвести в GNU Make 3.81).

Одним из альтернативных решений является использование автоматической переменной $(@D) для получения каталожной части имени файла цели с удаленной косой чертой. И сделайте каталог перед touch файлом:

.PHONY: default
default: dir/file.dat dir/other.dat

# In order to create these files, make their parent directory first
dir/%.dat:
        mkdir -p $(@D)
        touch "$@"

Тест под GNU Make 3.81 пройден:

$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
...

$ make
mkdir -p dir
touch "dir/file.dat"
mkdir -p dir
touch "dir/other.dat"
...