Как получить текущий относительный каталог вашего Makefile?

202

У меня есть несколько файлов Makefile в определенных каталогах приложений, например:

/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, чтобы поместить двоичные файлы в папку их соответствующих типов?

boltup_im_coding
источник

Ответы:

272

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

Вы можете использовать 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 изменился после включения других Make-файлов
Том Грюнер,
10
Я знаю, что это старый вопрос, но я столкнулся и подумал, что это может быть полезно. Это также не будет работать, если в относительном пути make-файла есть пробел. Конкретно, $(lastword "$(MAKEFILE_LIST)")make-файл на пути "Sub Directory/Makefile"даст вам "Directory/Makefile". Я не думаю, что есть какой-то способ обойти это, так как $(MAKEFILE_LIST)с двумя make-файлами дает вам одну строку "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

Надеюсь это поможет

sleepycal
источник
3
ИМХО, было бы лучше использовать внутреннюю make-функцию dirвместо того, shell dirnameчтобы получить путь с завершающим /символом.
Серж Руссак
3
из-за уменьшения зависимости от внешних инструментов. Т.е. что если в какой-то системе будет псевдоним команды dirname? ..
Серж Руссак,
6
Это почти сработало для меня, но у DIR были первые пробелы, поэтому небольшое изменение сделало работу отлично:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Торстен Лоренц
9
Чтобы ссылаться на текущий make-файл, использование $MAKEFILE_LISTдолжно предшествовать любым includeоперациям ( документам ).
Нобар
2
Следует использовать кавычки - по крайней мере, вокруг аргумента dirname- в случае пробелов в пути.
Нобар
46

Если вы используете GNU make, $ (CURDIR) на самом деле является встроенной переменной. Это место, где Makefile находится в текущем рабочем каталоге, где, вероятно, находится Makefile, но не всегда .

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

См. Приложение A Краткий справочник в http://www.gnu.org/software/make/manual/make.html.

Цунхсинг Дэвид Вонг
источник
18
Из руководства по GNU Make (стр. 51): «когда запускается GNU make (после обработки любых опций -C), она устанавливает переменную CURDIR в качестве пути к текущему рабочему каталогу». Не то место, где находится Makefile - хотя они могут быть одинаковыми.
Саймон Певеретт
4
Понижено, потому что ответ технически неверен, как отметил Саймон Певеретт в предыдущем комментарии.
Subfuzion
5
@SimonPeverett: Выражение в скобках означает «текущий рабочий каталог ПОСЛЕ-C применены опции». А именно $ (CURDIR) дает точно такие же результаты для "make -C xxx" и "cd xxx; make". Также это удаляет трейлинг /. Я пытался связать с gmakev3.81. Но Ты прав, если makeназывается как make -f xxx/Makefile.
Правда
CURDIRу меня работает где PWDнет. У меня mkdir -p a/s/dтогда в каждой папке был make-файл, который печатался PWDи CURDIRраньше +$(MAKE) <subdir>.
Марк К Коуэн
27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
момо
источник
12
... если у вас нет пробелов в любых путях.
Крейг Рингер
9
... если вы не включили какой-то другой make-файл в предыдущую строку
robal
1
@robal Да. Это то, с чем я только что столкнулся. Это прекрасно работает, если у вас есть только два Makefile (основной и включенный).
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 ../wherever
  • сделать -C ~ / где угодно
  • cd ../wherever; делать

Все дают мне 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/])используется для удаления косой черты.

Бруно Броноски
источник
3

Решение найдено здесь: 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
ВНИМАНИЕ, быстро-Google: это не каталог, где находится make-файл, а текущий рабочий каталог (в котором он makeбыл запущен). Это действительно то, что ОП действительно хотел, но название вопроса поддельное, поэтому сам вопрос противоречив. Таким образом, это правильный ответ на неправильный вопрос. ;)
Сз.
Это категорически неправильно и избыточный неправильный ответ
UpAndAdam
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)
kenorb
источник
1
Критическое значение! Второй пример выше нуждается в: = оператор присваивания или еще некоторые очень странные и сердитые вещи могут происходить со сложными Makefiles (поверьте мне)
EMON
1
Первый - повторяющаяся ошибка, которая не работает для сборок вне дерева.
Йохан Буле
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

Пример для вашей справки, как показано ниже:

Структура папок может быть такой:

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

Там, где есть два файла Makefile, каждый, как показано ниже;

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'

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

parasrish
источник
2
Это получает текущий рабочий каталог, а не домашний каталог make-файла.
Джесси Чисхолм
Это строки в Makefile. Теперь, когда команда Makefile выполняется, она получит путь, по которому находится Makefile. Я понимаю, что "домашний каталог makefile" ссылается на то же самое, то есть путь к каталогу, в котором выполняется Makefile.
parasrish
@ Джесс Чисхолм, пожалуйста, зайдите снова. Я объяснил с использованием.
parasrish
Повторяющаяся ошибка: она не работает для сборок вне дерева. Кроме того, ваш терминал gnome предоставляет простой способ копирования текста: просто выберите его. Ходят слухи, что Имгур совершенно разорен.
Йохан Буле
-2

Обновление 2018/03/05 Finnaly Я использую это:


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%/*})

Может отображаться правильно, если выполняется в другой оболочке и другом каталоге.

zhukunqian
источник
1
«Может показывать правильно, если выполняется, если другая оболочка и другой каталог» не имеет смысла на английском языке. Можете ли вы попытаться объяснить снова?
Кит М
извините за мой плохой английский язык
zhukunqian
Спасибо за редактирование, я вижу, вы имели в виду IN сейчас. Можете ли вы объяснить, что это делает, прежде чем я попробую? Я не уверен, как его использовать. Как то, что вы подразумеваете под «другой оболочкой»
Кит М
1
В этом ответе так много неправильных вещей, которые невозможно исправить. Я предлагаю простое удаление.
Йохан Буле
-2

Одной строки в Makefile должно быть достаточно:

DIR := $(notdir $(CURDIR))

Тач Ван
источник