Comment obtenir le répertoire relatif actuel de votre Makefile?

202

J'ai plusieurs Makefiles dans des répertoires spécifiques à l'application comme celui-ci:

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

Chaque Makefile inclut un fichier .inc dans ce chemin d'un niveau supérieur:

/project1/apps/app_rules.inc

Dans app_rules.inc, je définis la destination de l'endroit où je veux que les binaires soient placés lors de la construction. Je veux que tous les binaires soient dans leur app_typechemin respectif :

/project1/bin/app_typeA/

J'ai essayé d'utiliser$(CURDIR) , comme ceci:

OUTPUT_PATH = /project1/bin/$(CURDIR)

mais à la place, j'ai fait enterrer les binaires dans le nom du chemin entier comme ceci: (remarquez la redondance)

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

Que puis-je faire pour obtenir le "répertoire courant" d'exécution afin que je puisse connaître juste le app_typeXafin de mettre les binaires dans leur dossier de types respectifs?

boltup_im_coding
la source

Réponses:

272

La fonction shell.

Vous pouvez utiliser la shellfonction: current_dir = $(shell pwd). Ou shellen combinaison avec notdir, si vous ne devez pas le chemin absolu: current_dir = $(notdir $(shell pwd)).

Mettre à jour.

La solution donnée ne fonctionne que lorsque vous exécutez à makepartir du répertoire actuel du Makefile.
Comme l'a noté @Flimm:

Notez que cela retourne le répertoire de travail actuel, pas le répertoire parent du Makefile.
Par exemple, si vous exécutez cd /; make -f /home/username/project/Makefile, la current_dirvariable sera /, non /home/username/project/.

Le code ci-dessous fonctionnera pour les Makefiles invoqués depuis n'importe quel répertoire:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
Nikolai Popov
la source
5
Cela ne fonctionne pas. C'est le même problème. pwd imprime le nom complet du chemin, je veux juste le nom du dossier actuel dans
lequel
6
Il doit avoir la valeur développée immédiatement, donc l'opérateur: = doit être utilisé. Comme dans mkfile_path: = et current_dir = (sinon les valeurs du côté droit pourraient être évaluées plus tard lorsque le MAKEFILE_LIST a changé après que d'autres Makefiles ont été inclus
Tom Gruner
10
Je sais que c'est une vieille question, mais je me suis juste heurtée et j'ai pensé que cela pourrait être utile. Cela ne fonctionnera pas non plus lorsqu'il y a un espace dans le chemin relatif du makefile. Plus précisément, $(lastword "$(MAKEFILE_LIST)")d'un makefile au chemin "Sub Directory/Makefile"vous donnera "Directory/Makefile". Je ne pense pas qu'il y ait moyen de contourner cela car $(MAKEFILE_LIST)avec deux makefiles vous donne une seule chaîne "Makefile One Makefile Two, qui ne peut pas gérer les espaces
mrosales
2
Ça current_dirne marche pas pour moi. $(PWD)fait et c'est beaucoup plus simple.
CaTx
3
"solution only works when you are running make from the Makefile's current directory"est une manière douce de dire "ne fonctionne pas vraiment". Je mettrais le dernier morceau en haut de la réponse.
Victor Sergienko
138

Tiré d' ici ;

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

Apparaît comme;

$ cd /home/user/

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

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

J'espère que cela t'aides

sleepycal
la source
3
À mon humble avis, il serait préférable d'utiliser la fonction dirde shell dirnamecréation interne plutôt que d'obtenir un nom de chemin avec un /caractère de fin .
Serge Roussak
3
en raison de la réduction des dépendances sur les outils externes. C'est-à-dire s'il y aura un alias de commande dirname sur un système? ..
Serge Roussak
6
Cela a presque fonctionné pour moi, mais DIR avait un espace blanc de premier plan, donc un changement mineur a parfaitement fonctionné:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz
9
Pour référencer le makefile actuel , l'utilisation de $MAKEFILE_LISTdoit précéder toute includeopération ( docs ).
nobar
2
Devrait utiliser des guillemets - au moins autour de l'argument dirname- au cas où il y aurait des espaces dans le chemin.
nobar
46

Si vous utilisez GNU make, $ (CURDIR) est en fait une variable intégrée. C'est l' emplacement où le Makefile réside dans le répertoire de travail actuel, qui est probablement où se trouve le Makefile, mais pas toujours .

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

Voir l'annexe A Référence rapide sur http://www.gnu.org/software/make/manual/make.html

Tzunghsing David Wong
la source
18
Dans le manuel GNU Make (page 51): "lorsque GNU make démarre (après avoir traité toutes les options -C), il définit la variable CURDIR au nom de chemin du répertoire de travail actuel." Pas l'endroit où se trouve le Makefile - bien qu'ils puissent être les mêmes.
Simon Peverett
4
Voté parce que la réponse n'est pas techniquement correcte, comme l'a noté Simon Peverett dans le commentaire précédent.
Subfuzion
5
@SimonPeverett: L'expression entre parenthèses signifie le "répertoire de travail courant APRÈS -C opts appliqué". A savoir $ (CURDIR) donne exactement les mêmes résultats pour "make -C xxx" et "cd xxx; make". En outre, il supprime la fuite /. J'ai essayé à égalité avec la gmakev3.81. Mais vous avez raison si makeest appelé comme make -f xxx/Makefile.
TrueY
CURDIRtravaille pour moi où PWDn'a pas. J'ai mkdir -p a/s/densuite fait dans chaque dossier un makefile qui s'imprimait PWDet CURDIRavant +$(MAKE) <subdir>.
Mark K Cowan
27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
momo
la source
12
... sauf si vous avez des espaces dans tous les chemins.
Craig Ringer
9
... sauf si vous avez inclus un autre makefile dans la ligne précédente
vol
1
@robal Yep. C'est ce que je viens de rencontrer. Cela fonctionne très bien si vous n'avez que deux Makefiles (le principal plus un inclus).
sherrellbc
6

J'ai essayé plusieurs de ces réponses, mais sur mon système AIX avec GNU make 3.80, je devais faire certaines choses à l'ancienne.

Il s'avère que lastword, abspathet realpathn'ont pas été ajoutés avant 3,81. :(

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

Comme d'autres l'ont dit, pas le plus élégant car il invoque deux fois un shell, et il a toujours des problèmes d'espace.

Mais comme je n'ai pas d'espace sur mes chemins, cela fonctionne pour moi quelle que soit la façon dont j'ai commencé make:

  • make -f ../wherever/makefile
  • faire -C ../partout
  • faire -C ~ / partout
  • cd ../wherever; faire

Tous me donnent whereverpour current_diret le chemin absolu vers whereverpour mkfile_dir.

Jesse Chisholm
la source
Pour ma part, j'ai compilé gnu-make-3.82 sur aix (5-6-7) sans problème.
Zsigmond Lőrinczy
Oui, en 3.81, les fonctions nécessaires aux anciennes solutions ont été mises en œuvre. Ma réponse est pour les anciens systèmes avec 3.80 ou version antérieure. Malheureusement, au travail, je ne suis pas autorisé à ajouter ou à mettre à niveau des outils sur le système AIX. :(
Jesse Chisholm
5

J'aime la réponse choisie, mais je pense qu'il serait plus utile de la montrer réellement que de l'expliquer.

/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

Production:

+ 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

REMARQUE: la fonction $(patsubst %/,%,[path/goes/here/])est utilisée pour supprimer la barre oblique de fin.

Bruno Bronosky
la source
3

Solution trouvée ici: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

La solution est : $ (CURDIR)

Vous pouvez l'utiliser comme ça:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder
Soma Bini
la source
3
Bienvenue dans Stack Overflow. Pouvez-vous ajouter plus de détails à votre réponse? La question indique que cela $(CURDIR)a déjà été tenté sans succès.
Sean
4
ATTENTION, quick-googlers: Ce n'est pas le répertoire, où se trouve le makefile, mais le répertoire de travail actuel (où a makeété lancé en). C'est, en effet, ce que voulait vraiment OP, mais le titre de la question est faux, donc la question elle-même est incohérente. Donc, c'est une bonne réponse à une question incorrecte. ;)
Sz.
C'est tout à fait faux et une mauvaise réponse redondante
UpAndAdam
2

Voici une ligne pour obtenir le chemin absolu vers votre Makefilefichier en utilisant la syntaxe shell:

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

Et voici la version sans shell basée sur la réponse @ 0xff :

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

Testez-le en l'imprimant, comme:

cwd:
        @echo $(CWD)
Kenorb
la source
1
Une importance capitale! Le deuxième exemple ci-dessus a besoin de l'opérateur d'affectation: = ou bien des choses très étranges et en colère peuvent se produire avec des makefiles complexes (croyez-moi)
EMon
1
La première est une erreur répétée et ne fonctionne pas pour les versions hors arborescence.
Johan Boulé
0

Pour autant que je sache, c'est la seule réponse ici qui fonctionne correctement avec les espaces:

space:= 
space+=

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

Il fonctionne essentiellement en échappant les caractères d'espace en les remplaçant ' 'par '\ 'ce qui permet à Make de l'analyser correctement, puis il supprime le nom de fichier du makefile en MAKEFILE_LISTfaisant une autre substitution de sorte que vous vous retrouvez avec le répertoire dans lequel se trouve makefile. Pas exactement le plus compact chose dans le monde, mais cela fonctionne.

Vous vous retrouverez avec quelque chose comme ça où tous les espaces sont échappés:

$(info CURRENT_PATH = $(CURRENT_PATH))

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

Exemple pour votre référence, comme ci-dessous:

La structure des dossiers peut être la suivante:

enter image description here

Où il y a deux Makefiles, chacun comme ci-dessous;

sample/Makefile
test/Makefile

Voyons maintenant le contenu des Makefiles.

échantillon / Makefile

export ROOT_DIR=${PWD}

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

test / Makefile

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

Maintenant, exécutez l'exemple / Makefile, en tant que;

cd sample
make

PRODUCTION:

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'

L'explication serait que le répertoire parent / home peut être stocké dans l'indicateur d'environnement, et peut être exporté, afin qu'il puisse être utilisé dans tous les makefiles du sous-répertoire.

parasite
la source
2
Ceci obtient le répertoire de travail actuel, pas le répertoire personnel du makefile.
Jesse Chisholm
Ce sont les lignes du Makefile. Maintenant, pendant l'exécution de la commande Makefile, cela obtiendra le chemin d'accès du Makefile. Je comprends que "le répertoire personnel de makefile" se réfère à la même chose, c'est-à-dire le répertoire-chemin dans lequel Makefile s'exécute.
parasrish
@Jesse Chisholm veuillez revisiter. Je l'ai expliqué à l'usage.
parasrish
Une erreur répétée: cela ne fonctionne pas pour les versions hors arborescence. De plus, votre terminal gnome fournit un moyen simple de copier du texte: il suffit de le sélectionner. La rumeur veut qu'Imgur soit en faillite.
Johan Boulé
-2

update 2018/03/05 finnaly j'utilise ceci:


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

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

Il peut être exécuté correctement dans un chemin relatif ou un chemin absolu. Exécuté correctement appelé par crontab. Exécuté correctement dans un autre shell.

montrer l'exemple, 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 /]# 

vieux: j'utilise ceci:

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

Il peut s'afficher correctement s'il est exécuté dans un autre shell et un autre répertoire.

zhukunqian
la source
1
"Il peut afficher correctement s'il est exécuté si un autre shell et un autre répertoire" n'a pas de sens en anglais. Pouvez-vous essayer d'expliquer à nouveau?
Keith M
désolé pour mon pauvre anglais
zhukunqian
Merci pour l'édition, je vois que vous vouliez dire maintenant. Pouvez-vous expliquer ce que cela fait avant de l'essayer? Je ne sais pas comment l'utiliser. Comme ce que tu veux dire par "autre coquille"
Keith M
1
Cette réponse a tellement de mauvaises choses qu'elle ne peut pas être corrigée. Je suggère un retrait simple.
Johan Boulé
-2

Une ligne dans le Makefile devrait suffire:

DIR := $(notdir $(CURDIR))

Thach Van
la source