InglésFrancésEspañol

Ad


icono de página de OnWorks

makepp_cookbook: en línea en la nube

Ejecute makepp_cookbook en el proveedor de alojamiento gratuito de OnWorks sobre Ubuntu Online, Fedora Online, emulador en línea de Windows o emulador en línea de MAC OS

Este es el comando makepp_cookbook que se puede ejecutar en el proveedor de alojamiento gratuito de OnWorks utilizando una de nuestras múltiples estaciones de trabajo en línea gratuitas, como Ubuntu Online, Fedora Online, emulador en línea de Windows o emulador en línea de MAC OS.

PROGRAMA:

NOMBRE


makepp_cookbook: la mejor manera de configurar archivos MAKE para diversas situaciones

DESCRIPCIÓN


Descubrí que prácticamente nadie lee un manual para una herramienta de fabricación, porque francamente
nadie está realmente interesado en el proceso de creación en sí, solo estamos interesados ​​en los resultados.
Así que este libro de cocina se elaboró ​​con la esperanza de que las personas puedan obtener lo que necesitan.
rápidamente a partir de los ejemplos sin tener que leer el manual. Esto muestra cómo escribir
preguntas, mientras que las instrucciones de instalación y los obstáculos se encontrarán en el
preguntas frecuentes.

Construir la bibliotecas
Do Usted realmente necesite a ¿biblioteca?

He visto varios programas grandes que constan de una gran cantidad de módulos, cada uno de los cuales
que vive en su propio directorio. Por lo general, cada directorio se coloca en su propia biblioteca,
y luego el programa final se vincula con todas las bibliotecas.

En muchos casos, creo que en lugar de usar una biblioteca, hay un enfoque mejor. Bibliotecas
no son realmente la solución correcta si cada módulo no puede o no se reutilizará en ningún otro
programa, porque entonces obtienes todos los inconvenientes de las bibliotecas y ninguno de los
ventajas. Las bibliotecas son útiles en los siguientes casos:

1. Cuando tiene un montón de subrutinas que deben estar vinculadas con varios
programas, y ningún programa utiliza realmente el 100% de las subrutinas; cada programa utiliza un
subconjunto diferente. En este caso, probablemente sea una buena idea utilizar una biblioteca estática (una
.a archivo o un archivo de almacenamiento).

2. Cuando tenga un módulo que deba estar vinculado a varios programas diferentes, y
desea cargarlo dinámicamente para que cada programa no tenga que tener una copia separada de
la biblioteca. Las bibliotecas dinámicas pueden ahorrar espacio en archivos ejecutables y, a veces, mejorar
rendimiento del sistema porque solo hay una copia de la biblioteca cargada para todos los
diferentes programas que lo utilizan.

3. Cuando el tiempo de enlace es prohibitivamente largo, utilice bibliotecas compartidas para grandes
el programa puede acelerar significativamente el enlace.

El uso de bibliotecas estáticas tiene una desventaja principal: en algunos sistemas (por ejemplo, Linux), el orden
en el que se vinculan las bibliotecas es de vital importancia. El enlazador procesa bibliotecas
en el orden especificado en su línea de comando. Agarra todo lo que cree que necesita
cada biblioteca, luego pasa a la siguiente biblioteca. Si alguna biblioteca posterior se refiere a un
símbolo que aún no se ha incorporado de una biblioteca anterior, el enlazador no
saber volver atrás y tomarlo de la biblioteca anterior. Como resultado, puede ser necesario
para enumerar la biblioteca varias veces en la línea de comandos del vinculador. (Trabajé en un proyecto
donde tuvimos que repetir la lista completa de bibliotecas tres veces. Este proyecto es lo que hizo
prefiero el enfoque alternativo sugerido a continuación, el de la vinculación incremental).

El uso de bibliotecas dinámicas tiene varias desventajas. Primero, su programa puede ser ligeramente
más lento para iniciar si la biblioteca no está siendo utilizada por algún otro programa, porque
tiene que ser encontrado y cargado. En segundo lugar, puede ser una molestia obtener toda la dinámica
bibliotecas instaladas en las ubicaciones correctas; no puede simplemente copiar el ejecutable del programa,
también debe asegurarse de copiar todas sus bibliotecas. En tercer lugar, en algunos sistemas,
Es difícil depurar el código dentro de las bibliotecas compartidas porque los depuradores no admiten
ellos bien.

Si su módulo nunca se usará en ningún otro programa, entonces hay pocas razones para usar
una biblioteca: obtienes todas las desventajas de usar bibliotecas y ninguna de las ventajas.
La técnica que prefiero es utilizar enlaces incrementales, cuando estén disponibles.

Así es como puede hacer esto en Linux:

my_module.o: $ (filter_out my_module.o, $ (comodín * .o))
ld -r -o $ (salida) $ (entradas)

Lo que esto hará es crear otro .o archivo llamado mi_modulo.o, que consistirá en
todos los .o archivos en este subdirectorio. El vinculador resolverá la mayor cantidad de
referencias como pueda, y dejará el resto de referencias a resolver en un
etapa posterior de vinculación. En el nivel superior, cuando finalmente crea su programa,
en lugar de enlazar con libmy_module.a or libmy_module.so, simplemente se vincularía con
mi_modulo.o. Cuando enlazas .o archivos, no tiene problemas con la dependencia de pedidos en el
línea de comando del enlazador.

Dejar que hacer pp la figura salir que bibliotecas módulos en

Incluso si tiene una biblioteca real, donde un programa determinado necesita solo unos pocos archivos de él
(en lugar de cada módulo), makepp podría averiguar qué módulos son
necesarios de la biblioteca e incluir solo aquellos en la compilación. Esto puede ahorrar compilación
tiempo si está desarrollando la biblioteca junto con un programa, porque no se molesta en
compile módulos de biblioteca que no sean necesarios para el programa en particular en el que está trabajando.

Si su biblioteca observa estrictamente la convención de que todas las funciones o clases declaradas en
un archivo xyz.h están completamente implementados en un archivo fuente que se compila para xyz.o (es decir, tu
no divida la implementación en xyz1.o y xyz2.o), entonces puede usar el
Función "$ (infer_objects)" para decirle a makepp que extraiga solo los módulos relevantes del
Biblioteca. Esto puede funcionar sorprendentemente bien para bibliotecas con incluso docenas de archivos de inclusión.
Básicamente, "$ (infer_objects)" examina la lista de .h archivos que se incluyen y se ve
para corresponder .o archivos. Si está desarrollando rápidamente una biblioteca y un programa
juntos, esto puede ahorrar tiempo de compilación, porque nunca se molesta en compilar módulos de
la biblioteca que el programa no usa.

Aquí hay un ejemplo de la forma en que lo uso:

mi_programa: $ (inferir_objetos * .o, $ (LIB1) / *. o $ (LIB2) / *. o)
$ (CXX) $ (entradas) -o $ (salida) $ (BIBLIOTECAS_SISTEMA)

La función "$ (infer_objects)" devuelve su primer argumento (después de hacer comodines
expansión en él), y también mira a través de la lista de archivos en su segundo argumento, para
archivos cuyo nombre es el mismo que el nombre de cualquier .h archivos incluidos por cualquier archivo en su primer
argumento. Si se encuentra alguno de estos archivos, se añaden a la lista.

Construir la a estático bibliotecas

Si está seguro de que realmente necesita una biblioteca y el enlace incremental no está disponible o
no es lo que quieres hacer, hay un par de formas de hacerlo. Primero, aquí hay un ejemplo
donde todos los archivos se enumeran explícitamente:

ARCHIVOS_BIBLIOTECA = abcde

libmine.a: $ (ARCHIVOS_BIBLIOTECA) .o
& rm -f $ (salida)
$ (AR) cr $ (salida) $ (entradas)
ranlib $ (salida) # Puede que no sea necesario, dependiendo de su sistema operativo.

El & rm es el comando "rm" incorporado de makepp. Si está acostumbrado a escribir archivos MAKE, es posible que
un poco sorprendido por este comando; puede que estés acostumbrado a algo más parecido a esto:

libmine.a: $ (ARCHIVOS_BIBLIOTECA) .o
$ (AR) ru $ @ $? # ¡¡¡¡¡¡¡No recomendado!!!!!!!
ranlib $ (salida)

donde $? (también conocido como "$ (modified_inputs)") es una variable automática que significa cualquier archivo
que han cambiado desde la última vez que se creó la biblioteca, y $ @ es aproximadamente el mismo
como "$ (salida)".

Este enfoque no se recomienda por varias razones:

· Suponga que elimina un archivo fuente del directorio actual. Todavía está en el
library, porque no reconstruyó la biblioteca desde cero. Como resultado, cualquier cosa
que los enlaces con esta biblioteca tendrán la obsolescencia .o archivo, y eso puede arruinar su
construye. (Una vez me confundí completamente con esto cuando intentaba eliminar el código muerto
de un proyecto: seguí eliminando archivos y todavía estaba vinculado, así que pensé que el código era
muerto. Sin embargo, cuando otra persona reconstruyó el proyecto desde cero, no vinculó ningún
¡más! El problema era que el viejo .o archivos todavía estaban en el archivo.)

Además, dependiendo de sus opciones para "ar" y su implementación de "ar" (por ejemplo, si
use la opción "q" en lugar de "r"), puede terminar teniendo varias versiones del
mismo .o dentro de .a expediente. Si las diferentes versiones definen diferentes globales, la
el vinculador puede intentar extraer ambos. Probablemente esto sea algo malo.

Es por eso que primero eliminamos el archivo de la biblioteca y lo creamos desde cero. Esta voluntad
toma un poco más de tiempo que actualizar módulos en una biblioteca, pero no mucho más; sobre
una computadora moderna, la cantidad de tiempo consumida por el ar el programa es minúsculo en comparación
a lo que toma el compilador de C en una compilación típica, por lo que no vale la pena preocuparse
acerca de.

· Una de las formas en que makepp intenta garantizar construcciones correctas es que
reconstruir automáticamente si la línea de comando para construir un objetivo dado ha cambiado. Pero
usando el $? variable puede causar problemas, porque cada vez que se actualiza la biblioteca,
el comando de construcción es diferente. (Puede suprimir esto usando
": build_check ignore_action"; consulte makepp_build_check para obtener más detalles).

· Actualizar el archivo en lugar de reconstruirlo hará imposible que makepp
coloque el archivo correctamente en un caché de compilación (consulte makepp_build_cache para obtener más detalles).

A veces puede encontrar que enumerar todos los archivos es un poco molesto, especialmente si un
El proyecto está experimentando un rápido desarrollo y la lista de archivos cambia constantemente. Eso
puede ser más fácil construir la biblioteca usando comodines, como este:

libmine.a: $ (solo_destinos * .o)
& rm $ (salida)
$ (AR) cr $ (salida) $ (entradas)

Esto pone todos los .o archivos en el directorio actual en la biblioteca. El comodín
coincide con cualquiera .o archivo que existe o se puede construir, por lo que funcionará incluso si los archivos no
existen todavía.

La función "only_targets" se utiliza para excluir .o archivos que no tienen los correspondientes
archivos de origen más. Suponga que tiene un archivo llamado xyz.c que solías poner en tu
Biblioteca. Esto significa que hay un xyz.o archivo por ahí. Ahora borras xyz.c
porque está obsoleto, pero te olvidas de borrar xyz.o. Sin los "only_targets"
función, xyz.o todavía estaría incluido en la lista de .o archivos incluidos en la biblioteca.

Construir la a lugar de trabajo dinámico bibliotecas

El proceso de construcción de bibliotecas dinámicas depende completamente del sistema. Yo altamente
recomiendo usar libtool para construir una biblioteca dinámica (ver
<http://www.gnu.org/software/libtool/>), para que no tenga que averiguar cómo hacerlo en
su plataforma, y ​​para que su archivo MAKE continúe funcionando incluso cuando cambie a un
diferentes sistemas operativos. Consulte la documentación de libtool para obtener más detalles. Aquí hay un Makefile de muestra:

LIBTOOL: = libtool

libflick.la: $ (solo_destinos * .lo)
$ (LIBTOOL) --mode = link $ (CC) $ (entradas) -o $ (salida)

% .lo:% .c
$ (LIBTOOL) --mode = compilar $ (CC) $ (CFLAGS) $ (INCLUYE) -c $ (entrada) -o $ (salida)

Construir la on Varios una experiencia diferente máquinas or telecomunicaciones
Uno de los problemas más molestos con los archivos MAKE es que casi nunca funcionan cuando
cambie a una máquina diferente o una red diferente. Si sus archivos MAKE tienen que funcionar
todas las máquinas posibles del planeta, entonces probablemente necesite algún tipo de configuración
texto. Pero si solo tiene que trabajar en unas pocas máquinas diferentes, hay varias formas
puedes abordar este problema:

Uso a una experiencia diferente incluir presentar in todos las ambientes

Al comienzo de cada archivo MAKE, puede incluir una línea como esta:

incluir system_defs.mk

El archivo sistema_defs.mk normalmente se ubicaría en un lugar diferente para cada
medio ambiente. Si desea que sus directorios de compilación sean idénticos en todas las máquinas, coloque
sistema_defs.mk en un directorio por encima de los directorios de compilación, o bien proporcione una ruta de inclusión
para makepp usando la opción de línea de comando "-I".

Esto suele ser un poco doloroso, pero funciona bien si hay una gran cantidad de
Diferencias.

Uso if declaraciones

Esta es la forma más fea de hacerlo, pero normalmente funcionará.

sisys i386
CC: = gcc
más ifsys sun4u
CC: = cc
más ifsys hpux11
CC = c89
terminara si

Si todo lo que necesita hacer es buscar algunos programas o bibliotecas o incluir archivos en diferentes
lugares, puede haber mejores formas (ver más abajo).

encontrar_programa, primero disponible, encontrar archivo

Estas funciones pueden buscar varios directorios diferentes en su sistema para encontrar el
archivos apropiados. Esto no es tan poderoso como un script de configuración, por supuesto, pero lo encuentro
útil. Por ejemplo, hago lo siguiente:

CXX; = $ (buscar_programa g ++ c ++ pg ++ cxx CC aCC)
# Elija el primer compilador de C ++ que esté disponible en PATH.
# (Por cierto, si no define CXX en absoluto, esto
# es la forma en que se define).
TCL_INCLUDE; = -I $ (dir_noslash $ (buscar archivo tcl.h, \
/usr/local/stow/tcl-8.4.5-no-thread/include \
/usr/include/tcl8.4 / usr / include / tcl \
/net/na1/tcl8.4a3/include /net/na1/tcl8.4a3/include))
# $ (findfile) busca tcl.h en cada uno de los indicados
# directorios y devuelve la ruta completa. Esto es entonces
# convertido en una opción de compilación quitando el
# nombre de archivo (dejando el directorio) y con el prefijo -I.
% .o:% .cpp
$ (CXX) $ (CXXFLAGS) $ (TCL_INCLUDE) $ (entrada) -o $ (salida)

TCL_LIB; = $ ((primer_disponible
/usr/local/stow/tcl-8.4.5-nothread/lib/libtcl8.4.so
/usr/lib/libtcl8.4.so /usr/lib/libtcl.so
/net/na1/tcl8.4a3/lib/libtcl8.4.a
/net/na1/tcl8.4a3/lib/libtcl8.4.sl))
# Encuentre dónde está la biblioteca Tcl. Entonces esto es explícitamente
# listado en el comando de enlace:
mi_programa: * .o
$ (CXX) $ (CXXFLAGS) $ (entradas) -o $ (salida) $ (TCL_LIB)

¡Prepárate! competitiva of Perl's config información

Las técnicas anteriores pueden no ser suficientes si necesita información adicional sobre
su sistema, como si existe un doble largo o cuál es el orden de bytes. Sin embargo,
perl ya ha calculado estas cosas, por lo que puede usar sus respuestas.

El script de autoconfiguración de Perl hace que toda su información de configuración esté disponible a través de
el hash% Config. No hay sintaxis para acceder a un hash de Perl directamente en makepp, pero puede
suelte en Perl y establezca variables escalares, a las que se puede acceder directamente desde makepp:

perl_begin
# Obtener valores del hash de configuración.
usar configuración;
$ CC = $ Config {'cc'}; # Compilador de C que utilizó perl;
$ byteorder_flags = "-DBYTEORDER = $ Config {'byteorder'}";
$ longdouble_defined = $ Config {'d_longdbl'} eq 'define';
$ CFLAGS_for_shared_libs = $ Config {'cccdlflags'};
$ LDFLAGS_for_shared_libs = $ Config {'ccdlflags'};
fin_perl

Además, una vez que haya hecho 'use Config', puede usar la instrucción "$ (perl)", como
modo:

SHARED_LIB_EXTENSION: = $ (perl $ Config {'dlext'})

Escriba "perldoc Config" para ver qué información está disponible a través del hash% Config.

La configuración de Perl es un buen lugar para obtener información sobre tipos de enteros, bytes
order, y otras cosas que generalmente requieren un script de configuración separado para ubicar. Algunos de
su información que se relaciona con la presencia de elementos en el sistema de archivos podría no ser
válido. Por ejemplo, $ Config {'cc'} se refiere al compilador de C con el que se compiló perl,
que podría no ser el mismo compilador de C que desea utilizar. De hecho, puede que ni siquiera exista
en su sistema, ya que probablemente instaló Perl a través de un paquete binario.

Tips para usando comodines
Coincidencia de todos archivos excepto a a ciertos subconjunto

Los comodines de Makepp no ​​tienen ninguna forma en este momento de hacer coincidir todos los archivos excepto un cierto
set, pero puede hacerlo con una combinación de funciones.

Por ejemplo, suponga que tiene un programa de prueba para cada módulo en una biblioteca, pero no
desea incluir los programas de prueba en la biblioteca. Si todos los programas de prueba comienzan con
test, entonces puede excluirlos de esta manera:

libproduction.a: $ (filter_out prueba *, $ (comodín * .o))

Las funciones "$ (filter)" y "$ (filter_out)" son un conjunto muy poderoso de filtros para hacer
todo tipo de operaciones de intersección y diferencia de conjuntos. Por ejemplo,

SUBDIRS; = $ (filter_out * test * * $ (ARCH) *, $ (shell find. -Type d -print))
# Devuelve todos los subdirectorios que no tienen
# "prueba" o $ (ARCH) en ellos.

$ (filter $ (patsubst test_dir / test _%. o,% .o, $ (comodín test_dir / *. o)), \
$ (comodín * .o))
# Devuelve una lista de archivos .o en el actual
# directorio para el que hay un correspondiente
# test _ *. o archivo en el subdirectorio test_dir.
$ (filter_out $ (patsubst man / man3 /%. 3,% .o, $ (comodín man / man3 / *. 3)), \
$ (comodín * .o))
# Devuelve una lista de archivos .o en el actual
# directorio para el que no hay una página de manual
# con el mismo nombre de archivo en el subdirectorio man / man3.

Usar las "$ (only_targets )" función a eliminarlos duro .o archivos

Suponga que está construyendo un programa o una biblioteca con un comando de construcción como este:

programa: * .o
$ (CC) $ (entradas) -o $ (salida)

Suponga que ahora elimina un archivo fuente. Si olvida eliminar el correspondiente .o archivo,
seguirá estando vinculado aunque ya no haya forma de construirlo. En el
futuro, makepp probablemente reconocerá esta situación automáticamente y la excluirá de
la lista de comodines, pero en este momento, debe indicarle que la excluya manualmente:

programa: $ (only_targets * .o)
$ (CC) $ (entradas) -o $ (salidas)

Makepp no ​​conoce ninguna forma de construir lo obsoleto .o archivo ya que su archivo de origen es
desaparecido, por lo que la función "$ (only_targets)" lo excluirá de la lista de dependencias.

Tips para múltiples directorios
Una de las principales razones para escribir makepp fue simplificar el manejo de múltiples
directorios. Makepp puede combinar comandos de compilación de varios archivos MAKE, por lo que puede
tratar correctamente una regla en un archivo MAKE que depende de un archivo creado por un
diferente archivo MAKE.

¿ a do in place of recursiva para lograr

Makepp admite marca recursiva para compatibilidad con versiones anteriores, pero es muy recomendable
que Tú no úselo. Si no sabe lo que es, bien.

Consulte "Mejor sistema para compilaciones jerárquicas" en makepp para obtener detalles sobre por qué no desea
utilice la marca recursiva o busque en la web "marca recursiva considerada dañina".

En lugar de hacer una marca recursiva para hacer el objetivo "todo" en cada archivo MAKE, es
generalmente es más fácil dejar que makepp descubra qué objetivos realmente necesitarán construirse.
Además, si pones todos tus .o y archivos de biblioteca en el mismo directorio que el
makefiles, entonces makepp averiguará automáticamente qué archivos make también se necesitan - el
lo único que se necesita es que su nivel superior haga una lista de los archivos que se necesitan
para el paso de vinculación final. Vea los ejemplos a continuación.

Un makefile para cada una directorio: implícitamente carga

La forma más común de manejar varios directorios es poner un archivo MAKE en cada directorio.
que describe cómo compilar todo en o desde ese directorio. Si pones .o archivos en
el mismo directorio que los archivos de origen, luego carga implícita (consulte "Carga implícita" en
makepp_build_algorithm) encontrará automáticamente todos los archivos MAKE. Si pones tu .o
archivos en un directorio diferente (por ejemplo, en un subdirectorio dependiente de la arquitectura), entonces
probablemente tendrá que cargar todos los archivos MAKE relevantes usando la instrucción "load_makefile".

Aquí hay un archivo MAKE de nivel superior de muestra para una jerarquía de directorios que usa carga implícita
para crear un programa que consta de muchas bibliotecas compartidas (pero consulte "¿Realmente necesita una
library? "en makepp_cookbook, porque hacer un programa a partir de un montón de bibliotecas compartidas
no es necesariamente una buena idea):

# Makefile de nivel superior:
programa: main.o ** / *. la # Enlace en bibliotecas compartidas de todos los subdirectorios.
$ (LIBTOOL) --mode = enlace $ (CC) $ (CFLAGS) $ (entradas) -o $ (salida) $ (LIBS)

Eso es prácticamente todo lo que necesita en el archivo MAKE de nivel superior. En cada subdirectorio,
probablemente haría algo como esto:

# Makefile en cada subdirectorio:
incluir standard_defs.mk # Búsquedas., .., ../ .., etc.hasta que
# busca el archivo de inclusión indicado.
# anula algunas definiciones de variables aquí
BANDERAS_ESPECIALES: = -hacer_algo_diferente

Cada archivo MAKE puede ser probablemente el mismo si los comandos para construir los objetivos
son bastante similares.

Finalmente, pondría lo siguiente en el estándar_defs.mk archivo (que probablemente debería
estar ubicado en el directorio de nivel superior):

# Configuración de variables comunes y reglas de compilación para todos los directorios.
CFLAGS: = -g -O2
INCLUDE_DIR: = $ (find_upwards incluye)
# Búsquedas., .., ../ .., etc. para un archivo o
# directorio llamado incluye, así que si pones
# todos sus archivos de inclusión allí, esto
# Encuéntralos.
INCLUYE: = -I $ (INCLUDE_DIR)

% .lo:% .c
$ (LIBTOOL) --mode = compilar $ (CC) $ (CFLAGS) $ (INCLUYE) -c $ (entrada) -o $ (salida)

lib $ (relativo_a., ..). la: $ (solo_destinos * .lo)
$ (LIBTOOL) --mode = enlace $ (CC) $ (CFLAGS) -o $ (salida) $ (entradas)
# $ (relativo_a., ..) devuelve el nombre del actual
# subdirectorio relativo al nivel superior
# subdirectorio. Entonces, si este archivo MAKE es xyz / Makefile,
# esta regla construirá xyz / libxyz.la.

# Publicar archivos de inclusión públicos en el directorio de inclusión de nivel superior:
$ (INCLUDE_DIR) / public _%. H: public _%. H
: build_check symlnk
& ln -fr $ (entrada) $ (salida)

Un makefile para cada una directorio: explícito carga

Si quieres poner todos tus .o archivos en un subdirectorio dependiente de la arquitectura, luego
el ejemplo anterior debe modificarse para que sea algo como esto:

# Makefile de nivel superior:
MAKEFILES: = $ (comodín ** / Makeppfile) # Lista de todos los subdirectorios a
# obtener archivos MAKE de.

load_makefile $ (MAKEFILES) # Cárgalos todos en.

include standard_defs.mk # Obtener comando de compilación para main.o.

programa: $ (ARCO) /main.o * / ** / $ (ARCO) / *. la
$ (LIBTOOL) --mode = enlace $ (CC) $ (CFLAGS) $ (entradas) -o $ (salida) $ (LIBS)
# * / ** / $ (ARCH) excluye el subdirectorio
# $ (ARCH), donde no queremos construir
# una biblioteca compartida.

Cada archivo MAKE sería exactamente el mismo que antes:

# Makefile en cada subdirectorio:
incluir standard_defs.mk
# ... variable anula aquí

Y, finalmente, estándar_defs.mk contendría algo como lo siguiente:

# Configuración de variables comunes y reglas de compilación para todos los directorios.
ARCH; = $ (shell uname -s) - $ (shell uname -m) - $ (shell uname -r)
# A veces la gente usa solo $ (shell uname -m), pero
# esto será lo mismo para FreeBSD y Linux en
# un x86. La -r no es realmente útil en Linux,
# pero es importante para otros sistemas operativos: binarios para
# SunOS 5.8 normalmente no se ejecutará en SunOS 5.7.
& mkdir -p $ (ARCH) # Asegúrese de que exista el directorio de salida.
CFLAGS: = -g -O2
INCLUDE_DIR: = $ (find_upwards incluye)
# Búsquedas., .., ../ .., etc. para un archivo o
# directorio llamado incluye, así que si pones
# todos sus archivos de inclusión allí, esto
# Encuéntralos.
INCLUYE: = -I $ (INCLUDE_DIR)

$ (ARCO) /%. Lo:% .c
$ (LIBTOOL) --mode = compilar $ (CC) $ (CFLAGS) $ (INCLUYE) -c $ (entrada) -o $ (salida)

$ (ARCO)/ lib$ (relativo_a., ..). la: $ (solo_destinos * .lo)
$ (LIBTOOL) --mode = enlace $ (CC) $ (CFLAGS) -o $ (salida) $ (entradas)
# $ (relativo_a., ..) devuelve el nombre del actual
# subdirectorio relativo al nivel superior
# subdirectorio. Entonces, si este archivo MAKE es xyz / Makefile,
# esta regla construirá xyz / $ (ARCH) /libxyz.la.

# Copie los archivos de inclusión públicos en el directorio de inclusión de nivel superior:
$ (INCLUDE_DIR) / public _%. H: public _%. H
& cp $ (entrada) $ (salida)

Automáticamente fabricación las hacer archivos

Si sus archivos MAKE son todos extremadamente similares (como en el ejemplo anterior), puede decirle a Makepp
para construirlos automáticamente si no existen. Simplemente agregue lo siguiente a su nivel superior
archivo MAKE:

SUBDIRS: = $ (filter_out unwanted_dir1 unwanted_dir2, $ (comodín * / **))
$ (foreach) / Makeppfile:: foreach $ (SUBDIRS)
& echo "include standard_defs.mk" -o $ (salida)
& echo "_include additional_defs.mk" -o >> $ (salida)
# Si el archivo additional_defs.mk existe, entonces
# se incluirá, pero si no existe,
# se ignorará la instrucción _include.

Ahora los propios archivos MAKE se construirán automáticamente.

Un makefile , solamente at las parte superior nivel

Si todos sus archivos MAKE son idénticos, puede preguntar: ¿por qué debería tener un archivo MAKE en cada
¿nivel? ¿Por qué no poner todo eso en el archivo MAKE de nivel superior?

Si, esto se puede hacer. La principal desventaja es que se vuelve más difícil de especificar
diferentes opciones de construcción para cada subdirectorio. Una segunda desventaja es que su
makefile probablemente será un poco más difícil de leer.

Aquí hay un ejemplo de cómo hacer precisamente eso:

# Makefile de nivel superior para la jerarquía de directorios. Construye el programa
# de un conjunto de bibliotecas compartidas como ejemplo. (Ver advertencias arriba
# por qué es posible que desee utilizar enlaces incrementales o algún otro
# enfoque en lugar de bibliotecas compartidas).
makepp_percent_subdirs: = 1 # Permitir que% coincida con varios directorios.
SUBDIRS: = $ (filter_out * CVS * other-unwanted_dirs $ (comodín **))
CFLAGS: = -g -O2
INCLUYE: = -Incluye

% .lo:% .c
$ (LIBTOOL) --mode = compilar $ (CC) $ (INCLUYE) $ (CFLAGS) -c $ (entrada) -o $ (salida)

$ (foreach)/ lib$ (notdir $ (foreach)). la: $ (foreach) / *. lo: foreach $ (SUBDIRS)
$ (LIBTOOL) --mode = enlace $ (CC) $ (CFLAGS) -o $ (salida) $ (entradas)
# Regla para hacer todas las bibliotecas.

programa: main.o ** / *. la
$ (LIBTOOL) --mode = enlace $ (CC) $ (CFLAGS) -o $ (salida) $ (entradas)

incluye / $ (notdir $ (foreach)): $ (foreach): foreach ** / public _ *. h
& cp $ (entrada) $ (salida)
# Regla de muestra para copiar públicamente
# archivos .h accesibles en el lugar correcto.

A limpia dirigidos

Los archivos MAKE tradicionales contienen un objetivo limpio, que permite eliminar todo lo que se
construido. Hay tres razones por las que no debería hacer esto con makepp:

1. Makepp hace todo lo posible para garantizar una construcción correcta. Entonces el desesperado "yo no
saber lo que está mal ", hacer que quieras empezar desde cero es cosa del pasado.

2. A veces, la gente intentará ahorrar tiempo haciendo dos cosas contradictorias a la vez:
"Limpiar todo". Esto puede confundir el sistema de comodines inteligente de makepp, porque
primero obtenga los hechos antes de hacer cualquier cosa. Luego viene la acción limpia, que no
no decirle a makepp lo que hace (de hecho, no puede, porque deshace algo: el
contrario de lo que es una herramienta de construcción). Luego viene "todos", pero los archivos actualizados,
que donde allí, misteriosamente se han ido.

3. Existe el comando "makeppclean", que hace lo mismo y de manera más eficiente.

Sin embargo, conservamos esta sección histórica, ya que le dice algo sobre la
cómo funciona makepp: un objetivo falso llamado "limpio" es solo el nombre de un conjunto de comandos para
elimine todos los archivos que resulten del proceso de creación. Por lo general, un objetivo limpio se ve
algo como esto:

$ (falso limpio):
& rm -fm $ (comodín * .o .makepp_log)
# -my .makepp_log elimina toda la basura de makepp.

En lugar de enumerar explícitamente los archivos que desea eliminar, también puede decirle a makepp que
elimine todo lo que sabe cómo construir, así:

$ (falso limpio):
& rm -fm .makepp_log $ (solo_destinos *)

Esto tiene la ventaja de que si alguno de sus archivos de origen se puede compilar a partir de otros archivos,
también serán eliminados; por otro lado, rancio .o archivos (archivos que solían ser
compilable pero cuyo archivo de origen se ha eliminado desde entonces) no se eliminará.

Si tiene una compilación que incluye archivos MAKE en varios directorios diferentes, su
makefile de nivel puede hacer referencia al objetivo "limpio" (o cualquier otro objetivo falso) en un diferente
archivo MAKE:

# Makefile de nivel superior
SUBDIRS: = sub1 sub2

# construye reglas aquí

# Limpiar después de la construcción:
$ (limpieza falsa): $ (SUBDIRS) / clean
& rm -fm .makepp_log $ (solo_destinos *)

Alternativamente, puede poner su objetivo "limpio" solo en el archivo MAKE de nivel superior y hacer que
procesar todos los directorios, así:

$ (falso limpio):
& rm -fm $ (solo_destinos ** / *)

Usar Qt's poder preprocesador
Este ejemplo muestra un archivo MAKE para una utilidad que usa la biblioteca GUI Qt de Nokia (consulte
<http://qt.nokia.com>). Lo único que es un poco inusual en esto es que
debe ejecutar un preprocesador llamado "moc" en la mayoría de los archivos ".h" que contienen definiciones de widgets,
pero no desea ejecutar "moc" en ningún archivo ".h" que no utilice la macro "Q_OBJECT".

Automáticamente determinar que archivos necesite poder archivos

Por supuesto, podría simplemente enumerar todos los archivos ".h" que necesitan tener "moc" ejecutado en ellos.
Sin embargo, si está desarrollando rápidamente nuevos widgets, puede resultar algo molesto
siga actualizando la lista en el archivo MAKE. Puede evitar la necesidad de enumerar el moc
módulos explícitamente con algo como esto:

MOC: = $ (QTDIR) / bin / moc
MÓDULOS: = cualquier módulo que tenga en su programa
MOC_MODULES: = $ (patsubst% .h, moc_%, $ (& grep -l / Q_OBJECT / * .h))
# Analiza todos los archivos .h en busca de la macro Q_OBJECT.

mi_programa: $ (MODULOS) .o $ (MOC_MODULES) .o
$ (CXX) $ (entradas) -o $ (salida)

moc _%. cxx:% .h # Hace los archivos moc a partir de los archivos .h.
$ (MOC) $ (entrada) -o $ (salida)

% .o:% .cxx
$ (CXX) $ (CXXFLAGS) -c $ (entrada) -o $ (salida)

Este enfoque analiza cada uno de sus .h archivos cada vez que se ejecuta makepp, buscando el
Macro "Q_OBJECT". Esto suena caro, pero probablemente no le llevará mucho tiempo. (Los .h
todos los archivos deberán cargarse desde el disco de todos modos mediante el proceso de compilación, por lo que
ser almacenado en caché.)

#incluir las .moc presentar

Otro enfoque es "#incluir" la salida del preprocesador "moc" en su widget
archivo de implementación. Esto significa que debe recordar escribir el "#include", pero tiene
la ventaja de que hay menos módulos para compilar, por lo que la compilación es más rápida.
(Para la mayoría de las compilaciones de C ++, la mayor parte del tiempo se dedica a leer los archivos de encabezado y
la salida del preprocesador debe incluir casi tantos archivos como su widget
de todos modos.) Por ejemplo:

// mi_widget.h
class MyWidget: public QWidget {
Q_OBJETO
// ...
}

// mi_widget.cpp

#include "my_widget.h"
#include "my_widget.moc" // my_widget.moc es la salida del
// preprocesador moc.
// Otras cosas de implementación aquí.
MyWidget :: MyWidget (QWidget * padre, const char * nombre):
QWidget (padre, nombre)
{
// ...
}

Ahora necesita tener una regla en su archivo MAKE para hacer todos los archivos ".moc", como este:

MOC: = $ (QTDIR) / bin / moc
# Regla para hacer archivos .moc:
% .moc:% .h
$ (MOC) $ (entrada) -o $ (salida)

Makepp es lo suficientemente inteligente como para darse cuenta de que necesita crear "my_widget.moc" si no lo hace
ya existe, o si está desactualizado.

Este segundo enfoque es el que suelo utilizar porque acelera la compilación.

Reemplazos para para lograr modismos
HACER OBJETIVOS

A veces, las personas tienen reglas en su archivo MAKE que dependen del objetivo que están construyendo,
utilizando la variable especial "MAKECMDGOALS". Por ejemplo, a veces uno ve cosas como
modo:

ifneq ($ (producción de filtros, $ (MAKECMDGOALS)),)
CFLAGS: = -O2
más
CFLAGS: = -g
terminara si

Esto funcionará bien con makepp. Sin embargo, recomiendo no utilizar "MAKECMDGOALS" para tales
casos (y también lo hace GNU manual). Es mejor dejar su optimizado y
depurado-compilado .o archivos en directorios separados, o dándoles diferentes prefijos o
sufijos, o usar repositorios, para mantenerlos separados.

Probablemente el único momento en el que quieras hacer referencia a "MAKECMDGOALS" es si
toma mucho tiempo cargar sus archivos MAKE, y no lo necesita para su objetivo "limpio"
(pero no necesitas un objetivo limpio). Por ejemplo,

ifneq ($ (MAKECMDGOALS), limpiar)
load_makefile $ (comodín ** / Makeppfile)
más
no_implicit_load. # Evite la carga automática de cualquier otro archivo MAKE.
terminara si

$ (falso limpio):
& rm -f $ (comodín ** / *. o)

recursiva para lograr a build in una experiencia diferente directorios

Consulte "Consejos para varios directorios" en makepp_cookbook.

recursiva para lograr a el cambio propuesta de of a variable

Algunos archivos MAKE se vuelven a invocar a sí mismos con un valor diferente de una variable, por ejemplo, el debug
destino en el siguiente fragmento de archivo MAKE

.PHONY: toda depuración

optimizado:
$ (MAKE) programa CFLAGS = -O2

depurar:
$ (MAKE) programa CFLAGS = -g

programa: ao bo
$ (CC) $ (CFLAGS) $ ^ -o $ @

% .o:% .c
$ (CC) $ (CFLAGS) -c $ <-o $ @

Si el usuario escribe "hacer depuración", compila el programa en el modo predeterminado con la depuración habilitada.
en lugar de con optimización.

Una mejor manera de hacerlo es construir dos programas diferentes, con dos conjuntos diferentes de
archivos de objeto, como este:

CFLAGS: = -O2
DEBUG_FLAGS: = -g
MÓDULOS: = ab

programa: $ (MÓDULOS) .o
$ (CC) $ (CFLAGS) $ (entradas) -o $ (salida)

debug / program: debug / $ (MODULES) .o
$ (CC) $ (DEBUG_FLAGS) $ (entradas) -o $ (salida)

% .o:% .c
$ (CC) $ (CFLAGS) -c $ (entrada) -o $ (salida)

depuración /%. o:% .c
$ (CC) $ (DEBUG_FLAGS) -c $ (entrada) -o $ (salida)

$ (depuración falsa): depuración / programa

La ventaja de hacerlo de esta manera es (a) no necesita reconstruir todo cuando
cambiar de depuración a optimizado y viceversa; (B)

Lo anterior se puede escribir de forma algo más concisa utilizando repositorios. El seguimiento
makefile es exactamente equivalente:

depuración del repositorio =. # Hace que el subdirectorio de depuración parezca una copia de
# el subdirectorio actual.
load_makefile debug CFLAGS = -g
# Anular CFLAGS cuando se invoca en el subdirectorio de depuración
CFLAGS: = -O2 # Valor de CFLAGS cuando se invoca en este subdirectorio

programa: ao bo
$ (CC) $ (CFLAGS) $ ^ -o $ @

% .o:% .c
$ (CC) $ (CFLAGS) -c $ <-o $ @

$ (depuración falsa): depuración / programa
# Si el usuario escribe "makepp debug", compila
# debug / program en lugar de program.

Observaciones recomendaciones
Cómo do I build una parte diferentemente solo ¿una vez?

Makepp hace que esto sea difícil de hacer porque el resultado es inconsistente con respecto a las reglas.
Pero hay situaciones en las que puede necesitar esto, por ejemplo, para compilar solo un módulo con
mucha información de depuración. Puede lograr esto en dos pasos construyendo primero el
dependencia por separado, y luego excluirla de la fase de enlace:

makepp DEBUG = 3 buggy.o # Construirlo con otra opción.
makepp --dont-build = buggy.o buggy # Úselo, a pesar de la opción de construcción "incorrecta".

Cómo do I para lograr seguro my salida directorios ¿existe?

Puede especificar una regla para construir el directorio de salida, luego asegúrese de que cada archivo que
entra en el directorio de salida depende de ello. Pero suele ser más fácil hacer algo como
modo:

# La forma clásica
ficticio: = $ (prueba de shell -d $ (DIRECTORIO_DE_ SALIDA) || mkdir -p $ (DIRECTORIO_DE_ SALIDA))
# Esto suele ser más fácil que hacer que todos los archivos dependan de
# $ (OUTPUT_DIRECTORY) y tener una regla para hacerlo.
# Tenga en cuenta que debe usar: = en lugar de = para forzarlo a
# ejecutar inmediatamente.
# Un enfoque alternativo: usando código Perl, OUTPUT_DIRECTORY local var
perl_begin
-d $ DIRECTORIO_DE_ SALIDA o mkdir $ DIRECTORIO_DE_ SALIDA;
fin_perl
# La forma moderna, no hace nada por los directorios existentes
& mkdir -p $ (DIRECTORIO_DE SALIDA)

Una de estas declaraciones debe estar cerca de la parte superior de su archivo MAKE, por lo que se ejecutan
antes que cualquier cosa que pueda necesitar el directorio.

Cómo do I forzar a comando a ejecutar on cada ¿construir?

La forma más sencilla es no utilizar el mecanismo de reglas en absoluto, sino simplemente ejecutarlo, como
modo:

ficticio: = $ (fecha de shell> last_build_timestamp)

O ponlo en un bloque de perl, como este:

perl_begin
sistema ("comando para ejecutar");
fin_perl

Este enfoque tiene la desventaja de que se ejecutará incluso si un objetivo no relacionado es
siendo ejecutado.

Un segundo enfoque es declarar el archivo como un objetivo falso, incluso si es un archivo real.
Esto obligará a makepp a volver a ejecutar el comando para compilarlo cada vez, pero solo si
aparece en la lista de dependencias de alguna regla.

Cómo do I acortar las aquí build comandos?

A menudo hay tantas opciones para compilar comandos que lo que se muestra en el
la pantalla es ilegible. Puede cambiar lo que se muestra suprimiendo la visualización del
comando completo y, a continuación, imprima explícitamente la parte interesante del comando. Es
fácil de imprimir solo la parte relevante del comando usando "$ (filter_out)", como
modo:

ALL_CFLAGS = $ (CFLAGS) $ (INCLUYE) $ (ADDL_CXX_FLAGS) $ (DEBUG_FLAGS)

% .o:% .c
@ & echo $ (notdir $ (CC)) ... \
$ (filter_out -I * $ (ADDL_CXX_FLAGS), $ (ALL_CFLAGS)) \
-c $ (entrada)
@ $ (CC) $ (ALL_CFLAGS) -c $ (entrada) -o $ (salida)

(La "@" delante del comando suprime la impresión del comando).

Esto le permitirá ver la mayoría de las opciones interesantes, pero no mostrará todas las
incluir directorios (¡de los cuales a menudo hay muchos!). Si la parte que te interesa
es contiguo en su comando, también puede usar la función "imprimir" (que agrega un
nueva línea, por lo que no desea varios de ellos):

objetivo:
@ ... $ (imprimir parte interesante) ...

Cómo do I convertir a presentar dentro dependencias?

Para algunos formatos de archivo poco conocidos, no vale la pena implementar un escáner. En un proyecto
tenemos archivos xml, digamos foobar.xml que contiene las dependencias para foobar.fuera:


a
B
C


Decidimos adherirnos a este diseño simple, por lo que no necesitamos analizar xml. Con el
incorporado y sed, esto es lo que hacemos con tres sustituciones simples para los tres tipos de
líneas:

% .d:% .xml
& sed 's! ! $ (raíz) .out: \\! || ¡s! (. +) ! $$ 1 \\! || ¡s! !# ¡Vacío!' \
$ (entrada) -o $ (salida)

incluir foobar.d

Al intentar incluir esto, se produce "foobar.d" primero:

foobar.fuera: \
un \
B \
C \
# Vacío

La línea vacía (solo un comentario o realmente vacía) evita tener que preocuparse por el final
barra invertida. Una alternativa que produce una lista de varias líneas es:

% .d:% .xml
& sed 's! ! $ (raíz) .out: \ $$ ((! || s! !))! || s! <. +?> !! g '\
$ (entrada) -o $ (salida)

incluir foobar.d

Esto produce un equivalente:

foobar.out: $ ((
a
b
c
))

Si tiene que realizar una reescritura más compleja, defina una función dentro del archivo MAKE o en un
módulo que incluyes. Por ejemplo, si no se define $ _, se omitirán las líneas de entrada:

sub mifiltro {
devuelve undef $ _ si /
my $ tallo = f_stem;
¡s! ! $ tallo.out: \ $ ((! || s! !))! || s! <. +?> !! g;
}

% .d:% .xml
& sed 's! ! $ (raíz) .out: \ $$ ((! || s! !))! || s! <. +?> !! g '\
$ (entrada) -o $ (salida)

incluir foobar.d

Use makepp_cookbook en línea usando los servicios de onworks.net


Servidores y estaciones de trabajo gratuitos

Descargar aplicaciones de Windows y Linux

Comandos de Linux

Ad