¿Cómo escribo el comando 'cd' en un archivo MAKE?

354

Por ejemplo, tengo algo como esto en mi archivo MAKE:

all:
     cd some_directory

Pero cuando makeescribí solo vi 'cd some_directory', como en el echocomando.

Davit Siradeghyan
fuente
55
No está claro qué quiere hacer, pero, según mi experiencia make, nunca quise cambiar el directorio de esta manera. ¿Quizás debería intentar otro enfoque para su solución?
P Shved
2
Es un error común de los novatos creer que su directorio es importante. Para la mayoría de las cosas no lo es; cd dir; cmd filecasi siempre se puede expresar más útilmente como cmd dir/file.
tripleee
34
Es un error común de los novatos creer que su directorio de trabajo actual es intrascendente. Muchos programas, especialmente los scripts de shell, se escriben teniendo en cuenta un valor específico .. Es cierto que la mayoría de las herramientas están diseñadas de tal manera que no necesita cambiar su pwd por ello. Pero esto no siempre es cierto, y no creo que sea una buena idea llamarlo "error" creer que su directorio puede ser importante.
Evelyn Kokemoor
Consejo: si su comando cd dice "No existe el fichero o directorio", incluso cuando el directorio (relativa) hace existir, comprobación de que la variable de entorno CDPATH está vacía o incluye "". Make ejecuta comandos con "sh", que solo encontrará una ruta relativa a través de CDPATH si está configurado. Esto contrasta con bash, que lo intentará. antes de consultar CDPATH.
Denis Howe

Respuestas:

621

En realidad, está ejecutando el comando, cambiando el directorio a some_directory, sin embargo, esto se realiza en un shell de subproceso y no afecta ni a make ni al shell desde el que está trabajando.

Si está buscando realizar más tareas dentro de some_directory , debe agregar un punto y coma y agregar también los otros comandos. Tenga en cuenta que no puede usar las nuevas líneas, ya que son interpretadas por make como el final de la regla, por lo que cualquier línea nueva que use para mayor claridad debe escapar mediante una barra invertida.

Por ejemplo:

all:
        cd some_dir; echo "I'm in some_dir"; \
          gcc -Wall -o myTest myTest.c

Tenga en cuenta también que el punto y coma es necesario entre cada comando aunque agregue una barra diagonal inversa y una nueva línea. Esto se debe al hecho de que el shell analiza toda la cadena como una sola línea. Como se señaló en los comentarios, debe usar '&&' para unir comandos, lo que significa que solo se ejecutarán si el comando anterior fue exitoso.

all:
        cd some_dir && echo "I'm in some_dir" && \
          gcc -Wall -o myTest myTest.c

Esto es especialmente crucial cuando se realiza un trabajo destructivo, como la limpieza, ya que de lo contrario destruiría las cosas incorrectas, en caso de que cdfalle por cualquier motivo.

Sin embargo, un uso común es llamar a make en el subdirectorio, que es posible que desee examinar. Hay una opción de línea de comando para esto para que no tenga que llamarse a cdsí mismo, por lo que su regla se vería así

all:
        $(MAKE) -C some_dir all

que cambiará some_diry ejecutará Makefileallí con el objetivo "todos". Como práctica recomendada, use en $(MAKE)lugar de llamar makedirectamente, ya que se encargará de llamar a la instancia de make correcta (si, por ejemplo, usa una versión de make especial para su entorno de compilación), así como proporcionar un comportamiento ligeramente diferente al ejecutar usando ciertos interruptores, como -t.

Para el registro, haga siempre eco el comando que ejecuta (a menos que se suprima explícitamente), incluso si no tiene salida, que es lo que está viendo.

falstro
fuente
8
Pues no siempre . Para suprimir el eco, simplemente ponga @ al comienzo de la línea.
Beta
2
@Beta: bueno, sí, y un prefijo de guión también ignora el estado del error. Tal vez me dejé llevar un poco, quería señalar el hecho de que make hace eco del comando, independientemente de qué tipo de comando sea. Y en este caso, es un comando sin salida, lo que hace que el eco parezca aún más extraño para alguien que no está familiarizado con make.
falstro
46
Dos liendres: 1. Los comandos realmente deberían estar unidos &&, porque ;si el directorio no existe y cdfalla, el shell seguirá ejecutando el resto de los comandos en el directorio actual, lo que puede causar cosas como misterioso "archivo no encontrado "mensajes para compilaciones, bucles infinitos al invocar make, o desastre para reglas como clean:: cd dir; rm -rf *. 2. Al invocar sub-marcas, llame en $(MAKE)lugar de makepara que las opciones se transmitan correctamente .
andrewdotn
2
@perreal, generalmente defino una regla de patrón de la siguiente manera: %-recursive:con el cuerpo: @T="$@";$(MAKE) -C some_dir $${T%-*}(generalmente también tengo un bucle for, para recorrer una lista de subdirecciones, $${T%-*}es una expansión bash que elimina la -recursiveparte del nombre del objetivo) y luego definir objetivos a corto explícita a mano (y .PHONY) para cada uno, como all: all-recursive, check: check-recursive, clean: clean-recursive.
falstro
1
@ChristianStewart, cierto, como se mencionó en los comentarios 2 y 3.
falstro
95

A partir de GNU make 3.82 (julio de 2010), puede usar el .ONESHELLobjetivo especial para ejecutar todas las líneas de recetas en una sola instanciación del shell (énfasis en negrita mío):

  • Nuevo objetivo especial: .ONESHELLindica a make que invoque una sola instancia del shell y le proporcione la receta completa , independientemente de cuántas líneas contenga. [...]
.ONESHELL: # Only applies to all target
all:
    cd ~/some_dir
    pwd # Prints ~/some_dir if cd succeeded
Chnossos
fuente
66
Ten en cuenta que pwden sí funciona, así como `pwd`(con acentos abiertos), pero $(shell pwd)y $(PWD)se sigue devolver el directorio antes de hacer el cdcomando, por lo que no se puede utilizar directamente.
Anol
3
Sí, porque la expansión de variables y funciones se realiza antes de ejecutar los comandos por make, mientras que pwdy `pwd`es ejecutada por el propio shell.
Chnossos
2
No todos trabajan en makefiles heredados, e incluso esta respuesta es sobre saber que existe esta posibilidad.
Chnossos
1
Esta puede ser una opción molesta / peligrosa porque solo el último comando para un objetivo puede causar una falla (se ignorarán las fallas de comandos anteriores), y probablemente nadie que trabaje con usted esperará eso.
tobii
1
Conjunto SHELLFLAGS en -e -cy el shell saldrá en el primer comando que falla.
Timothy Baldwin
21

Aquí hay un lindo truco para lidiar con directorios y hacer. En lugar de usar cadenas multilínea, o "cd;" en cada comando, defina una función chdir simple de la siguiente manera:

CHDIR_SHELL := $(SHELL)
define chdir
   $(eval _D=$(firstword $(1) $(@D)))
   $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL))
endef

Entonces todo lo que tiene que hacer es llamarlo en su regla así:

all:
          $(call chdir,some_dir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c

Incluso puedes hacer lo siguiente:

some_dir/myTest:
          $(call chdir)
          echo "I'm now always in some_dir"
          gcc -Wall -o myTest myTest.c
JoeS
fuente
41
"Lindo"? Más como suficiente cuerda para dispararte en el pie.
tripleee
1
¿Este directorio actual está configurado para los comandos solo en esa regla, o para todas las reglas ejecutadas posteriormente? Además, ¿alguna variación de este trabajo en Windows?
user117529
8
Por supuesto, esto se rompe para la ejecución paralela ( -jn), que es realmente el objetivo de make realmente.
bobbogo
55
Este es un mal truco. Si alguna vez tiene que recurrir a tal cosa, no está utilizando Makefiles para lo que son.
gatopeich
44
No estoy en desacuerdo de que es un mal truco, eso es seguro. Pero demuestra algunas de las cosas malvadas que puedes hacer.
JoeS
9

¿Qué quieres que haga una vez que llegue allí? Cada comando se ejecuta en una subshell, por lo que la subshell cambia de directorio, pero el resultado final es que el siguiente comando todavía está en el directorio actual.

Con GNU make, puedes hacer algo como:

BIN=/bin
foo:
    $(shell cd $(BIN); ls)
Nadir SOUALEM
fuente
55
Por qué el $(shell ...)cuándo cd $(BIN); lso cd $(BIN) && ls(como señaló @andrewdotn) sería suficiente.
vapace
1

Para cambiar dir

foo: 
    $(MAKE) -C mydir

multi:
    $(MAKE) -C / -C my-custom-dir   ## Equivalent to /my-custom-dir
jackotonye
fuente
-6

Me gusta esto:

target:
    $(shell cd ....); \
    # ... commands execution in this directory
    # ... no need to go back (using "cd -" or so)
    # ... next target will be automatically in prev dir

¡Buena suerte!

Cubo de rubik
fuente
1
No, esto está mal. Específicamente, $(shell cd ....)se ejecuta cuando se analiza inicialmente el Makefile, no cuando se ejecuta esta receta en particular.
tripleee
@triplee No del todo: $(shell)se expande solo cuando make decide construir el objetivo . Si make nunca necesita la receta, no la ampliará.
bobbogo