Creación de software web con Make

Más sobre Marcos ↬
Mi esperanza es que veas que Make es una herramienta de automatización/orquestación que puede usarse en lugar de otras herramientas de compilación modernas y te ayudará a fortalecer tu comprensión y capacidad para usar el entorno de terminal/shell (lo cual es una gran ventaja en mi opinión, y ayuda a abrir muchas vías de progresión técnica). Se han escrito libros completos sobre el tema de Make y la escritura de Makefiles, por lo que Mark dejará que usted investigue más allá de esta publicación si logra despertar su interés.
La mayoría de los desarrolladores web utilizan algún tipo de herramienta de compilación hoy en día. No me refiero al software de integración continua como Jenkins CI (un sistema de compilación muy popular), sino al software de nivel inferior que utiliza para adquirir dependencias y construir sus aplicaciones.
Hay una vertiginosa variedad de opciones para elegir:
- Apache Ant (basado en XML)
- Rastrillo (a base de Ruby)
- Grunt (basado en JS)
- Gulp (basado en JS)
- Brócoli (basado en JS)
- NPM (basado en JS)
- Buenos y viejos scripts de shell (aunque no hay una orquestación real a su alrededor)
Sin embargo, la herramienta de compilación que quiero ver con más detalle aquí es la abuela de todas: Make .
Diseñado originalmente en 1976, Make es la utilidad de compilación líder para Unix, Linux y Mac OS X. Lo más probable es que la mayoría de las computadoras en las que inicie sesión ya la tengan instalada y disponible para usar. Esto realmente reduce el punto de entrada de la configuración (que para otras herramientas enumeradas anteriormente puede ser tedioso y propenso a errores, con la excepción de los scripts de shell, ya que el shell es algo inherentemente disponible para todos los sistemas).
Mi esperanza es que veas que Make es una herramienta de automatización/orquestación que puede usarse en lugar de otras herramientas de compilación modernas y te ayudará a fortalecer tu comprensión y capacidad para usar el entorno de terminal/shell (lo cual es una gran ventaja en mi opinión, y ayuda a abrir muchas vías de progresión técnica).
No podría esperar cubrir todos los aspectos de lo que ofrece Make, así que no considere erróneamente esta publicación como algo remotamente exhaustivo. Se han escrito libros completos sobre el tema de Make y la escritura de Makefiles, así que te dejaré investigar más allá de esta publicación si logré despertar tu interés.
Permítanme comenzar haciendo referencia al sitio web de GNU para conocer su definición de qué es y qué hace Make:
"GNU Make es una herramienta que controla la generación de ejecutables y otros archivos no fuente de un programa a partir de los archivos fuente del programa"
Make se basa en la definición de un Makefile y que consta de un conjunto de instrucciones para crear su software. Si ha utilizado otro sistema de compilación, como Grunt , notará que la mayoría usa una convención de nomenclatura tomada de Make (por ejemplo, Gruntfile ).
El objetivo de un Makefile (en el sentido tradicional) es crear un programa; aunque Make se puede utilizar para ejecutar cualquier tipo de tarea y, por lo tanto, no se limita a compilar software. Al igual que otras herramientas de compilación basadas en JavaScript no se limitan a crear aplicaciones JavaScript, pueden manejar la mayoría de las tareas que desea ejecutar (tal vez compilar CSS u optimizar imágenes).
Descubrirá que Make se distribuye ampliamente y probablemente ya esté en su computadora. Por ejemplo, estoy usando una computadora portátil Apple con Mac OS X instalado. Si ejecuto el siguiente comando:
make --version
Recibo la siguiente respuesta:
GNU Make 3.81Copyright (C) 2006 Free Software Foundation, Inc.This is free software; see the source for copying conditions.There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR APARTICULAR PURPOSE.This program built for i386-apple-darwin11.3.0
Lo que significa que ya tengo el make
comando disponible y puedo empezar a escribir mi Makefile de inmediato.
Ejemplo sencillo
Consideremos un requisito estándar del proyecto, que es ejecutar un linter como JSHint sobre un archivo JavaScript (es decir, analizar el código para detectar problemas de formato y errores y advertencias generales).
Nota : como se mencionó anteriormente, tradicionalmente Make se usa para compilar archivos de programa. En este caso, he optado por un ejemplo simple que no requiere compilación, sino que debería demostrar cómo Make es realmente útil para muchos tipos diferentes de tareas.
Imagine que tiene un archivo test.js y contiene el siguiente contenido:
function foo() { bar = "baz"}
Si tuviéramos que ejecutar el comando jshint test.js --show-non-errors
(suponiendo que tenga instalada la versión CLI de JSHint), deberíamos ver algo como lo siguiente:
test.js: line 2, col 14, Missing semicolon.1 errortest.js : Implied globals: bar: 2 Unused Variables: foo(1),
Entonces podemos ver en este resultado que JSHint nos advierte que tenemos una función foo
que no se está utilizando y una variable que parece haber sido declarada globalmente; pero también indica que tenemos un error en nuestro programa: nos falta un punto y coma en la línea 2 de nuestro archivo JavaScript.
Muy bien, entonces, ¿cómo podemos llevar este ejemplo más allá y automatizar el proceso de análisis (que se volverá más complicado a medida que nuestra aplicación crezca en tamaño y características) usando la utilidad Make?
Primero necesitamos crear un Makefile . A continuación se muestran los contenidos del Makefile que voy a utilizar para demostrar cómo funciona Make (explicaré la estructura del archivo en la siguiente sección):
lint jshint *.js --show-non-errors
Nota : Los Makefiles usan tabulaciones en lugar de espacios, por lo que si su editor está configurado para reemplazar espacios con tabulaciones, es posible que las cosas no funcionen como se esperaba.
Para ejecutar el Makefile anterior, necesitaríamos usar el make
comando Shell. Esto por sí solo ejecutará el primer objetivo que encuentre (también conocido como objetivo predeterminado ), que en este caso es lint
. También puedes ser más explícito y especificar el objetivo exacto que deseas ejecutar proporcionando el nombre del objetivo al make
comando, así:
make lint
Ejecutar el comando anterior es lo mismo que ejecutar:
jshint test.js --show-non-errors
También habrás notado que utilizamos un comodín *
para indicar varios archivos JavaScript a la vez.
En este caso, usar Make significa que es más fácil recordar comandos específicos para tareas comunes como esta. Tener que recordar el formato del comando de shell JSHint ahora no es un problema, especialmente considerando que estoy usando el ejemplo más básico de ejecución de JSHint, y el comando de shell en sí puede volverse mucho más largo y difícil de manejar.
El Makefile también actúa como un archivo documentado que ahora se puede enviar al control de versiones, lo que significa que ahora tenemos un registro del paso de compilación. Ambos puntos se vuelven aún más importantes a medida que los pasos de compilación/construcción se vuelven cada vez más complicados, lo que sucederá a medida que su aplicación o sistema de software crezca y evolucione naturalmente.
Nota : si su Makefile está en un directorio diferente, puede pasar su ubicación al make
comando usando la -f
bandera de esta manera:make -f makefile
La convención para escribir Makefiles es tener el comando predeterminado (su punto de entrada) en la parte superior del archivo y hacer que Make procese los comandos de arriba hacia abajo. Sin embargo, no es necesario que hagas esto (como verás, no me he preocupado mucho con los ejemplos de esta publicación) y eres libre de poner tus reglas en el orden que tenga sentido para ti. Pero tenga en cuenta que cuando llame al comando Crear, querrá especificar el objetivo específico si no es el predeterminado.
Terminología
Hay tres frases clave que debes tener en cuenta cuando hablas de un Makefile:
- Normas
- Objetivos
- Requisitos previos
El siguiente fragmento demuestra la estructura básica de un Makefile:
target: prereq1 prereq2 commands
Puede ver que tenemos: un único objetivo (esto es a lo que hacemos referencia cuando ejecutamos el comando make target
); un conjunto de dependencias (es decir, requisitos previos); y un comando para ejecutar (por ejemplo jshint test.js --show-non-errors
). Toda esta estructura se denomina colectivamente "regla" y un Makefile normalmente se compone de varias reglas.
Requisitos previos
Los requisitos previos son las dependencias del objetivo. Lo que esto significa es que el objetivo no se puede construir con éxito sin que primero se resuelvan las dependencias.
Imaginemos que estamos compilando Sass en CSS. Un ejemplo de Makefile (que veremos con más detalle en breve) podría verse así:
compile: foo.scss sass foo.scss foo.css
En el ejemplo anterior especificamos el requisito previo como foo.scss
; lo que significa que Make buscará un destino llamado foo.scss
o esperará que exista un archivo en la estructura de directorio actual.
No tenemos un objetivo nombrado foo.scss
y, por lo tanto, si ese archivo tampoco existiera, entonces no podríamos resolver la dependencia y, posteriormente, la regla fallaría (si no puede resolver la dependencia, entonces el comando en la regla lo hará). t ser ejecutado).
Cómo decide qué hacer
Cómo y por qué Make decide qué hacer cuando ejecutas make target
es muy importante, ya que te ayudará a comprender las implicaciones de rendimiento de ciertas tareas. La regla general para Make es bastante simple: si el objetivo (o cualquiera de sus archivos de requisitos previos) está desactualizado o falta, entonces se ejecutarán los comandos para ese objetivo.
Make utiliza la marca de tiempo de modificación para evitar el procesamiento duplicado. Si la marca de tiempo de los archivos dependientes es anterior a la salida resultante, ejecutar Make no hará nada. Por lo tanto, puede obligar a Make a recompilar un archivo simplemente usando el touch
comando en los archivos relevantes.
Nota : si desea ver qué ejecutará Make sin que realmente haga nada, ejecute el make
comando como lo haría normalmente, pero asegúrese de incluir la -n
bandera. Esto hará que Make imprima todos los comandos que se ejecutarían, incluidos los comandos recopilados a partir de cualquier requisito previo especificado.
Variables automáticas
Consideremos otro ejemplo en el que queremos compilar una hoja de estilo Sass en CSS:
compile: foo.scss sass foo.scss foo.css
Tenemos una ligera duplicación aquí, la referencia a foo.scss . Podemos limpiar esto un poco usando algunas variables especiales que proporciona Make (también conocidas como variables automáticas). Específicamente para el problema que queremos resolver, usaremos la $
variable automática.
Cuando compile
se ejecuta el objetivo, la $
variable hará referencia al primer requisito previo de la lista, lo que simplificará el ejemplo y le evitará tener que repetirlo. El siguiente ejemplo demuestra cómo se ve esto:
compile: foo.scss sass $ foo.css
Esto es bueno porque eliminamos un valor codificado e hicimos nuestro código un poco más flexible. Pero ¿qué pasa si tenemos múltiples dependencias?
Supongamos que tenemos tres archivos foo.txt , bar.txt y baz.txt . Podemos usar una combinación de la $^
variable (que nos proporciona todas las dependencias/requisitos previos como una lista) y un pequeño fragmento de código de shell Bash estándar (los comandos Make son, en última instancia, scripts de shell estructurados con azúcar sintáctico adicional) para recorrer la lista de dependencias proporcionada. .
El siguiente ejemplo demuestra cómo se podría escribir esto:
list: foo.txt bar.txt baz.txt for i in $^; do echo "Dependency: $$i"; done
La ejecución make list
daría como resultado la siguiente respuesta:
for i in foo.txt bar.txt baz.txt; do echo "Dependency: $i"; doneDependency: foo.txtDependency: bar.txtDependency: baz.txt
Nota : debido a que los Makefiles tienen su propia sintaxis especial, el uso de $
entrará en conflicto al escribir nuestro script de shell (que también tiene su propia sintaxis especial $
). Esto significa que si queremos usar el carácter del dólar y no que sea específico de Makefile, entonces tenemos que escaparlo usando otro dólar. Entonces, en lugar de escribir $i
, lo cual funciona bien en el contexto de un script de shell normal, tuvimos que escribir $$i
.
Veremos algunas variables automáticas diferentes a lo largo de esta publicación, pero mientras tanto, consulte la lista de referencia rápida a continuación para conocer algunas de las más útiles:
$
: primer requisito previo$^
: lista de requisitos previos$?
: lista de requisitos previos que han cambiado$@
: nombre del objetivo$*
: el valor de un marcador de posición de destino
La referencia completa de variables automáticas está disponible en el sitio web de GNU Make.
Más adelante en esta publicación revisaremos este for
ejemplo de bucle y demostraremos una forma más idiomática de lograr el resultado que queremos.
Comandos
Vale la pena tener en cuenta que cada comando proporcionado dentro de la regla general se considera un contexto de shell independiente. Esto significa que si exporta una variable de entorno de shell en un comando, no estará disponible en el siguiente comando. Una vez que finaliza el primer comando, se genera un shell nuevo para el siguiente comando, y así sucesivamente.
También notarás que al ejecutar Make, imprimirá las instrucciones del comando antes de ejecutarlas. Esto se puede desactivar de tres maneras. Puedes ejecutar Make con la -s
bandera, que silenciará cualquier salida; o puedes usar la @
sintaxis antes del comando en sí, así:
list: foo.txt bar.txt baz.txt @for i in $^; do echo "Dependency: $$i"; done
La tercera forma de silenciar la salida es utilizar la .SILENCE
bandera. El siguiente fragmento demuestra cómo silenciar tres objetivos foo
: bar
y baz
:
.SILENT: foo bar baz
Nota : ¡desafortunadamente, silenciar la salida también significa silenciar cualquier error!
Al igual que las secuencias de comandos de shell, si tiene un comando que es más complicado de lo que cabe en una sola línea, entonces (al menos por razones de legibilidad) deberá escribirlo en varias líneas y evitar los saltos de línea. usando el personaje, como lo demuestra el siguiente ejemplo:
list: foo.txt bar.txt baz.txt for i in $^; do echo "Dependency: $$i"; done
Objetivos como requisitos previos
Hasta ahora nuestros requisitos previos han sido archivos físicos que ya existían. Pero, ¿qué sucede si primero necesita crear dinámicamente los archivos a través de otros destinos? Make te permite especificar objetivos como dependencias, por lo que eso no es un problema. Veamos cómo funciona esto en el siguiente ejemplo:
foo: @echo foo foo-file.txtbar: @echo bar bar-file.txtbaz: foo bar @echo baz | cat - foo-file.txt bar-file.txt baz-file.txt
Nota : Make normalmente utiliza la convención de nombrar los objetivos después de los archivos que crean. Esto no es una necesidad, pero generalmente se considera una buena práctica.
Lo que tenemos son tres objetivos: foo
, bar
y baz
. Los dos primeros no tienen dependencias propias y lo único que hacen es generar un nuevo archivo de texto. El último objetivo, baz
especifica los otros dos objetivos como sus dependencias. Entonces, cuando ejecutamos make baz
no deberíamos ver ningún resultado (ya que hemos usado la @
sintaxis especial para silenciar cualquier resultado), pero deberíamos encontrar que tenemos los siguientes archivos creados:
- foo-archivo.txt
- archivo-barra.txt
- archivo-baz.txt
El último archivo de la lista debe contener no sólo una línea que muestre baz
sino también otras dos líneas que comprendan el contenido de los otros archivos. Entonces ejecutar cat baz-file.txt
debería imprimir:
bazfoobar
Nota : si no lo ha visto usado antes, el -
comando cat
le indica que espere una entrada de la entrada estándar (el echo
comando escribe en la salida estándar y eso se canaliza |
al cat
comando como entrada estándar)
Accediendo a objetivos
En el ejemplo anterior, estaba generando un archivo basado en el contenido de otros dos objetivos (que a su vez generaron dinámicamente algunos archivos). Hubo un poco de repetición que podría haberse limpiado si hubiéramos usado otra variable automática proporcionada por Make, específicamente $@
.
La $@
variable es una referencia al nombre del objetivo, así que veamos cómo podemos usar esto con nuestro ejemplo anterior:
foo: @echo $@ "$@-file.txt"bar: @echo $@ "$@-file.txt"baz: foo bar @echo $@ | cat - foo-file.txt bar-file.txt "$@-file.txt"
En el ejemplo anterior, nos evitamos tener que escribir algunas veces foo
, bar
pero no los erradicamos por completo, ya que todavía tenemos que hacer referencia a y como requisitos previos, además de hacer referencia a ellos desde el propio comando.baz
foo
bar
baz
Con respecto al baz
comando, podríamos usarlo $^
junto con algunas secuencias de comandos de shell para limpiarlo, de modo que nuevamente no dependamos de valores codificados. El siguiente ejemplo muestra cómo lograrlo:
foo: @echo $@ "$@-file.txt"bar: @echo $@ "$@-file.txt"baz: foo bar @files=$$(echo $^ | sed -E 's/([a-z]+)/1-file.txt/g'); echo $@ | cat - $$files "$@-file.txt"
Oh chico, está bien. Entonces, sí, hemos eliminado algunos valores más codificados, pero a menos que esté sumamente seguro con los scripts de shell, supongo que la refactorización anterior no tendrá mucho sentido para usted. Pero analicémoslo un poco para que podamos ver lo que tenemos:
- Usamos
$^
para obtener la lista de dependencias; en este caso,foo bar
. - Lo canalizamos al
sed
comando. También utilizamos el motor de expresiones regulares extendido-E
para que nuestro patrón de expresiones regulares sea más fácil de entender. - El
sed
comando se reemplazafoo bar
confoo-file.txt bar-file.txt
. - Hacemos ese reemplazo dentro de un subproceso
$()
, que es una sintaxis de shell especial. Esto significa que tenemos que escapar del signo de dólar dentro del Makefile ($$()
). - Los valores devueltos por el subproceso (
foo-file.txt bar-file.txt
) luego se almacenan en una variable llamadafiles
y hacemos referencia a esa variable en lugar de los valores codificados originales.
Además de todo eso, todavía tenemos duplicaciones: el foo
y al bar
que se hace referencia dentro del área de requisitos previos. Esto tiene que estar codificado a menos que vayamos a usar Make o alguna otra forma de scripting de shell para generar dinámicamente el Makefile real; lo que incluso para mí es ir demasiado lejos en este caso.
Bien, entonces, ¿qué nos dice esto en última instancia? Esa simplicidad es la clave.
La razón por la que me tomé todas estas molestias es que me permitió demostrar primero cómo ampliar realmente lo que Make puede hacer por usted si tiene suficiente conocimiento de scripts de shell; y segundo, permitirme demostrar ahora cómo se puede utilizar un Make más idiomático para simplificar el código y evitar una ingeniería excesiva como en el ejemplo anterior:
baz: foo-file.txt bar-file.txt echo $@ | cat - $^ $@-file.txt%-file.txt: echo $* $@
En esta versión refactorizada definimos un objetivo llamado baz
y configuramos sus dependencias para que sean dos archivos que no existen. Tampoco tenemos objetivos definidos en nuestro Makefile.
Para resolver este problema utilizamos una regla virtual, una que utiliza %
la sintaxis de marcador de posición de Make para comparar patrones. Veremos la %
sintaxis con más detalle en breve, pero por ahora bastará con saber que actúa como un comodín.
Cuando ejecutamos make baz
, Make intentará resolver las dos dependencias. La siguiente regla %-file.txt
coincidirá con ambos foo-file.txt
y bar-file.txt
, por lo tanto, el comando echo $* $@
se ejecutará dos veces.
El comando toma la parte dinámica de la regla (las partes foo
y bar
) y las pone a disposición a través de $*
. Escribimos esos dos valores en $@
, que es el nombre del objetivo (en este caso foo-file.txt
y bar-file.txt
) y posteriormente creamos esos dos archivos.
Ahora hemos resuelto las baz
dependencias de la regla y podemos pasar a ejecutar su comando, lo que completa los requisitos como ya hemos visto.
Análisis de objetivos y requisitos previos
Hay muchas variables automáticas diferentes disponibles para Make y veremos algunas más a medida que avancemos. Pero como ya hemos comentado $@
y $
, vale la pena señalar que también puede analizar los detalles del directorio y del nombre de archivo específicos para la primera dependencia y el destino utilizando la sintaxis $(D)
/ $(F)
para el requisito previo y $(@D)
/ $(@F)
para el destino.
Usando el siguiente fragmento como ejemplo (lo ejecutarías con make foo/bar/baz.txt
):
bing/bop.txt: @# do nothingfoo/bar/baz.txt: bing/bop.txt @echo $(@D) @echo $(@F) @echo ------- @echo $(D) @echo $(F)
El ejemplo anterior generaría primero la estructura del directorio y luego el nombre del archivo que se ha analizado desde el destino, y luego la estructura del directorio y el nombre del archivo analizado desde el requisito previo:
foo/barbaz.txt-------bingbop.txt
Dependiendo de sus requisitos, esta puede ser una herramienta bastante poderosa para ayudarlo a construir comandos más complejos.
Nota : si está interesado en saber dónde make
se encuentra su binario, puede usar la variable especial incorporada MAKE
en su comando: @echo $(MAKE)
.
Objetivos dinámicos
Los objetivos pueden hacer coincidir dinámicamente múltiples valores desconocidos y permitir abstraer funciones comunes, como generar archivos que tengan nombres similares (para dar un ejemplo simplificado).
Para hacer esto necesitamos aprovechar la sintaxis del marcador de posición %
y su $*
sintaxis correspondiente. El siguiente ejemplo demuestra la estructura básica:
dynamic-%: @echo "Placeholder value: $* and target value: $@"
Si ejecuta el objetivo usando make dynamic-foo
, obtendrá la siguiente respuesta (observe que el aspecto dinámico del comando foo
se captura en el marcador de posición):
Placeholder value: foo and target value: dynamic-foo
Desreferenciación (Variables y Macros)
Make proporciona la utilidad multipropósito $()
, que se utiliza para desreferenciar valores. Los valores pueden ser funciones (Make tiene muchas funciones integradas y echaremos un vistazo rápido a algunas de ellas más adelante) o pueden ser nombres de variables. Consideremos un ejemplo simple en el que desreferenciamos una variable:
some_var := abcprint_var: @echo $(some_var)
Observe en el ejemplo anterior que definimos la variable usando la :=
sintaxis (mientras que con la mayoría de los idiomas asignaría un valor a una variable usando =
). Make también se admite =
como operador de asignación alternativo, pero su uso es específicamente para situaciones en las que es necesario aprovechar la desreferenciación recursiva. Veamos qué significa eso en la práctica revisando el siguiente ejemplo:
foo = $(bar)bar = $(baz)baz = qux value hererecursive: @echo $(foo)
Esto regresa qux value here
y demuestra cómo la foo
variable evaluó recursivamente todos los demás valores gracias al =
operador.
Si intentáramos esto usando foo := $(bar)
en su lugar, entonces el recursive
objetivo habría impreso una línea vacía ya que utiliza un algoritmo de expansión simple y directo, lo que significa que su valor del lado derecho se expande inmediatamente (es decir, se expande en el momento de la declaración). Con este ejemplo, Make no expande recursivamente los valores hacia bar
y posteriormente hacia baz
para encontrar el valor final de qux value here
.
También hay otros tipos de asignación que puedes usar, como la variable condicional ?=
. Lo que eso hará es asignar un valor a la variable definida solo si aún no tiene un valor definido. Por ejemplo:
assignment = fooassignment ?= barconditional_assignment: @echo $(assignment)
Si ejecutamos make conditional_assignment
, veremos el valor foo
impreso. El valor bar
no está asignado porque ya se definió un valor.
Otro tipo de tarea que vale la pena considerar es +=
, que funciona más o menos como se esperaría si fuera programador (ya que es un operador que aparece en muchos idiomas diferentes). De hecho, agrega el valor a la variable, manteniendo también el valor original. Por ejemplo:
hello_world = hellohello_world += worldsay_hello: @echo $(hello_world)
El ejemplo anterior se imprime hello world
tal como se agregó world
al valor existente hello
. Curiosamente, Make también coloca automáticamente un espacio entre los valores asignados (observe que el valor impreso no era helloworld
).
Una última cosa que quiero cubrir es el uso de macros en Make. Una macro es una colección de comandos que se expanden y ejecutan cuando se elimina la referencia. Es muy parecido a una función, en el sentido de que agrupa el comportamiento. El siguiente ejemplo demuestra cómo funciona:
define do_lots_of_things echo Hi there echo I do lots of things echo So it's best I do this in this macroendefstuff: @$(do_lots_of_things)
Cuando ejecutamos make stuff
vemos todos los diferentes mensajes impresos en la pantalla. También podríamos reutilizar esta macro en muchas reglas de destino diferentes si quisiéramos, que es realmente el objetivo de ellas.
Nota : observe que tuve que evitar el uso de comillas simples '
. Esto se hizo porque sin él el comando fallaría debido a un error de sintaxis en Make.
Funciones
Como se mencionó en la sección anterior, la $()
utilidad funcionó para eliminar la referencia a un valor, pero también puede manejar una serie de funciones integradas. Aunque algunas de las funciones podrían reemplazarse con comandos de shell estándar.
Nota : puede encontrar una lista completa de funciones en el sitio web de GNU Make.
Filtrar
Echemos un vistazo a algunas funciones interesantes que ofrece Make. El primero que me gusta es filter
:
filter: foo.txt bar.txt baz.txt @echo $(filter ba%.txt, $^)
En esta regla usamos la filter
función, que toma como primer argumento el patrón que desea intentar hacer coincidir y el texto dentro del cual desea buscar. En nuestro ejemplo, el texto que se buscará es la lista de requisitos previos ( $^
que ya hemos visto). El patrón que esperamos hacer coincidir utiliza el %
valor
Deja un comentario