Làm thế nào để có được thư mục tương đối hiện tại của Makefile của bạn?


202

Tôi có một số Makefiles trong các thư mục cụ thể của ứng dụng như thế này:

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

Mỗi Makefile bao gồm một tệp .inc trong đường dẫn này tăng một cấp:

/project1/apps/app_rules.inc

Bên trong app_rules.inc Tôi đang đặt đích đến nơi tôi muốn các nhị phân được đặt khi được xây dựng. Tôi muốn tất cả các nhị phân nằm trong app_typeđường dẫn tương ứng của chúng :

/project1/bin/app_typeA/

Tôi đã thử sử dụng$(CURDIR) , như thế này:

OUTPUT_PATH = /project1/bin/$(CURDIR)

nhưng thay vào đó tôi đã nhận được các nhị phân được chôn trong toàn bộ tên đường dẫn như thế này: (chú ý sự dư thừa)

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

Tôi có thể làm gì để có được "thư mục hiện tại" thực thi để tôi có thể biết chỉ app_typeXđể đặt các nhị phân trong thư mục loại tương ứng của chúng?

Câu trả lời:


272

Hàm vỏ.

Bạn có thể sử dụng shellchức năng : current_dir = $(shell pwd). Hoặc shellkết hợp với notdir, nếu bạn không cần đường dẫn tuyệt đối : current_dir = $(notdir $(shell pwd)).

Cập nhật.

Giải pháp đã cho chỉ hoạt động khi bạn đang chạy maketừ thư mục hiện tại của Makefile.
Như @Flimm đã lưu ý:

Lưu ý rằng điều này trả về thư mục làm việc hiện tại, không phải thư mục mẹ của Makefile.
Ví dụ, nếu bạn chạy cd /; make -f /home/username/project/Makefile, current_dirbiến sẽ là /, không /home/username/project/.

Mã dưới đây sẽ hoạt động cho Makefiles được gọi từ bất kỳ thư mục nào:

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

5
Điều đó không làm việc. Đó là cùng một vấn đề. pwd in tên đường dẫn đầy đủ, tôi chỉ muốn tên của thư mục hiện tại thực sự tôi đang ở.
boltup_im_coding

6
Nó cần phải được mở rộng giá trị ngay lập tức để sử dụng toán tử: =. Như trong mkfile_path: = và current_dir = (nếu không, các giá trị bên phải có thể được đánh giá sau khi MAKEFILE_LIST đã thay đổi sau khi các Makefile khác được đưa vào
Tom Gruner

10
Tôi biết đây là một câu hỏi cũ nhưng tôi mới chạy vào và nghĩ rằng nó có thể hữu ích. Điều này cũng sẽ không hoạt động khi có một khoảng trống trong đường dẫn tương đối của tệp thực hiện. Cụ thể, $(lastword "$(MAKEFILE_LIST)")của một makefile tại đường dẫn "Sub Directory/Makefile"sẽ cung cấp cho bạn "Directory/Makefile". Tôi không nghĩ có bất kỳ cách nào khác vì điều này $(MAKEFILE_LIST)với hai makefile cung cấp cho bạn một chuỗi duy nhất "Makefile One Makefile Two, không thể xử lý các khoảng trắng
mrosales

2
Điều đó current_dirkhông làm việc cho tôi. $(PWD)không và nó đơn giản hơn nhiều.
CaTx

3
"solution only works when you are running make from the Makefile's current directory"là một cách nhẹ nhàng để nói "không thực sự làm việc". Tôi sẽ đặt phần mới nhất ở đầu câu trả lời.
Victor Sergienko

138

Như được lấy từ đây ;

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

Hiện lên như;

$ cd /home/user/

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

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

Hi vọng điêu nay co ich


3
IMHO tốt hơn là sử dụng chức năng tạo nội bộ dirthay vì shell dirnamemặc dù nó có tên đường dẫn với /ký tự dấu .
Serge Roussak

3
vì giảm sự phụ thuộc vào các công cụ bên ngoài. Tức là nếu có bí danh của lệnh dirname tại một số hệ thống thì sao? ..
Serge Roussak

6
Điều này gần như làm việc với tôi, nhưng DIR có một khoảng trắng hàng đầu, vì vậy một thay đổi nhỏ đã khiến công việc trở nên hoàn hảo:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz

9
Để tham chiếu tệp thực hiện hiện tại , việc sử dụng $MAKEFILE_LISTphải đi trước mọi includehoạt động ( tài liệu ).
tộc

2
Nên sử dụng dấu ngoặc kép - ít nhất là xung quanh đối số để dirname- trong trường hợp có khoảng trắng trong đường dẫn.
tộc

46

Nếu bạn đang sử dụng GNU make, $ (CURDIR) thực sự là một biến tích hợp. Đó là vị trí mà Makefile nằm trong thư mục làm việc hiện tại, có lẽ là nơi Makefile nằm, nhưng không phải lúc nào cũng vậy .

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

Xem Phụ lục Tham khảo nhanh trong http://www.gnu.org/software/make/manual/make.html


18
Từ hướng dẫn GNU Make (trang 51): "khi GNU make start (sau khi nó đã xử lý bất kỳ tùy chọn -C nào), nó đặt biến CURDIR thành tên đường dẫn của thư mục làm việc hiện tại." Không phải vị trí nơi Makefile được đặt - mặc dù, chúng có thể giống nhau.
Simon Peverett

4
Bị từ chối vì câu trả lời không đúng về mặt kỹ thuật, như Simon Peverett đã lưu ý trong bình luận trước đó.
Subfuzion

5
@SimonPeverett: Biểu thức trong ngoặc có nghĩa là "thư mục làm việc hiện tại SAU -C opts được áp dụng". Cụ thể là $ (CURDIR) cho kết quả chính xác giống nhau cho "make -C xxx" và "cd xxx; make". Ngoài ra, nó loại bỏ các dấu /. Tôi đã thử gắn với gmakev3.81. Nhưng bạn đúng nếu makeđược gọi là make -f xxx/Makefile.
TrueY

CURDIRlàm việc cho tôi nơi PWDkhông. Tôi đã làm mkdir -p a/s/dsau đó trong mỗi thư mục có một tệp thực hiện PWDCURDIRtrước đó +$(MAKE) <subdir>.
Mark K Cowan

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

12
... Trừ khi bạn có không gian trong bất kỳ con đường nào.
Craig Ringer

9
... trừ khi bạn đã bao gồm một số makefile khác trong dòng trước
robal

1
@robal Yep. Đó là những gì tôi vừa gặp phải. Điều này hoạt động rất tốt nếu bạn chỉ có hai Makefiles (cái chính cộng với một cái đi kèm).
sherrellbc

6

Tôi đã thử nhiều câu trả lời trong số này, nhưng trên hệ thống AIX của tôi với gnu kiếm được 3,80 tôi cần phải làm một số việc cũ.

Hóa ra lastword, abspathrealpathkhông được thêm cho đến 3,81. :

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

Như những người khác đã nói, không phải là thanh lịch nhất vì nó gọi vỏ hai lần, và nó vẫn có vấn đề về không gian.

Nhưng vì tôi không có bất kỳ khoảng trống nào trên đường đi, nó hoạt động với tôi bất kể tôi bắt đầu như thế nào make:

  • thực hiện -f .. / bất cứ nơi nào / makefile
  • làm -C .. / bất cứ nơi nào
  • làm -C ~ / bất cứ nơi nào
  • cd .. / bất cứ nơi nào; làm

Tất cả cho tôi wherevercho current_dirvà đường dẫn tuyệt đối để wherevercho mkfile_dir.


Tôi, trước hết, đã biên dịch gnu-make-3,82 trên aix (5-6-7) mà không gặp vấn đề gì.
Zsigmond Lőrinczy

Có, bằng 3,81 các chức năng cần thiết cho các giải pháp trước đó đã được triển khai. Câu trả lời của tôi là dành cho các hệ thống cũ hơn với 3,80 hoặc sớm hơn. Đáng buồn thay, tại nơi làm việc, tôi không được phép thêm hoặc nâng cấp các công cụ lên hệ thống AIX. :(
Jesse Chisholm

5

Tôi thích câu trả lời được chọn, nhưng tôi nghĩ sẽ hữu ích hơn khi thực sự cho thấy nó hoạt động hơn là giải thích nó.

/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

Đầu ra:

+ 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

GHI CHÚ: Hàm $(patsubst %/,%,[path/goes/here/])này được sử dụng để tước dấu gạch chéo.


2

Đây là một lớp lót để có được đường dẫn tuyệt đối đến Makefiletệp của bạn bằng cú pháp shell:

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

Và đây là phiên bản không có vỏ dựa trên câu trả lời @ 0xff :

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

Kiểm tra nó bằng cách in nó, như:

cwd:
        @echo $(CWD)

1
Tầm quan trọng! Ví dụ thứ hai ở trên cần toán tử gán: = nếu không một số điều rất kỳ lạ và tức giận có thể xảy ra với các
tệp tạo

1
Đầu tiên là một lỗi lặp đi lặp lại và không hoạt động đối với các bản dựng ngoài cây.
Johan Boulé

2

Giải pháp được tìm thấy ở đây: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

Giải pháp là : $ (CURDIR)

Bạn có thể sử dụng nó như thế:

CUR_DIR = $(CURDIR)

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

3
Chào mừng bạn đến với Stack Overflow. Bạn có thể thêm chi tiết cho câu trả lời của bạn? Các câu hỏi $(CURDIR)đã được thử không thành công.
Sean

4
CẢNH BÁO, nhân viên nhanh chóng: Đây không phải là thư mục, nơi tạo tệp , mà là thư mục làm việc hiện tại (nơi makeđã được khởi chạy). Đó thực sự là những gì OP thực sự muốn, nhưng tiêu đề câu hỏi là không có thật, vì vậy bản thân câu hỏi không nhất quán. Vì vậy, đây là một câu trả lời đúng cho một câu hỏi không chính xác. ;)
Sz.

Điều này hoàn toàn sai và một câu trả lời sai thừa
UpAndAdam

0

Theo như tôi biết đây là câu trả lời duy nhất ở đây hoạt động chính xác với khoảng trắng:

space:= 
space+=

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

Về cơ bản, nó hoạt động bằng cách thoát các ký tự khoảng trắng bằng cách thay thế ' 'cho '\ 'phép Make phân tích chính xác và sau đó xóa tên tệp của tệp tạo tệp trongMAKEFILE_LIST bằng cách thực hiện một thay thế khác để bạn rời khỏi thư mục makefile. điều trên thế giới nhưng nó làm việc

Bạn sẽ kết thúc với một cái gì đó như thế này, nơi tất cả các không gian được thoát:

$(info CURRENT_PATH = $(CURRENT_PATH))

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

-2

Ví dụ để bạn tham khảo, như sau:

Cấu trúc thư mục có thể là:

nhập mô tả hình ảnh ở đây

Trường hợp có hai Makefiles, mỗi cái như dưới đây;

sample/Makefile
test/Makefile

Bây giờ, chúng ta hãy xem nội dung của Makefiles.

mẫu / Makefile

export ROOT_DIR=${PWD}

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

kiểm tra / Makefile

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

Bây giờ, thực hiện mẫu / Makefile, như;

cd sample
make

ĐẦU RA:

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'

Giải thích, có thể là thư mục mẹ / home có thể được lưu trữ trong cờ môi trường và có thể được xuất ra, để nó có thể được sử dụng trong tất cả các tệp tạo thư mục con.


2
Điều này nhận được thư mục làm việc hiện tại, không phải thư mục nhà của makefile.
Jesse Chisholm

Đây là những dòng trong Makefile. Bây giờ, khi lệnh Makefile thực thi, điều này sẽ nhận được đường dẫn của Makefile. Tôi hiểu "thư mục nhà makefile" đang đề cập đến cùng, tức là đường dẫn thư mục mà Makefile đang thực thi.
ký sinh

@Jlie Chisholm vui lòng xem lại. Tôi đã giải thích với việc sử dụng.
ký sinh

Một lỗi lặp đi lặp lại: nó không hoạt động đối với các bản dựng ngoài cây. Ngoài ra, thiết bị đầu cuối gnome của bạn cung cấp một cách đơn giản để sao chép văn bản: chỉ cần chọn nó. Tin đồn có Imgur bị phá vỡ.
Johan Boulé

-2

cập nhật 2018/03/05 Finnaly tôi sử dụng này:


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

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

Nó có thể được thực hiện chính xác trong đường dẫn tương đối hoặc đường dẫn tuyệt đối. Thực hiện đúng được viện dẫn bởi crontab. Thực hiện đúng trong vỏ khác.

hiển thị ví dụ, a.sh in đường dẫn tự.

[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 /]# 

cũ: Tôi sử dụng cái này:

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

Nó có thể hiển thị chính xác nếu được thực thi trong shell khác và thư mục khác.


1
"Nó có thể hiển thị chính xác nếu được thực thi nếu shell khác và thư mục khác" không có nghĩa trong tiếng Anh. Bạn có thể thử giải thích lại không?
Keith M

xin lỗi vì tiếng anh kém của tôi
zhukunqian 17/03/2017

Cảm ơn đã chỉnh sửa, tôi thấy bạn có nghĩa là VÀO ngay bây giờ. Bạn có thể giải thích điều này trước khi tôi thử nó không? Tôi không chắc làm thế nào để sử dụng nó. Giống như những gì bạn có nghĩa là "vỏ khác"
Keith M

1
Câu trả lời này có quá nhiều điều sai nên không thể sửa được. Tôi đề nghị loại bỏ đơn giản.
Johan Boulé

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.