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

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

17 ответов

Решение

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

Ты можешь использовать 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))))

Как взято отсюда;

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

Появляется как;

$ cd /home/user/

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

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

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

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

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

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

THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

Простой, правильный, современный способ:

Для GNU make >= 3.81, представленный в 2006 г.

      ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
  1. MAKEFILE_LISTизменения, когда включаемые файлы входят и выходят из области действия. Последний элемент — это текущий файл.

  2. lastwordизвлекает последний элемент (имя Makefile относительноpwd)

  3. realpathвстроен вmake, и разрешается в канонический путь от корня файловой системы

  4. dirобрезает имя файла, оставляя только каталог.

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

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

Я попробовал многие из этих ответов, но в моей системе 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,

Решение найдено здесь: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

Решение: $(CURDIR)

Вы можете использовать это так:

CUR_DIR = $(CURDIR)

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

Собрав воедино приведенные здесь примеры, вы получите полный путь к make-файлу:

# Get the location of this makefile.
ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

Вот одна строка, чтобы получить абсолютный путь к вашему 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)

Насколько мне известно, это единственный ответ, который правильно работает с пробелами:

space:= 
space+=

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

По сути, он работает путем экранирования пробелов путем замены ' ' за '\ ' который позволяет Make правильно его проанализировать, а затем удаляет имя файла makefile в MAKEFILE_LIST сделав другую замену, чтобы у вас остался каталог, в котором находится make-файл. Не совсем самая компактная вещь в мире, но она действительно работает.

Вы получите что-то вроде этого, где все пробелы экранированы:

$(info CURRENT_PATH = $(CURRENT_PATH))

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

использовать{}вместо()

      cur_dir=${shell pwd}
parent_dir=${shell dirname ${shell pwd}}}

Если переменная make содержит Относительный путь ROOT_DIR

ROOT_DIR := ../../../

Их, чтобы получить абсолютный путь, просто используйте метод ниже.

ROOT_DIR := $(abspath $(ROOT_DIR))

Его работа прекрасно в GNUMake...

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

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

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

Где есть два файла 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'

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

Обновление 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%/*})

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

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

DIR := $(notdir $(CURDIR))

Другие вопросы по тегам