Come ottenere l'attuale directory relativa del tuo Makefile?


202

Ho diversi Makefile in directory specifiche dell'app come questa:

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

Ogni Makefile include un file .inc in questo percorso di un livello superiore:

/project1/apps/app_rules.inc

All'interno di app_rules.inc sto impostando la destinazione di dove voglio che i binari vengano posizionati quando vengono creati. Voglio che tutti i binari siano nel loro rispettivo app_typepercorso:

/project1/bin/app_typeA/

Ho provato a usare$(CURDIR) , in questo modo:

OUTPUT_PATH = /project1/bin/$(CURDIR)

ma invece ho seppellito i binari nell'intero percorso in questo modo: (nota la ridondanza)

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

Cosa posso fare per ottenere la "directory corrente" di esecuzione in modo che io possa sapere solo app_typeXper mettere i binari nella loro cartella dei rispettivi tipi?

Risposte:


272

La funzione shell.

È possibile utilizzare shellla funzione: current_dir = $(shell pwd). O shellin combinazione con notdir, se non ha bisogno di percorso assoluto: current_dir = $(notdir $(shell pwd)).

Aggiornare.

La soluzione fornita funziona solo quando si esegue makedalla directory corrente del Makefile.
Come ha notato @Flimm:

Si noti che questo restituisce la directory di lavoro corrente, non la directory principale del Makefile.
Ad esempio, se si esegue cd /; make -f /home/username/project/Makefile, la current_dirvariabile sarà /, no /home/username/project/.

Il codice seguente funzionerà per i Makefile invocati da qualsiasi directory:

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

5
Questo non funziona È lo stesso problema. pwd stampa il nome completo del percorso, voglio solo il nome della cartella attuale in cui mi trovo.
boltup_im_coding

6
È necessario che il valore sia espanso immediatamente, quindi è necessario utilizzare l'operatore: =. Come in mkfile_path: = e current_dir = (altrimenti i valori del lato destro potrebbero essere valutati in seguito quando MAKEFILE_LIST è cambiato dopo l'inclusione di altri Makefile
Tom Gruner

10
So che questa è una vecchia domanda ma mi sono appena imbattuto e ho pensato che potesse essere utile. Anche questo non funzionerà quando c'è uno spazio nel percorso relativo del makefile. In particolare, ti darà $(lastword "$(MAKEFILE_LIST)")un makefile sul percorso . Non credo che ci sia alcun modo per aggirare questo dato che con due makefile ti dà una singola stringa , che non può gestire gli spazi"Sub Directory/Makefile""Directory/Makefile"$(MAKEFILE_LIST)"Makefile One Makefile Two
mrosales

2
Questo current_dirnon funziona per me. $(PWD)fa ed è molto più semplice.
CaTx,

3
"solution only works when you are running make from the Makefile's current directory"è un modo mite di dire "non funziona davvero". Vorrei mettere l'ultimo pezzo in cima alla risposta.
Victor Sergienko,

138

Come preso da qui ;

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

Si presenta come;

$ cd /home/user/

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

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

Spero che questo ti aiuti


3
IMHO sarebbe meglio usare make-function interna dirinvece di shell dirnameottenere un nome di percorso con /carattere finale .
Serge Roussak,

3
a causa della riduzione delle dipendenze da strumenti esterni. Vale a dire se ci fosse un alias di comando dirname in qualche sistema? ..
Serge Roussak

6
Questo ha funzionato quasi per me, ma DIR aveva uno spazio bianco principale, quindi una piccola modifica ha funzionato perfettamente:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz,

9
Per fare riferimento al makefile corrente , l'uso di $MAKEFILE_LISTdeve precedere qualsiasi includeoperazione ( doc ).
nobar

2
Dovrebbe usare le virgolette - almeno attorno all'argomento dirname- nel caso ci siano spazi nel percorso.
nobar l'

46

Se stai usando GNU make, $ (CURDIR) è in realtà una variabile incorporata. È la posizione in cui il Makefile risiede nella directory di lavoro corrente, che è probabilmente dove si trova il Makefile, ma non sempre .

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

Vedere l'Appendice A Guida rapida in http://www.gnu.org/software/make/manual/make.html


18
Dal manuale di GNU Make (pagina 51): "all'avvio di GNU make (dopo aver elaborato qualsiasi opzione -C) imposta la variabile CURDIR sul percorso della directory di lavoro corrente." Non la posizione in cui si trova il Makefile, anche se potrebbero essere uguali.
Simon Peverett,

4
Sottovalutato perché la risposta non è tecnicamente corretta, come osservato da Simon Peverett nel commento precedente.
Subfuzion

5
@SimonPeverett: l'espressione tra parentesi indica la "directory di lavoro corrente DOPO -C opte applicate". Vale a dire $ (CURDIR) fornisce esattamente gli stessi risultati per "make -C xxx" e "cd xxx; make". Inoltre rimuove il finale /. Ho provato legato con gmakev3.81. Ma hai ragione se makesi chiama come make -f xxx/Makefile.
TrueY

CURDIRfunziona per me dove PWDno. Ho mkdir -p a/s/dquindi fatto in ogni cartella un makefile che è stato stampato PWDe CURDIRprima +$(MAKE) <subdir>.
Mark K Cowan,

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

12
... a meno che tu non abbia spazi in qualsiasi percorso.
Craig Ringer,

9
... a meno che tu non abbia incluso qualche altro makefile nella riga precedente
robal

1
@robal Yep. Questo è quello che ho appena incontrato. Funziona alla grande se hai solo due Makefile (quello principale più uno incluso).
Sherrellbc,

6

Ho provato molte di queste risposte, ma sul mio sistema AIX con gnu ho fatto 3.80 che dovevo fare alcune cose alla vecchia scuola.

Risulta che lastword, abspathe realpathnon sono stati aggiunti fino 3.81. :(

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

Come altri hanno già detto, non il più elegante in quanto invoca due volte una shell e presenta ancora problemi di spazio.

Ma poiché non ho spazi nei miei percorsi, funziona per me indipendentemente da come ho iniziato make:

  • make -f ../wherever/makefile
  • make -C ../wherever
  • make -C ~ / dovunque
  • cd ../wherever; rendere

Tutti mi danno whereverper current_dire il percorso assoluto whereverper mkfile_dir.


Io, per esempio, ho compilato gnu-make-3.82 su aix (5-6-7) senza problemi.
Zsigmond Lőrinczy,

Sì, per 3.81 sono state implementate le funzioni necessarie per le soluzioni precedenti. La mia risposta è per i sistemi più vecchi con 3.80 o precedenti. Purtroppo, al lavoro non mi è permesso aggiungere o aggiornare strumenti al sistema AIX. :(
Jesse Chisholm,

5

Mi piace la risposta scelta, ma penso che sarebbe più utile mostrarla effettivamente che spiegarla.

/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

Produzione:

+ 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

NOTA: la funzione $(patsubst %/,%,[path/goes/here/])viene utilizzata per eliminare la barra finale.


3

Soluzione trovata qui: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

La soluzione è : $ (CURDIR)

Puoi usarlo così:

CUR_DIR = $(CURDIR)

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

3
Benvenuto in Stack Overflow. Puoi aggiungere più dettagli alla tua risposta? La domanda afferma che $(CURDIR)è già stato tentato senza successo.
Sean,

4
ATTENZIONE, googler rapidi: questa non è la directory in cui si trova il makefile, ma la directory di lavoro corrente (dove è makestata avviata). È, in effetti, ciò che OP voleva davvero, ma il titolo della domanda è falso, quindi la domanda stessa è incoerente. Quindi, questa è una risposta corretta a una domanda errata. ;)
Sz.

Questo è
assolutamente

2

Ecco una riga per ottenere il percorso assoluto del tuo Makefilefile usando la sintassi della shell:

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

Ed ecco la versione senza shell basata sulla risposta @ 0xff :

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

Provalo stampandolo, come:

cwd:
        @echo $(CWD)

1
Importanza critica! Il secondo esempio sopra ha bisogno dell'operatore di assegnazione = = altrimenti possono accadere cose molto strane e arrabbiate con makefile complessi (fidati di me)
EMon

1
Il primo è un errore ripetuto e non funziona con build fuori dall'albero.
Johan Boulé,

0

Per quanto ne so, questa è l'unica risposta che funziona correttamente con gli spazi:

space:= 
space+=

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

Si lavora essenzialmente dalla fuoriuscita di caratteri di spazio sostituendo ' 'per il '\ 'quale permette Marchio di analizzare in modo corretto, e poi si rimuove il nome del makefile in MAKEFILE_LISTfacendo un'altra sostituzione così si è lasciato con la directory che makefile è. Non esattamente il più compatto cosa al mondo ma funziona.

Finirai con qualcosa del genere in cui tutti gli spazi sono fuggiti:

$(info CURRENT_PATH = $(CURRENT_PATH))

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

-2

Esempio di riferimento, come di seguito:

La struttura delle cartelle potrebbe essere come:

inserisci qui la descrizione dell'immagine

Dove ci sono due Makefile, ognuno come sotto;

sample/Makefile
test/Makefile

Ora, vediamo il contenuto dei Makefile.

campione / Makefile

export ROOT_DIR=${PWD}

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

test / Makefile

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

Ora, esegui l'esempio / Makefile, come;

cd sample
make

PRODUZIONE:

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'

Spiegazione, sarebbe che la directory principale / home può essere memorizzata nel flag di ambiente e può essere esportata, in modo che possa essere utilizzata in tutti i makefile della sottodirectory.


2
Questo ottiene la directory di lavoro corrente, non la home directory del makefile.
Jesse Chisholm,

Queste sono le linee nel Makefile. Ora, mentre il comando Makefile viene eseguito, questo otterrà il percorso in cui si trova il Makefile. Capisco che "home directory makefile" si riferisce allo stesso, vale a dire il percorso di directory in cui viene eseguito Makefile.
Parassita

@Jesse Chisholm, per favore, rivisita. Ho spiegato con l'uso.
Parassita

Un errore ripetuto: non funziona con build fuori dall'albero. Inoltre, il tuo terminale gnome fornisce un modo semplice per copiare il testo: selezionalo. Si dice che Imgur sia in rovina.
Johan Boulé,

-2

aggiornamento 2018/03/05 finemente lo uso:


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

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

Può essere eseguito correttamente nel percorso relativo o nel percorso assoluto. Eseguito correttamente invocato da crontab. Eseguito correttamente in altra shell.

mostra esempio, 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 /]# 

vecchio: io uso questo:

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

Può mostrare corretto se eseguito in altra shell e altra directory.


1
"Può mostrare corretto se eseguito se altra shell e altra directory" non ha senso in inglese. Puoi provare a spiegare di nuovo?
Keith M

scusate il mio povero inglese
zhukunqian

Grazie per la modifica, vedo che intendevi IN adesso. Puoi spiegare cosa fa questo prima di provarlo? Non sono sicuro di come usarlo. Come cosa vuoi dire con "altra shell"
Keith M

1
Questa risposta ha tante cose sbagliate che non può essere risolta. Suggerisco una semplice rimozione.
Johan Boulé,

-2

Una riga nel Makefile dovrebbe essere sufficiente:

DIR := $(notdir $(CURDIR))

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.