Bagaimana cara mendapatkan direktori relatif dari Makefile Anda saat ini?

202

Saya memiliki beberapa Makefile di direktori khusus aplikasi seperti ini:

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

Setiap Makefile menyertakan file .inc di jalur ini satu tingkat lebih tinggi:

/project1/apps/app_rules.inc

Di dalam app_rules.inc saya menetapkan tujuan di mana saya ingin binari ditempatkan ketika dibangun. Saya ingin semua binari berada di app_typejalurnya masing-masing :

/project1/bin/app_typeA/

Saya mencoba menggunakan$(CURDIR) , seperti ini:

OUTPUT_PATH = /project1/bin/$(CURDIR)

tetapi sebaliknya saya membuat binari dimakamkan di seluruh nama jalan seperti ini: (perhatikan redundansi)

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

Apa yang bisa saya lakukan untuk mendapatkan "direktori saat ini" dari eksekusi sehingga saya bisa tahu hanya app_typeXuntuk menempatkan binari di folder tipe masing-masing?

boltup_im_coding
sumber

Jawaban:

272

Fungsi shell.

Anda dapat menggunakan shellfungsi: current_dir = $(shell pwd). Atau shelldalam kombinasi dengan notdir, jika Anda tidak perlu path absolut: current_dir = $(notdir $(shell pwd)).

Memperbarui.

Solusi yang diberikan hanya berfungsi ketika Anda menjalankan makedari direktori Makefile saat ini.
Sebagaimana dicatat oleh @Flimm:

Perhatikan bahwa ini mengembalikan direktori kerja saat ini, bukan direktori induk dari Makefile.
Misalnya, jika Anda menjalankan cd /; make -f /home/username/project/Makefile, current_dirvariabelnya adalah /, bukan /home/username/project/.

Kode di bawah ini akan berfungsi untuk Makefiles yang dipanggil dari direktori apa pun:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
Nikolai Popov
sumber
5
Itu tidak bekerja. Itu adalah masalah yang sama. pwd mencetak nama path lengkap, saya hanya ingin nama folder aktual saya saat ini.
boltup_im_coding
6
Perlu memiliki nilai yang diperluas segera sehingga: = operator harus digunakan. Seperti dalam mkfile_path: = dan current_dir = (jika tidak nilai-nilai sisi kanan dapat dievaluasi kemudian ketika MAKEFILE_LIST telah berubah setelah Makefiles lainnya dimasukkan
Tom Gruner
10
Saya tahu ini adalah pertanyaan lama tetapi saya hanya bertemu dan berpikir ini bisa berguna. Ini juga tidak akan berfungsi ketika ada ruang di jalur relatif makefile. Secara khusus, $(lastword "$(MAKEFILE_LIST)")dari makefile di jalan "Sub Directory/Makefile"akan memberi Anda "Directory/Makefile". Saya tidak berpikir ada jalan keluar karena ini $(MAKEFILE_LIST)dengan dua makefile memberi Anda satu string "Makefile One Makefile Two, yang tidak dapat menangani spasi
mrosales
2
Itu current_dirtidak berhasil untuk saya. $(PWD)tidak dan itu jauh lebih sederhana.
CaTx
3
"solution only works when you are running make from the Makefile's current directory"adalah cara yang ringan untuk mengatakan "tidak benar-benar bekerja". Saya akan menempatkan potongan terbaru di bagian atas jawaban.
Victor Sergienko
138

Seperti yang diambil dari sini ;

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

Ditampilkan sebagai;

$ cd /home/user/

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

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

Semoga ini membantu

sleepycal
sumber
3
IMHO lebih baik menggunakan internal make-function dirdaripada shell dirnamemendapatkan pathname dengan /karakter trailing .
Serge Roussak
3
karena pengurangan ketergantungan pada alat eksternal. Yaitu bagaimana jika akan ada alias perintah dirname di beberapa sistem? ..
Serge Roussak
6
Ini hampir berhasil untuk saya, tetapi DIR memiliki ruang putih terkemuka, jadi perubahan kecil membuat pekerjaan dengan sempurna:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz
9
Untuk referensi makefile saat ini , penggunaan $MAKEFILE_LISTharus mendahului includeoperasi ( docs ) apa pun.
Nobar
2
Harus menggunakan tanda kutip - setidaknya di sekitar argumen untuk dirname- jika ada ruang di jalan.
nobar
46

Jika Anda menggunakan GNU make, $ (CURDIR) sebenarnya adalah variabel bawaan. Ini adalah lokasi di mana Makefile berada di direktori kerja saat ini, yang mungkin merupakan tempat Makefile berada, tetapi tidak selalu .

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

Lihat Lampiran Referensi Cepat di http://www.gnu.org/software/make/manual/make.html

Tzunghsing David Wong
sumber
18
Dari manual GNU Make (halaman 51): "ketika GNU make memulai (setelah ia memproses opsi -C), ia menetapkan variabel CURDIR ke nama path direktori kerja saat ini." Bukan lokasi tempat Makefile berada - meskipun, mereka mungkin sama.
Simon Peverett
4
Diturunkan karena jawabannya secara teknis tidak benar, seperti dicatat oleh Simon Peverett dalam komentar sebelumnya.
Subfuzion
5
@SimonPeverett: Ekspresi dalam tanda kurung berarti "direktori kerja saat ini SETELAH -C opts diterapkan". Yaitu $ (CURDIR) memberikan hasil yang persis sama untuk "make -C xxx" dan "cd xxx; make". Juga menghapus trailing /. Saya mencoba terikat dengan gmakev3.81. Tetapi Anda benar jika makedisebut sebagai make -f xxx/Makefile.
Benar
CURDIRbekerja untuk saya di mana PWDtidak. Saya mkdir -p a/s/dkemudian di setiap folder memiliki makefile yang dicetak PWDdan CURDIRsebelumnya +$(MAKE) <subdir>.
Mark K Cowan
27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
momo
sumber
12
... kecuali Anda memiliki spasi di jalur mana pun.
Craig Ringer
9
... kecuali Anda telah memasukkan beberapa makefile lain di baris sebelumnya
robal
1
@robal Yep. Itulah yang baru saja saya temui. Ini bekerja sangat baik jika Anda hanya memiliki dua Makefiles (yang utama ditambah satu yang disertakan).
sherrellbc
6

Saya mencoba banyak jawaban ini, tetapi pada sistem AIX saya dengan gnu make 3,80 saya perlu melakukan beberapa hal jadul.

Ternyata itu lastword, abspathdan realpathtidak ditambahkan hingga 3,81. :(

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

Seperti yang orang lain katakan, bukan yang paling elegan karena memanggil shell dua kali, dan masih memiliki masalah spasi.

Tetapi karena saya tidak memiliki ruang di jalur saya, itu berfungsi untuk saya terlepas dari bagaimana saya memulai make:

  • make -f ../wherever/makefile
  • buat -C ../ dimanapun
  • buat -C ~ / dimanapun
  • cd ../ dimanapun; membuat

Semua memberi saya whereveruntuk current_dirdan jalan absolut menuju whereveruntuk mkfile_dir.

Jesse Chisholm
sumber
Saya, misalnya, telah mengkompilasi gnu-make-3.82 pada aix (5-6-7) tanpa masalah.
Zsigmond Lőrinczy
Ya, pada 3,81 fungsi yang diperlukan untuk solusi sebelumnya telah diterapkan. Jawaban saya adalah untuk sistem yang lebih lama dengan 3,80 atau lebih lama. Sayangnya, di tempat kerja saya tidak diizinkan untuk menambah atau meningkatkan alat ke sistem AIX. :(
Jesse Chisholm
5

Saya suka jawaban yang dipilih, tetapi saya pikir akan lebih membantu untuk benar-benar menunjukkannya berfungsi daripada menjelaskannya.

/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

Keluaran:

+ 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

CATATAN: Fungsi $(patsubst %/,%,[path/goes/here/])ini digunakan untuk menghapus garis miring.

Bruno Bronosky
sumber
3

Solusi ditemukan di sini: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

Solusinya adalah : $ (CURDIR)

Anda bisa menggunakannya seperti itu:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder
Soma Bini
sumber
3
Selamat datang di Stack Overflow. Bisakah Anda menambahkan lebih detail pada jawaban Anda? Pertanyaannya menyatakan bahwa $(CURDIR)sudah dicoba tidak berhasil.
Sean
4
PERINGATAN, googler cepat: Ini bukan direktori, di mana makefile berada, tetapi direktori kerja saat ini (di mana makediluncurkan di). Memang, memang itu yang diinginkan OP, tetapi judul pertanyaannya palsu, jadi pertanyaannya sendiri tidak konsisten. Jadi, ini adalah jawaban yang benar untuk pertanyaan yang salah. ;)
Sz.
Ini jelas salah dan jawaban salah yang berlebihan
UpAndAdam
2

Berikut ini adalah satu-liner untuk mendapatkan path absolut ke Makefilefile Anda menggunakan sintaks shell:

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

Dan ini adalah versi tanpa shell berdasarkan jawaban @ 0xff :

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

Uji dengan mencetaknya, seperti:

cwd:
        @echo $(CWD)
kenorb
sumber
1
Pentingnya yang Penting! Contoh kedua di atas membutuhkan: = operator penugasan atau hal-hal yang sangat aneh dan marah dapat terjadi dengan makefile yang kompleks (percayalah)
EMon
1
Yang pertama adalah kesalahan berulang dan tidak bekerja untuk out-of-tree-builds.
Johan Boulé
0

Sejauh yang saya ketahui ini adalah satu-satunya jawaban di sini yang bekerja dengan benar dengan spasi:

space:= 
space+=

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

Ini pada dasarnya bekerja dengan melarikan diri karakter ruang dengan menggantikan ' 'untuk '\ 'yang memungkinkan Membuat untuk mengurai dengan benar, dan kemudian menghapus nama file dari makefile diMAKEFILE_LIST dengan melakukan substitusi lain sehingga kau pergi dengan direktori yang makefile di. Tidak persis paling kompak hal di dunia tetapi itu berhasil.

Anda akan berakhir dengan sesuatu seperti ini di mana semua ruang diloloskan:

$(info CURRENT_PATH = $(CURRENT_PATH))

CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/
Peter Frost
sumber
-2

Contoh untuk referensi Anda, seperti di bawah ini:

Struktur folder mungkin sebagai:

masukkan deskripsi gambar di sini

Di mana ada dua Makefile, masing-masing seperti di bawah ini;

sample/Makefile
test/Makefile

Sekarang, mari kita lihat konten Makefiles.

contoh / Makefile

export ROOT_DIR=${PWD}

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

test / Makefile

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

Sekarang, jalankan sampel / Makefile, sebagai;

cd sample
make

KELUARAN:

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'

Penjelasannya, adalah bahwa direktori induk / home dapat disimpan di lingkungan-flag, dan dapat diekspor, sehingga dapat digunakan di semua makefile sub-direktori.

parasrish
sumber
2
Ini mendapatkan direktori kerja saat ini, bukan direktori home makefile.
Jesse Chisholm
Ini adalah garis-garis di Makefile. Sekarang, ketika perintah Makefile dijalankan, ini akan mendapatkan path di mana Makefile berada. Saya mengerti "direktori home makefile" mengacu pada hal yang sama, yaitu path direktori di mana Makefile mengeksekusi.
parasrish
@Jesse Chisholm silakan kembali. Saya telah menjelaskan dengan penggunaan.
parasrish
Kesalahan berulang: itu tidak berhasil untuk out-of-tree-builds. Juga, terminal gnome Anda menyediakan cara sederhana untuk menyalin teks: cukup pilih saja. Desas-desus bahwa Imgur adalah brankrupt.
Johan Boulé
-2

perbarui 2018/03/05 hingga akhir Saya menggunakan ini:


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

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

Itu dapat dijalankan dengan benar di jalur relatif atau jalur absolut. Dieksekusi benar dipanggil oleh crontab. Dieksekusi benar di shell lain.

perlihatkan contoh, a.sh print self path.

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

lama: Saya menggunakan ini:

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

Ini dapat ditampilkan dengan benar jika dieksekusi di shell lain dan direktori lain.

zhukunqian
sumber
1
"Ini dapat ditampilkan dengan benar jika dijalankan jika shell lain dan direktori lain" tidak masuk akal dalam bahasa Inggris. Bisakah Anda mencoba menjelaskan lagi?
Keith M
maaf untuk bahasa Inggris saya yang buruk
zhukunqian
Terima kasih atas hasil editnya, saya mengerti maksud Anda DI sekarang. Bisakah Anda menjelaskan apa yang dilakukannya sebelum saya mencobanya? Saya tidak yakin bagaimana menggunakannya. Seperti apa yang Anda maksud dengan "cangkang lain"
Keith M
1
Jawaban ini memiliki banyak hal salah yang tidak mungkin diperbaiki. Saya menyarankan penghapusan sederhana.
Johan Boulé
-2

Satu baris di Makefile harus cukup:

DIR := $(notdir $(CURDIR))

Thach Van
sumber