Как мне написать команду 'cd' в make-файле?

354

Например, у меня есть что-то вроде этого в моем make-файле:

all:
     cd some_directory

Но когда я печатал, makeя видел только «cd some_directory», как в echoкоманде.

Давид Сирадегян
источник
5
Неясно, что вы хотите сделать, но, по моему опыту make, я никогда не хотел менять каталог таким образом. Может быть, вам стоит попробовать другой подход к вашему решению?
П Швед
2
Это распространенная ошибка новичка полагать, что ваш каталог важен. Для большинства вещей это не так; cd dir; cmd fileпочти всегда может быть более полезно выражено как cmd dir/file.
tripleee
34
Распространенная ошибка новичка полагать, что ваш нынешний рабочий каталог несущественен. Многие программы, особенно сценарии оболочки, написаны с особым .вниманием. Это правда, что большинство инструментов разработаны таким образом, что вам не нужно менять свой pwd для него. Но это не всегда так, и я не считаю хорошей идеей называть это «ошибкой», полагая, что ваш каталог может быть важным.
Эвелин Кокемур
Подсказка: если ваша команда кд говорит «Нет такого файла или каталога», даже когда (относительно) каталога делает существует, проверьте , что ваша переменная среды CDPATH либо пусто , либо включает в себя «». Make выполняет команды с "sh", который только находит относительный путь через CDPATH, если он установлен. Это контрастирует с Bash, который будет пытаться. перед консультацией CDPATH.
Денис Хоу

Ответы:

621

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

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

Например:

all:
        cd some_dir; echo "I'm in some_dir"; \
          gcc -Wall -o myTest myTest.c

Также обратите внимание, что точка с запятой необходима между каждой командой, даже если вы добавляете обратную косую черту и символ новой строки. Это связано с тем, что вся строка интерпретируется оболочкой как одна строка. Как отмечено в комментариях, вы должны использовать '&&' для объединения команд, что означает, что они выполняются только в том случае, если предыдущая команда была успешной.

all:
        cd some_dir && echo "I'm in some_dir" && \
          gcc -Wall -o myTest myTest.c

Это особенно важно при выполнении разрушительной работы, такой как очистка, так как в противном случае вы уничтожите неправильные вещи, если cdпо какой-либо причине произойдет сбой.

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

all:
        $(MAKE) -C some_dir all

который изменится some_dirи выполнит Makefileтам с целью "все". Рекомендуется использовать $(MAKE)вместо makeнепосредственного вызова , так как он позаботится о том, чтобы вызвать правильный экземпляр make (если вы, например, используете специальную версию make для вашей среды сборки), а также обеспечить несколько другое поведение при запуске используя определенные переключатели, такие как -t.

Для записи make всегда повторяет команду, которую она выполняет (если она явно не подавлена), даже если у нее нет вывода, что вы и видите.

falstro
источник
8
Ну не всегда . Чтобы подавить эхо, просто поместите @ в начале строки.
бета,
2
@Beta: хорошо, да, и префикс тире также игнорирует состояние ошибки. Возможно, я немного увлекся, я хотел бы отметить тот факт, что make делает эхо команды независимо от того, какая это команда. И в этом случае, это команда без вывода, что делает эхо еще более странным для тех, кто не знаком с make.
Фальстро
46
Две части: 1. Команды действительно должны быть объединены &&, потому что ;если каталог не существует и происходит cdсбой, оболочка продолжит выполнение остальных команд в текущем каталоге, что может вызвать такие вещи, как таинственный «файл не найденные »сообщения для компиляций, бесконечные циклы при вызове make или аварии для правил вроде clean:: cd dir; rm -rf *. 2. При вызове подмоделей $(MAKE)вместо вызова вызывайте параметры,make чтобы параметры передавались правильно .
andrewdotn
2
@perreal, я обычно определяю шаблонное правило следующим образом: %-recursive:с телом: @T="$@";$(MAKE) -C some_dir $${T%-*}(у меня обычно тоже есть цикл for, чтобы перебрать список подкаталогов, $${T%-*}расширение bash, которое удаляет -recursiveчасть имени цели), а затем определить явную стенографию (и .PHONY) цели для каждого из них, как all: all-recursive, check: check-recursive, clean: clean-recursive.
Фальстро
1
@ChristianStewart, правда, как было упомянуто в комментариях 2 и 3.
Фальстро
95

Начиная с GNU make 3.82 (июль 2010 г.), вы можете использовать .ONESHELLспециальную цель для запуска всех строк рецепта в одном экземпляре оболочки (выделено жирным шрифтом):

  • Новая специальная цель: .ONESHELLинструктирует make вызывать отдельный экземпляр оболочки и предоставлять ему весь рецепт , независимо от того, сколько строк оно содержит. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded
Chnossos
источник
6
Просто отметьте, что pwdон работает так же, как и `pwd`(с обратными галочками), но $(shell pwd)и $(PWD)все равно вернет каталог, прежде чем выполнять cdкоманду, поэтому вы не можете использовать их напрямую.
Anol
3
Да, потому что расширение переменных и функций выполняется перед выполнением команд командой make, тогда как pwdи `pwd`выполняется самой оболочкой.
Chnossos
2
Не все работают с устаревшими make-файлами, и даже тогда этот ответ о том, что такая возможность существует.
Хносс
1
Это может быть раздражающим / опасным вариантом, потому что только последняя команда для цели может вызвать сбой (любые более ранние ошибки команды будут игнорироваться), и, вероятно, никто, работающий с вами, не будет ожидать этого.
Tobii
1
Установите SHELLFLAGSзначение -e -cи оболочка завершит работу при первой неудачной команде.
Тимоти Болдуин
21

Вот милая хитрость, чтобы справиться с каталогами и сделать. Вместо использования многострочных строк или «cd;» в каждой команде определите простую функцию chdir следующим образом:

CHDIR_SHELL := $(SHELL)
define chdir
   $(eval _D=$(firstword $(1) $(@D)))
   $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef

Тогда все, что вам нужно сделать, это назвать это в вашем правиле так:

all:
          $(call chdir,some_dir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

Вы даже можете сделать следующее:

some_dir/myTest:
          $(call chdir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c
Джос
источник
41
"Милый"? Больше похоже на достаточно веревки, чтобы выстрелить себе в ногу.
tripleee
1
Затем устанавливается этот текущий каталог для команд только в этом правиле или для всех впоследствии выполняемых правил? Кроме того, будут ли некоторые варианты этой работы под Windows?
user117529
8
Это, конечно, прерывает параллельное выполнение ( -jn), что и есть цель make .
Боббого
5
Это плохой взлом. Если вам когда-либо приходится прибегать к таким вещам, вы не используете Makefiles для того, для чего они предназначены.
gatopeich
4
Я не согласен, что это плохой взлом, это точно. Но демонстрирует некоторые из злых вещей, которые вы можете сделать.
JoeS
9

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

С GNU make вы можете сделать что-то вроде:

BIN=/bin
foo:
    $(shell cd $(BIN); ls)
Надир СУАЛЕМ
источник
5
Почему $(shell ...)когда cd $(BIN); lsили cd $(BIN) && ls(как указал @andrewdotn) будет достаточно.
vapace
1

Поменять реж

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir
jackotonye
источник
-6

Нравится:

target:
    $(shell cd ....); \
    # ... commands execution in this directory
    # ... no need to go back (using "cd -" or so)
    # ... next target will be automatically in prev dir

Удачи!

Кубик Рубика
источник
1
Нет, это неправильно В частности, $(shell cd ....)он выполняется при первоначальном разборе файла Makefile, а не при запуске этого конкретного рецепта.
tripleee
@triplee Не совсем - $(shell)расширяется только тогда, когда make решает собрать цель . Если марка не нуждается рецепт, он не будет расширять его.
Боббого