¿Cómo obtener el directorio relativo actual de su Makefile?

202

Tengo varios Makefiles en directorios específicos de aplicaciones como este:

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

Cada Makefile incluye un archivo .inc en esta ruta un nivel más arriba:

/project1/apps/app_rules.inc

Dentro de app_rules.inc Estoy configurando el destino de donde quiero que se coloquen los binarios cuando se construyan. Quiero que todos los binarios estén en su app_typeruta respectiva :

/project1/bin/app_typeA/

Intenté usar$(CURDIR) , así:

OUTPUT_PATH = /project1/bin/$(CURDIR)

pero en cambio obtuve los binarios enterrados en el nombre completo de la ruta de esta manera: (observe la redundancia)

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

¿Qué puedo hacer para obtener el "directorio actual" de ejecución de modo que pueda saber exactamente app_typeXpara colocar los archivos binarios en sus respectivas carpetas de tipos?

boltup_im_coding
fuente

Respuestas:

272

La función de shell.

Puede utilizar shellla función: current_dir = $(shell pwd). O shellen combinación con notdir, si usted no tiene por qué ruta absoluta: current_dir = $(notdir $(shell pwd)).

Actualizar.

La solución dada solo funciona cuando se ejecuta makedesde el directorio actual del Makefile.
Como señaló @Flimm:

Tenga en cuenta que esto devuelve el directorio de trabajo actual, no el directorio principal del Makefile.
Por ejemplo, si ejecuta cd /; make -f /home/username/project/Makefile, la current_dirvariable será /, no /home/username/project/.

El siguiente código funcionará para Makefiles invocados desde cualquier directorio:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
Nikolai Popov
fuente
55
Eso no funciona. Es el mismo problema. pwd imprime el nombre completo de la ruta, solo quiero el nombre de la carpeta actual en la que estoy.
boltup_im_coding
66
Necesita que el valor se expanda inmediatamente, por lo que se debe usar el operador: =. Como en mkfile_path: = y current_dir = (de lo contrario, los valores del lado derecho podrían evaluarse más tarde cuando MAKEFILE_LIST haya cambiado después de que se hayan incluido otros Makefiles
Tom Gruner
10
Sé que esta es una vieja pregunta, pero me encontré y pensé que podría ser útil. Esto tampoco funcionará cuando haya un espacio en la ruta relativa del archivo MAKE. Específicamente, $(lastword "$(MAKEFILE_LIST)")de un makefile en el camino "Sub Directory/Makefile"te dará "Directory/Makefile". No creo que haya alguna forma de evitar esto, ya que $(MAKEFILE_LIST)con dos archivos MAKE le da una sola cadena "Makefile One Makefile Two, que no puede manejar espacios
mrosales
2
Eso current_dirno me funciona. $(PWD)hace y es mucho más simple.
CaTx
3
"solution only works when you are running make from the Makefile's current directory"es una manera suave de decir "realmente no funciona". Pondría la última pieza en la parte superior de la respuesta.
Victor Sergienko
138

Como tomado de aquí ;

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

Aparece como;

$ cd /home/user/

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

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

Espero que esto ayude

soñoliento
fuente
3
En mi humilde opinión, sería mejor utilizar la función dirde shell dirnamecreación interna en lugar de obtener el nombre de ruta con el /carácter final .
Serge Roussak
3
debido a la reducción de dependencias de herramientas externas. Es decir, ¿qué pasaría si hubiera un alias del comando dirname en algún sistema? ..
Serge Roussak
66
Esto casi funcionó para mí, pero DIR tenía un espacio en blanco líder, por lo que un cambio menor hizo que funcionara perfectamente:DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
Thorsten Lorenz,
9
Para hacer referencia al archivo MAKE actual , el uso de $MAKEFILE_LISTdebe preceder a cualquier includeoperación ( docs ).
nobar
2
Debería usar comillas, al menos alrededor del argumento para dirname, en caso de que haya espacios en la ruta.
Nobar
46

Si está utilizando GNU make, $ (CURDIR) es en realidad una variable incorporada. Es la ubicación donde el Makefile reside en el directorio de trabajo actual, que es probablemente donde está el Makefile, pero no siempre .

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

Consulte el Apéndice A Referencia rápida en http://www.gnu.org/software/make/manual/make.html

Tzunghsing David Wong
fuente
18
Del manual de GNU Make (página 51): "cuando GNU make se inicia (después de haber procesado las opciones -C) establece la variable CURDIR en la ruta del directorio de trabajo actual". No es la ubicación de dónde se encuentra el Makefile, aunque podrían ser lo mismo.
Simon Peverett
44
Voto negativo porque la respuesta no es técnicamente correcta, como señaló Simon Peverett en el comentario anterior.
Subfuzion
55
@SimonPeverett: La expresión entre paréntesis significa el "directorio de trabajo actual DESPUÉS de que -C opte por aplicar". A saber, $ (CURDIR) da exactamente los mismos resultados para "make -C xxx" y "cd xxx; make". También elimina el rastro /. Intenté empatar con gmakev3.81. Pero tienes razón si makese llama como make -f xxx/Makefile.
Verdaderamente el
CURDIRfunciona para mí donde PWDno lo hizo. Hice mkdir -p a/s/dentonces en cada carpeta tenía un makefile que imprimió PWDy CURDIRantes +$(MAKE) <subdir>.
Mark K Cowan
27
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
momo
fuente
12
... a menos que tenga espacios en cualquier camino.
Craig Ringer
9
... a menos que haya incluido algún otro archivo MAKE en la línea anterior
robal
1
@robal Sí. Eso es lo que acabo de encontrar. Esto funciona muy bien si solo tiene dos Makefiles (el principal más uno incluido).
sherrellbc
6

Intenté muchas de estas respuestas, pero en mi sistema AIX con gnu make 3.80 necesitaba hacer algunas cosas de la vieja escuela.

Resulta que lastword, abspathy realpathno se agregaron hasta 3.81. :(

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

Como otros han dicho, no es el más elegante, ya que invoca un caparazón dos veces, y todavía tiene problemas de espacio.

Pero como no tengo espacios en mi camino, funciona para mí, independientemente de cómo empecé make:

  • make -f ../wherever/makefile
  • make -C ../wherever
  • hacer -C ~ / donde sea
  • cd ../wherever; hacer

Todos me dan whereverpor current_diry el camino absoluto a whereverpor mkfile_dir.

Jesse Chisholm
fuente
Por mi parte, he compilado gnu-make-3.82 en aix (5-6-7) sin problemas.
Zsigmond Lőrinczy
Sí, en 3.81 se implementaron las funciones necesarias para las soluciones anteriores. Mi respuesta es para sistemas más antiguos con 3.80 o anterior. Lamentablemente, en el trabajo no puedo agregar ni actualizar herramientas al sistema AIX. :(
Jesse Chisholm
5

Me gusta la respuesta elegida, pero creo que sería más útil mostrarla realmente funcionando que explicarla.

/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

Salida:

+ 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 función $(patsubst %/,%,[path/goes/here/])se usa para quitar la barra inclinada final.

Bruno Bronosky
fuente
3

Solución encontrada aquí: https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

La solución es : $ (CURDIR)

Puedes usarlo así:

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder
Soma Bini
fuente
3
Bienvenido a Stack Overflow. ¿Puedes agregar más detalles a tu respuesta? La pregunta dice que $(CURDIR)ya se intentó sin éxito.
Sean
44
ADVERTENCIA, quick-googlers: este no es el directorio, donde está el archivo MAKE, sino el directorio de trabajo actual (donde makese inició). Es, de hecho, lo que OP realmente quería, pero el título de la pregunta es falso, por lo que la pregunta en sí es inconsistente. Entonces, esta es una respuesta correcta a una pregunta incorrecta. ;)
Sz.
Esto es completamente incorrecto y una respuesta incorrecta redundante
UpAndAdam
2

Aquí hay una línea para obtener la ruta absoluta a su Makefilearchivo usando la sintaxis de shell:

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

Y aquí hay una versión sin shell basada en la respuesta @ 0xff :

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

Pruébelo imprimiéndolo, como:

cwd:
        @echo $(CWD)
kenorb
fuente
1
¡Importancia crítica! El segundo ejemplo encima de las necesidades del operador: = o bien algunas cosas muy extrañas y de ira puede ocurrir con los archivos make complejos (confía en mí)
Emon
1
El primero es un error repetido y no funciona para compilaciones fuera del árbol.
Johan Boulé el
0

Hasta donde sé, esta es la única respuesta aquí que funciona correctamente con espacios:

space:= 
space+=

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

En esencia, funciona al escapar los caracteres de espacio mediante la sustitución ' 'de '\ 'los cuales permite Make a analizar correctamente, y entonces se elimina el nombre de archivo del archivo MAKE en MAKEFILE_LISThaciendo otra sustitución por lo que se queda con el directorio que está en makefile. No es exactamente el más compacto cosa en el mundo pero funciona.

Terminarás con algo como esto donde se escapan todos los espacios:

$(info CURRENT_PATH = $(CURRENT_PATH))

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

Ejemplo para su referencia, como a continuación:

La estructura de la carpeta puede ser como:

ingrese la descripción de la imagen aquí

Donde hay dos Makefiles, cada uno de los siguientes;

sample/Makefile
test/Makefile

Ahora, veamos el contenido de los Makefiles.

muestra / Makefile

export ROOT_DIR=${PWD}

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

prueba / Makefile

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

Ahora, ejecute la muestra / Makefile, como;

cd sample
make

SALIDA:

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'

Explicación, sería que el directorio padre / inicio se puede almacenar en el indicador de entorno y se puede exportar, de modo que se pueda usar en todos los archivos MAKE del subdirectorio.

parasitar
fuente
2
Esto obtiene el directorio de trabajo actual, no el directorio de inicio del archivo MAKE.
Jesse Chisholm
Estas son las líneas en el Makefile. Ahora, a medida que se ejecuta el comando Makefile, obtendrá la ruta en la que se encuentra el Makefile. Entiendo que "directorio de inicio de makefile" se refiere al mismo, es decir, la ruta de directorio en la que se ejecuta Makefile.
parasire el
@Jesse Chisholm vuelve a visitar. He explicado con el uso.
Parásito
Un error repetido: no funciona para compilaciones fuera del árbol. Además, su terminal gnome proporciona una forma simple de copiar texto: simplemente selecciónelo. El rumor dice que Imgur es insoportable.
Johan Boulé
-2

actualización 2018/03/05 finalmente uso esto:


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

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

Se puede ejecutar correctamente en ruta relativa o ruta absoluta. Ejecutado correcto invocado por crontab. Ejecutado correcto en otro shell.

muestre el ejemplo, 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 /]# 

viejo: yo uso esto:

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

Puede mostrarse correcto si se ejecuta en otro shell y otro directorio.

zhukunqian
fuente
1
"Puede mostrarse correcto si se ejecuta si otro shell y otro directorio" no tiene sentido en inglés. ¿Puedes intentar explicar de nuevo?
Keith M
lo siento por mi pobre inglés
zhukunqian
Gracias por la edición, veo que te refieres a IN ahora. ¿Puedes explicar qué hace esto antes de que lo intente? No estoy seguro de cómo usarlo. Como qué quieres decir con "otro caparazón"
Keith M
1
Esta respuesta tiene tantas cosas incorrectas que no se puede solucionar. Sugiero una eliminación simple.
Johan Boulé
-2

Una línea en el Makefile debería ser suficiente:

DIR := $(notdir $(CURDIR))

Thach Van
fuente