Як отримати поточний відносний каталог вашого Makefile?


202

У мене є декілька Makefiles у конкретних каталогах додатків, таких як цей:

/project1/apps/app_typeA/Makefile
/project1/apps/app_typeB/Makefile
/project1/apps/app_typeC/Makefile

Кожен Makefile включає .inc файл у цьому шляху на один рівень вгору:

/project1/apps/app_rules.inc

Всередині app_rules.inc Я встановлюю місце призначення, де я хочу розміщувати двійкові файли під час створення. Я хочу, щоб усі двійкові файли були на своєму app_typeшляху:

/project1/bin/app_typeA/

Я спробував використовувати$(CURDIR) , як це:

OUTPUT_PATH = /project1/bin/$(CURDIR)

але замість цього я поховав двійкові файли у всій назві шляху так: (зауважте надмірність)

/project1/bin/projects/users/bob/project1/apps/app_typeA

Що я можу зробити, щоб отримати "поточний каталог" виконання, щоб я міг знати лише те app_typeX, щоб помістити бінарні файли у відповідну папку типів?

Відповіді:


271

Функція оболонки.

Ви можете використовувати shellфункцію: current_dir = $(shell pwd). Або shellв поєднанні з notdir, якщо вам не потрібно абсолютний шлях: current_dir = $(notdir $(shell pwd)).

Оновлення.

Дане рішення працює лише під час запуску makeіз поточного каталогу Makefile.
Як зазначав @Flimm:

Зауважте, що це повертає поточний робочий каталог, а не батьківський каталог Makefile.
Наприклад, якщо ви запустите cd /; make -f /home/username/project/Makefile, current_dirзмінна буде /, а не /home/username/project/.

Нижче наведений код буде працювати для Makefiles, які викликаються з будь-якого каталогу:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))

5
Це не працює. Це та сама проблема. pwd друкує повну назву шляху, я просто хочу назвати фактичну поточну папку, в якій я перебуваю.
boltup_im_coding

6
Потрібно негайно розширити значення, щоб використовувати оператор: =. Як і в mkfile_path: = і current_dir = (інакше значення правого боку можуть бути оцінені пізніше, коли MAKEFILE_LIST змінився після включення інших Makefiles
Том Грюнер,

10
Я знаю, що це давнє запитання, але я просто наткнувся і подумав, що це може бути корисним. Це також не буде працювати, коли у відносному шляху макіяжу є пробіл. В Зокрема, $(lastword "$(MAKEFILE_LIST)")з Makefile на шляху "Sub Directory/Makefile"дасть вам "Directory/Makefile". Я не думаю, що існує спосіб обходу, оскільки $(MAKEFILE_LIST)з двома файлами ви отримуєте єдину струну "Makefile One Makefile Two, яка не може обробляти пробіли
mrosales

2
Це current_dirне працює для мене. $(PWD)робить, і це набагато простіше.
CaTx

3
"solution only works when you are running make from the Makefile's current directory"це м'який спосіб сказати "не дуже працює". Я б поставив останню частину у верхній частині відповіді.
Віктор Сергієнко

138

Як взято звідси ;

ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

Показує як;

$ cd /home/user/

$ make -f test/Makefile 
/home/user/test

$ cd test; make Makefile 
/home/user/test

Сподіваюсь, це допомагає


3
IMHO, краще використовувати внутрішню функцію make, dirзамість того, shell dirnameщо вона отримує ім'я тракту з /символом, що триває .
Серж Руссак

3
через зменшення залежності від зовнішніх інструментів. Тобто, якщо в якійсь системі буде псевдонім команди
dirname

6
Це майже працювало для мене, але DIR мав провідний пробіл, тому незначна зміна зробила свою роботу чудово:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz

9
Для посилання на поточний makefile використання використання $MAKEFILE_LISTповинно передувати будь-яким includeопераціям ( документам ).
nobar

2
Слід використовувати лапки - принаймні навколо аргументу до dirname- у випадку, якщо на шляху є пробіли.
nobar

46

Якщо ви використовуєте GNU make, $ (CURDIR) - це фактично вбудована змінна. Це місце, де Makefile знаходиться в поточному робочому каталозі, імовірно, там, де Makefile, але не завжди .

OUTPUT_PATH = /project1/bin/$(notdir $(CURDIR))

Див Додаток Короткий довідник на http://www.gnu.org/software/make/manual/make.html


18
З посібника з виготовлення GNU (стор. 51): "коли GNU make start запускається (після того, як він обробив будь-які параметри -C), він встановлює змінну CURDIR на ім'я шляху поточного робочого каталогу." Не місце розташування Makefile - хоча вони можуть бути однаковими.
Simon Peverett

4
Відмовлено від того, що відповідь технічно не є правильною, як зазначив Саймон Певерет у попередньому коментарі.
Subfuzion

5
@SimonPeverett: Вираз у дужках означає "поточний робочий каталог ПІСЛЯ -CC застосовано". А саме $ (CURDIR) дає абсолютно однакові результати для "make -C xxx" та "cd xxx; make". Крім того, це знімає сліди /. Я намагався пов'язати з gmakev3.81. Але ви праві, якщо вас makeназивають як make -f xxx/Makefile.
TrueY

CURDIRпрацює для мене там, де PWDцього не зробив. Я mkdir -p a/s/dтоді в кожній папці мав файл, який друкував PWDі CURDIRраніше +$(MAKE) <subdir>.
Марк К Коуан

27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))

12
... якщо у вас немає пробілів у будь-яких контурах.
Крейг Рінгер

9
... якщо ви не включили в попередній рядок якийсь інший makefile
robal

1
@robal Yep З цим я щойно стикався. Це чудово спрацьовує, якщо у вас є лише два Makefiles (основний плюс включений).
sherrellbc

6

Я перепробував багато з цих відповідей, але в моїй системі AIX з gnu make 3,80 мені потрібно було зробити деякі речі старої школи.

Виявляється lastword,abspath і realpathне було додано до 3,81. :(

mkfile_path := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
mkfile_dir:=$(shell cd $(shell dirname $(mkfile_path)); pwd)
current_dir:=$(notdir $(mkfile_dir))

Як говорили інші, вона не найелегантніша, оскільки двічі викликає оболонку, і у неї все ще є проблеми з пробілами.

Але оскільки у мене немає пробілів на моїх шляхах, це працює для мене незалежно від того, як я почав make :

  • make -f ../wherever/makefile
  • зробити -C ../ скрізь
  • make -C ~ / куди завгодно
  • кд ../ скрізь; зробити

Всі дають мені whereverза current_dirі абсолютний шлях до whereverдля mkfile_dir.


Я, наприклад, без проблем склав gnu-make-3.82 на aix (5-6-7).
Zsigmond Lőrinczy

Так, на 3.81 було реалізовано необхідні функції для попередніх рішень. Моя відповідь стосується старих систем з 3,80 або новіших версій. На жаль, на роботі мені не дозволяється додавати або модернізувати інструменти до системи AIX. :(
Джессі Чисгольм

5

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

/tmp/makefile_path_test.sh

#!/bin/bash -eu

# Create a testing dir
temp_dir=/tmp/makefile_path_test
proj_dir=$temp_dir/dir1/dir2/dir3
mkdir -p $proj_dir

# Create the Makefile in $proj_dir
# (Because of this, $proj_dir is what $(path) should evaluate to.)
cat > $proj_dir/Makefile <<'EOF'
path := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
cwd  := $(shell pwd)

all:
    @echo "MAKEFILE_LIST: $(MAKEFILE_LIST)"
    @echo "         path: $(path)"
    @echo "          cwd: $(cwd)"
    @echo ""
EOF

# See/debug each command
set -x

# Test using the Makefile in the current directory
cd $proj_dir
make

# Test passing a Makefile
cd $temp_dir
make -f $proj_dir/Makefile

# Cleanup
rm -rf $temp_dir

Вихід:

+ cd /tmp/makefile_path_test/dir1/dir2/dir3
+ make
MAKEFILE_LIST:  Makefile
         path: /private/tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test/dir1/dir2/dir3

+ cd /tmp/makefile_path_test
+ make -f /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
MAKEFILE_LIST:  /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
         path: /tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test

+ rm -rf /tmp/makefile_path_test

ПРИМІТКА. Ця функція $(patsubst %/,%,[path/goes/here/])використовується для зняття косої коси.


2

Ось один лайнер, щоб отримати абсолютний шлях до Makefileфайлу за допомогою синтаксису оболонки:

SHELL := /bin/bash
CWD := $(shell cd -P -- '$(shell dirname -- "$0")' && pwd -P)

А ось версія без оболонки на основі @ 0xff відповіді :

CWD := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))

Перевірте його, роздрукувавши, як-от:

cwd:
        @echo $(CWD)

1
Критичне значення! Другий вище приклад потребує: = оператора присвоєння або ще дуже дивні і гнівні речі можуть трапитися зі складними файлами (повірте мені)
EMon

1
По-перше, це повторна помилка і не працює для складання дерев.
Йохан Буле

2

Тут знайдено рішення: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

Рішення : $ (CURDIR)

Ви можете використовувати його так:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder

3
Ласкаво просимо до переповнення стека. Чи можете ви додати більше деталей до своєї відповіді? У питанні йдеться про те, що $(CURDIR)вже судили безрезультатно.
Шон

4
ПОПЕРЕДЖЕННЯ, швидкі googlers: Це не каталог, де є файл makefile, а поточний робочий каталог (куди makeбуло запущено). Дійсно, чого дійсно хотіла ОП, але назва питання є хибною, тому саме питання є непослідовним. Отже, це правильна відповідь на неправильне запитання. ;)
Sz.


0

Наскільки мені відомо, це єдина відповідь, яка коректно працює з пробілами:

space:= 
space+=

CURRENT_PATH := $(subst $(lastword $(notdir $(MAKEFILE_LIST))),,$(subst $(space),\$(space),$(shell realpath '$(strip $(MAKEFILE_LIST))')))

Це в основному працює витікання космічних символів, підставляючи ' 'для '\ 'яких дозволяє Зробити правильно розібрати його, а потім видаляє ім'я файлу Makefile в MAKEFILE_LISTроблячи іншу заміну , так що ви залишили з каталогом , який Makefile знаходиться. Не зовсім найкомпактніший річ у світі, але вона працює.

Ви закінчите щось подібне, де пробіли всі пробіли:

$(info CURRENT_PATH = $(CURRENT_PATH))

CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/

-2

Приклад для довідки, як показано нижче:

Структура папки може бути такою:

введіть тут опис зображення

Там, де є два Makefiles, кожен як нижче;

sample/Makefile
test/Makefile

Тепер давайте подивимось на зміст Makefiles.

зразок / Makefile

export ROOT_DIR=${PWD}

all:
    echo ${ROOT_DIR}
    $(MAKE) -C test

тест / Makefile

all:
    echo ${ROOT_DIR}
    echo "make test ends here !"

Тепер виконайте зразок / Makefile, як;

cd sample
make

ВИХІД:

echo /home/symphony/sample
/home/symphony/sample
make -C test
make[1]: Entering directory `/home/symphony/sample/test'
echo /home/symphony/sample
/home/symphony/sample
echo "make test ends here !"
make test ends here !
make[1]: Leaving directory `/home/symphony/sample/test'

Поясненням може бути те, що батьківський / домашній каталог може зберігатися у середовищі-прапорі та може бути експортований, щоб його можна було використовувати у всіх файлах підкаталогів.


2
Це отримує поточний робочий каталог, а не домашній каталог makefile.
Джессі Чизгольм

Це рядки в Makefile. Тепер, коли команда Makefile виконує, це отримає шлях, по якому знаходиться Makefile. Я розумію, що "домашній каталог makefile" посилається на те саме, тобто шлях до каталогу, в якому виконується Makefile.
парашутувати

@Jesse Chisholm, будь ласка, перегляньте знову. Я пояснив із використанням.
парашутувати

Повторна помилка: вона не працює для складання дерев. Крім того, ваш термінал gnome пропонує простий спосіб копіювання тексту: просто виберіть його. По чутках, Імгур є розгалуженим.
Йоган Буле

-2

оновлення 2018/03/05 остаточно я використовую це:


shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

Він може бути виконаний правильним щодо відносного шляху або абсолютного шляху. Виконано правильно, на яке посилається crontab. Виконано правильно в іншій оболонці.

показати приклад, a.sh друкувати самостійний шлях.

[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/test/a.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

echo $shellPath
[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/b.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

$shellPath/test/a.sh
[root@izbp1a7wyzv7b5hitowq2yz /]# ~/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# cd ~
[root@izbp1a7wyzv7b5hitowq2yz ~]# ./b.sh
/root/./test
[root@izbp1a7wyzv7b5hitowq2yz ~]# test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz ~]# cd test
[root@izbp1a7wyzv7b5hitowq2yz test]# ./a.sh
/root/test/.
[root@izbp1a7wyzv7b5hitowq2yz test]# cd /
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# 

старий: Я використовую це:

MAKEFILE_PATH := $(PWD)/$({0%/*})

Він може показувати правильність, якщо він виконується в іншій оболонці та іншому каталозі.


1
"Це може відображатись правильно, якщо виконується, якщо інша оболонка та інший каталог" не мають сенсу англійською мовою. Ви можете спробувати пояснити ще раз?
Кіт М

вибачте за мою бідну англійську
zhukunqian

Дякую за редагування, я бачу, що ви мали на увазі зараз. Чи можете ви пояснити, що це робить, перш ніж спробувати? Я не впевнений, як ним користуватися. Як і що ви маєте на увазі під «іншою оболонкою»
Кіт М

1
У цій відповіді стільки неправильних речей, що їх неможливо виправити. Я пропоную просте видалення.
Йохан Буле

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.