2009-11-24 14 views
227

Por ejemplo, tengo algo como esto en mi makefile:¿Cómo escribo el comando 'cd' en un archivo MAKE?

all: 
    cd some_directory 

Pero cuando he escrito make sólo vi 'cd some_directory', al igual que en el comando echo.

+3

No está claro lo que quiere hacer, pero, en mi experiencia con 'make', nunca quise cambiar el directorio de esta manera. Tal vez deberías probar otro enfoque para tu solución? –

+0

Es un error novato común creer que su directorio es importante. Para la mayoría de las cosas no lo es; 'cd dir; cmd file' casi siempre se puede expresar de manera más útil como 'dir de cmd/file'. – tripleee

+8

Es un error novato común creer que su actual directorio de trabajo es inconsecuente. Muchos programas, especialmente los scripts de shell, están escritos con un valor específico de '.' en mente. Es cierto que la mayoría de las herramientas están diseñadas de tal manera que no necesita cambiar su pwd por ellas. Pero esto no siempre es así, y no creo que sea una buena idea llamarlo un "error" al creer que su directorio puede ser importante. –

Respuesta

408

En realidad, se 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 se trabaja.

Si desea realizar más tareas dentro de some_directory, debe agregar un punto y coma y anexar los otros comandos también. Tenga en cuenta que no puede usar nuevas líneas, ya que son interpretadas por make como el final de la regla, por lo que las líneas nuevas que use para mayor claridad deben ser escapadas por una barra diagonal inversa.

Por ejemplo:

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

Tenga en cuenta también que la coma es necesaria entre todos los comandos a pesar de que se agrega una barra invertida y un salto de línea. Esto se debe al hecho de que toda la cadena es analizada como una sola línea por el shell. Como se indicó 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 importante cuando se hace trabajo destructivo, tales como limpieza, ya que de lo contrario se le destruye las cosas mal, debería el cd fallar por cualquier razón.

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 comandos para esto por lo que no tiene que llamar cd a sí mismo, por lo que su estado se vería así

all: 
     $(MAKE) -C some_dir all 

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

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

+6

Bueno, no * siempre *. Para suprimir el eco, simplemente ponga @ al comienzo de la línea. – Beta

+1

@Beta: bueno, sí, y un prefijo de tablero ignora también el estado del error. Tal vez me dejé llevar un poco, quería señalar el hecho de que make hace que echos el 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

+33

Dos liendres: 1. Los comandos deberían estar unidos por '&&', porque con ';' si el directorio no existe y el 'cd' falla, el shell seguirá ejecutando el resto de los comandos en el directorio actual , que puede causar misteriosos mensajes de "archivo no encontrado" para las compilaciones, bucles infinitos al invocar make o desastres para reglas como 'clean :: cd dir; rm -rf * '. 2. Al invocar subhabilidades, llame '$ (MAKE)' en lugar de 'make' para que [las opciones se transmitan correctamente] (http://www.gnu.org/software/make/manual/make.html # MAKE-Variable). – andrewdotn

5

¿Qué quieres que haga una vez que llega? 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, se puede hacer algo como:

BIN=/bin 
foo: 
    $(shell cd $(BIN); ls) 
+3

Por qué el '$ (shell ...)' cuando 'cd $ (BIN); ls' o 'cd $ (BIN) && ls' (como señaló @andrewdotn) sería suficiente. – vapace

20

He aquí un truco lindo para hacer frente a los directorios y hacer.En lugar de usar cadenas multilínea, o "cd"; en cada comando, defina una simple función chdir como tan:

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 llamar en su norma así:

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

Incluso puede hacer lo siguiente:

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

"Lindo"? Más como suficiente cuerda para dispararse en el pie. – tripleee

+0

¿Este directorio actual está configurado para los comandos justo en esa regla, o para todas las reglas ejecutadas posteriormente? Además, ¿habrá alguna variación de este trabajo en Windows? – user117529

+4

Esto, por supuesto, se rompe para la ejecución en paralelo ('-jn'), que es el punto total de _make_ realmente. – bobbogo

36

a partir de GNU make 3.82, se puede usar el objetivo especial .ONESHELL para ejecutar todas las líneas de recetas en una sola instancia de la cáscara:

  • Nueva objetivo especial: .ONESHELL instruye hacer para invocar una sola instancia de la cáscara y dotarla de la receta completa, independientemente del número de líneas que contiene. [...]

Ejemplo:

.ONESHELL: 
all: 
    cd ~/some_dir 
    pwd # this line is correctly run in ~/some_dir if cd succeeded 
+4

Simplemente tenga en cuenta que 'pwd' en sí funciona, así como' \ 'pwd \' '(con barras), pero' $ (shell pwd) 'y' $ (PWD) 'todavía devolverá el directorio antes de hacer el' cd 'comando, por lo que no puede usarlos directamente. – anol

+2

Sí porque la expansión de variables y funciones se realiza antes de ejecutar los comandos por make, mientras que 'pwd' y' \ 'pwd \' 'son ejecutados por el propio shell. – Chnossos

+0

Sí, pero nadie puede instalar 3.82 porque introduce cambios de compatibilidad de última hora :( – CpILL

1

De esta manera:

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!

Cuestiones relacionadas