วิธีรับไดเรกทอรีสัมพันธ์ปัจจุบันของ 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เพื่อวางไบนารีในโฟลเดอร์ประเภทของพวกเขา?

คำตอบ:


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 มีการเปลี่ยนแปลงหลังจาก Makefiles อื่น ๆ รวมอยู่ด้วย
Tom Gruner

10
ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันเพิ่งพบเจอและคิดว่ามันจะมีประโยชน์ สิ่งนี้จะไม่ทำงานเมื่อมีช่องว่างในพา ธ สัมพัทธ์ของ makefile โดยเฉพาะ$(lastword "$(MAKEFILE_LIST)")ของ Makefile ที่เส้นทางที่จะทำให้คุณ"Sub Directory/Makefile" "Directory/Makefile"ฉันไม่คิดว่าจะมีวิธีการใด ๆ เนื่องจาก$(MAKEFILE_LIST)มีสอง makefiles จะให้สายเดียว"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"เป็นวิธีที่ไม่รุนแรงที่จะพูดว่า ฉันจะใส่ชิ้นล่าสุดที่ด้านบนของคำตอบ
Victor Sergienko

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 มันจะเป็นการดีกว่าถ้าจะใช้ฟังก์ชั่นการสร้างแบบภายในdirแทนที่จะshell dirnameเป็นชื่อพา ธ ที่มี/ตัวอักษรต่อท้าย
Serge Roussak

3
เนื่องจากการลดการพึ่งพาเครื่องมือภายนอก คือจะเกิดอะไรขึ้นถ้ามีนามแฝงของคำสั่ง dirname ในบางระบบ? ..
Serge Roussak

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 ทำการเริ่มต้น (หลังจากประมวลผลตัวเลือก -C ใด ๆ ) มันจะตั้งค่าตัวแปร CURDIR เป็นชื่อพา ธ ของไดเรกทอรีการทำงานปัจจุบัน" ไม่ใช่ตำแหน่งที่ Makefile อยู่ - แม้ว่ามันอาจจะเหมือนกัน
Simon Peverett

4
ลดลงเนื่องจากคำตอบไม่ถูกต้องทางเทคนิคตามที่ระบุไว้โดย Simon Peverett ในความคิดเห็นก่อนหน้า
Subfuzion

5
@SimonPeverett: การแสดงออกในวงเล็บหมายถึง "ไดเรกทอรีการทำงานปัจจุบันหลังจาก -C opts ใช้" คือ $ (CURDIR) ให้ผลลัพธ์เหมือนกันทุกประการสำหรับ "make -C xxx" และ "cd xxx; make" /นอกจากนี้ยังจะเอาต่อท้าย ฉันพยายามผูกด้วยgmakev3.81 แต่คุณกำลังที่เหมาะสมถ้าจะเรียกว่าเป็นmake make -f xxx/Makefile
TrueY

CURDIRทำงานได้สำหรับฉันที่PWDไม่ได้ ฉันไม่ได้mkdir -p a/s/dแล้วในโฟลเดอร์แต่ละคนก็มี Makefile ซึ่งพิมพ์PWDและก่อนที่จะCURDIR +$(MAKE) <subdir>
Mark K Cowan

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

12
... เว้นแต่คุณจะมีช่องว่างในเส้นทางใด ๆ
Craig Ringer

9
... เว้นแต่คุณจะได้รวม makefile อื่น ๆ ในบรรทัดก่อนหน้า
robal

1
@robal Yep นั่นคือสิ่งที่ฉันเพิ่งพบ วิธีนี้ใช้งานได้ดีถ้าคุณมี Makefiles สองตัวเท่านั้น (ตัวหลักรวมกับอันที่รวมอยู่ด้วย)
sherrellbc

6

ฉันลองคำตอบเหล่านี้หลายคำ แต่ในระบบ AIX ด้วย gnu ทำ 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:

  • ทำให้ -f ../weverever/makefile
  • ทำ -C .. / ทุกที่
  • ทำ -C ~ / ทุกที่
  • cd .. / ทุกที่; แต่งหน้า

ให้ฉันทั้งหมดwhereverสำหรับcurrent_dirและเส้นทางสัมบูรณ์ไปสำหรับwherevermkfile_dir


ฉันหนึ่งได้รวบรวม gnu-make-3.82 ใน aix (5-6-7) โดยไม่มีปัญหา
Zsigmond Lőrinczy

ใช่ 3.81 มีการใช้ฟังก์ชั่นที่จำเป็นสำหรับการแก้ปัญหาก่อนหน้านี้ คำตอบของฉันสำหรับระบบเก่าที่มี 3.80 ขึ้นไป น่าเศร้าที่ทำงานฉันไม่ได้รับอนุญาตให้เพิ่มหรืออัปเกรดเครื่องมือให้กับระบบ AIX :(
Jesse Chisholm

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
ความสำคัญอย่างยิ่ง! ตัวอย่างที่สองข้างต้นจำเป็นต้องใช้: = โอเปอเรเตอร์ที่ได้รับมอบหมายหรือสิ่งอื่น ๆ ที่แปลกและโกรธอาจเกิดขึ้นได้กับ makefiles ที่ซับซ้อน (เชื่อฉัน)
EMon

1
ข้อแรกเป็นข้อผิดพลาดซ้ำ ๆ และไม่สามารถใช้งานได้กับการสร้างนอกโครงสร้าง
Johan Boulé

2

วิธีแก้ปัญหาพบที่นี่: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

ทางออกคือ : $ (CURDIR)

คุณสามารถใช้มันได้

CUR_DIR = $(CURDIR)

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

3
ยินดีต้อนรับสู่ Stack Overflow คุณสามารถเพิ่มรายละเอียดเพิ่มเติมในคำตอบของคุณ? คำถามระบุว่า$(CURDIR)ได้ลองแล้วไม่สำเร็จ
Sean

4
คำเตือนอย่างรวดเร็วชาว Google: นี่คือไม่ไดเรกทอรีที่ Makefile เป็น แต่ปัจจุบันทำงานไดเรกทอรี (ที่makeได้รับการเปิดตัวใน) มันคือสิ่งที่ OP ต้องการจริงๆ แต่ชื่อคำถามนั้นเป็นการหลอกลวงดังนั้นคำถามนั้นจึงไม่สอดคล้องกัน ดังนั้นนี่คือคำตอบที่ถูกต้องสำหรับคำถามที่ไม่ถูกต้อง ;)
Sz.

นี่เป็นคำตอบที่ผิดและคำตอบที่ซ้ำซ้อน
UpAndAdam

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'

คำอธิบายอาจเป็นไปได้ว่าไดเร็กทอรี parent / home สามารถเก็บไว้ใน environment-flag และสามารถส่งออกได้เพื่อให้สามารถใช้ใน make-directory ย่อยทั้งหมดได้


2
สิ่งนี้รับไดเร็กทอรีการทำงานปัจจุบันไม่ใช่ไดเร็กทอรีโฮมของ makefile
Jesse Chisholm

นี่คือบรรทัดใน Makefile ตอนนี้ในขณะที่คำสั่ง Makefile ดำเนินการสิ่งนี้จะได้รับพา ธ ที่ Makefile คือ ฉันเข้าใจว่า "makefile home directory" กำลังอ้างถึงเดียวกันเช่นเส้นทางไดเรกทอรีที่ Makefile กำลังดำเนินการ
ปรสิต

@Jesse Chisholm โปรดกลับมาอีกครั้ง ฉันอธิบายด้วยการใช้งาน
ปรสิต

ข้อผิดพลาดซ้ำ ๆ : มันไม่สามารถใช้งานได้กับต้นไม้นอกโครงสร้าง นอกจากนี้เทอร์มินอล Gnome ของคุณยังมีวิธีการที่ง่ายในการคัดลอกข้อความ: เพียงแค่เลือกมัน ข่าวลือมี Imgur เป็น brankrupt
Johan Boulé

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

มันสามารถแสดงให้ถูกต้องหากดำเนินการในเปลือกอื่น ๆ และไดเรกทอรีอื่น ๆ


1
"มันสามารถแสดงให้ถูกต้องหากดำเนินการถ้าเชลล์อื่น ๆ และไดเรกทอรีอื่น ๆ " ไม่สมเหตุสมผลในภาษาอังกฤษ คุณลองอธิบายอีกครั้งได้ไหม
Keith M

ขอโทษสำหรับภาษาอังกฤษที่ไม่ดีของฉัน
zhukunqian

ขอบคุณสำหรับการแก้ไขฉันเห็นว่าคุณหมายถึงในตอนนี้ คุณช่วยอธิบายสิ่งนี้ได้ไหมก่อนที่ฉันจะลอง ฉันไม่แน่ใจว่าจะใช้มันอย่างไร เช่นคุณหมายถึงอะไรโดย "shell อื่น ๆ "
Keith M

1
คำตอบนี้มีสิ่งผิดปกติมากมายที่ไม่สามารถแก้ไขได้ ฉันขอแนะนำให้ลบอย่างง่าย
Johan Boulé

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.