Як записати команду 'cd' у makefile?


354

Наприклад, я маю щось подібне у своєму makefile:

all:
     cd some_directory

Але коли я вводив, makeя бачив лише "cd some_directory", як у echoкоманді.


5
Незрозуміло, що ви хочете зробити, але, на моєму досвіді make, я ніколи не хотів змінювати каталог, як цей. Можливо, вам варто спробувати інший підхід до свого рішення?
П Швед

2
Поширена помилка новачка вважати, що ваш каталог важливий. Для більшості речей це не так; cd dir; cmd fileмайже завжди може бути більш корисно виражений як cmd dir/file.
трійка

34
Поширена помилка для новачків вважати, що ваш теперішній робочий каталог є несуттєвим. Багато програм, особливо сценарії оболонок, написані з певним значенням .. Це правда, що більшість інструментів розроблені таким чином, що вам не потрібно міняти свій pwd для цього. Але це не завжди так, і я не думаю, що вважати, що ваш каталог може бути важливим, це не гарна ідея називати це "помилкою".
Евелін Кокемур

Порада: якщо у вашій команді cd написано "Немає такого файлу чи каталогу", навіть коли (відносний) каталог не існує, перевірте, чи змінна середовища CDPATH порожня або містить ".". Створіть команди команд "sh", які знайдуть відносний шлях тільки через CDPATH, якщо він встановлений. Це контрастує з bash, який спробуємо. перед консультацією CDPATH.
Деніс Хоу

Відповіді:


620

Це фактично виконання команди, зміна каталогу на 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будь-якої причини вийде з ладу.

Хоча загальне використання - це зробити дзвінок в підкаталозі, в який ви можете заглянути. Для цього є варіант командного рядка, тому вам не потрібно називати cdсебе, тому ваше правило виглядатиме так

all:
        $(MAKE) -C some_dir all

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

Для запису завжди повторюйте команду, яку вона виконує (якщо явно не придушено), навіть якщо вона не має виводу, що саме ви бачите.


8
Ну, не завжди . Щоб придушити відлуння, просто поставте @ на початку рядка.
Бета

2
@ Beta: добре, так, і префікс тире також ігнорує стан помилки. Можливо, я трохи захопився, я хотів би зазначити той факт, що make робить відлуння команди, незалежно від того, що це за команда. І в цьому випадку це команда без виводу, яка робить ехолонг здаватися ще більш чужим для того, хто не знайомий з make.
falstro

46
Два гниди: 1. До команд слід дійсно приєднатися &&, тому що ;якщо каталог не існує і cdне виходить, оболонка буде продовжувати виконувати решту команд у поточному каталозі, що може спричинити такі речі, як таємничий „файл не знайдено »повідомлення для компіляцій, нескінченних циклів при виклику make або катастрофи для таких правил clean:: cd dir; rm -rf *. 2. Коли ви викликаєте субпродукти, дзвоніть $(MAKE)замість того, makeщоб параметри передавались правильно .
andrewdotn

2
@perreal, я зазвичай визначаю правило шаблону так: %-recursive:з body: @T="$@";$(MAKE) -C some_dir $${T%-*}(зазвичай у мене також є for-loop, щоб перейти до списку підкаталогів,$${T%-*} це розширення bash, яке видаляє -recursiveчастину назви цілі), а потім визначити явну стенографію (і .PHONY) цілі для кожного з них, як all: all-recursive, check: check-recursive, clean: clean-recursive.
falstro

1
@ChristianStewart, правда, про що було сказано в коментарях 2 та 3.
falstro

94

Починаючи з GNU make 3.82 (липень 2010 року), ви можете використовувати .ONESHELLспеціальну ціль, щоб запустити всі рядки рецептів в одному екземплярі оболонки (жирний наголос міни):

  • Нова спеціальна мета: .ONESHELLвказує зробити виклик одного екземпляра оболонки та надати їй весь рецепт , незалежно від того, скільки рядків він містить. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded

6
Відразу відзначимо , що pwdсаме по собі працює, а також `pwd`(з зворотні лапки), але $(shell pwd)і по- $(PWD)як і раніше буде повертати каталог перед виконанням cdкоманди, так що ви не можете використовувати їх безпосередньо.
анол

3
Так, тому що розширення змінної та функцій виконується перед виконанням команд make, тоді як pwdі `pwd`виконується самою оболонкою.
Chnossos

2
Не всі працюють над застарілими файлами, і навіть тоді ця відповідь полягає у знанні такої можливості.
Chnossos

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
"Симпатичний"? Більше, як достатньо мотузки, щоб стріляти собі в ногу.
трійка

1
Чи встановлений цей поточний каталог для команд саме в цьому правилі, або для всіх згодом виконаних правил? Також, чи зміниться ця робота в Windows?
користувач117529

8
Це, звичайно , перерви для паралельного виконання ( -jn), що вся суть марки дійсно.
bobbogo

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


-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 спочатку розбирається, а не тоді, коли виконується цей конкретний рецепт.
трійчатка

@triplee Не зовсім - $(shell)розгортається лише тоді, коли make вирішує побудувати ціль . Якщо марка не потребує рецепт, він не буде розширювати його.
Боббого
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.