Estamos obligados a usar un Makefile para unir todo para nuestro proyecto, pero nuestro profesor nunca nos mostró cómo hacerlo.
Sólo tengo un archivo, a3driver.cpp
. El controlador importa una clase desde una ubicación "/user/cse232/Examples/example32.sequence.cpp"
,.
Eso es. Todo lo demás está contenido con el .cpp
.
¿Cómo haría para crear un Makefile simple que crea un ejecutable llamado a3a.exe
?
Respuestas:
Como esto es para Unix, los ejecutables no tienen extensiones.
Una cosa a tener en cuenta es que
root-config
es una utilidad que proporciona la compilación correcta y las banderas de enlace; y las bibliotecas adecuadas para crear aplicaciones contra root. Eso es solo un detalle relacionado con la audiencia original de este documento.Hazme bebe
o nunca olvidas la primera vez que te hicieron
Una discusión introductoria sobre make y cómo escribir un simple makefile
¿Qué es hacer? ¿Y por qué debería importarme?
La herramienta llamada Make es un administrador de dependencia de compilación. Es decir, se encarga de saber qué comandos deben ejecutarse en qué orden tomar su proyecto de software de una colección de archivos fuente, archivos de objetos, bibliotecas, encabezados, etc., etc., algunos de los cuales pueden haber cambiado recientemente --- y convertirlos en una versión correcta y actualizada del programa.
En realidad, también puedes usar Make para otras cosas, pero no voy a hablar de eso.
Un archivo trivial
Suponga que tiene un directorio que contiene:,
tool
tool.cc
tool.o
support.cc
support.hh
ysupport.o
que dependeroot
y se supone que debe compilarse en un programa llamadotool
, y suponga que ha estado pirateando los archivos de origen (lo que significa que el existentetool
está desactualizado) y desea compilar el programa.Para hacer esto usted mismo podría
Compruebe si
support.cc
osupport.hh
es más reciente quesupport.o
, y si es así, ejecute un comando comoVerifique si
support.hh
otool.cc
son más nuevos quetool.o
, y si es así, ejecute un comando comoCompruebe si
tool.o
es más nuevo quetool
, y si es así, ejecute un comando como¡Uf! ¡Qué lío! Hay mucho que recordar y varias posibilidades de cometer errores. (Por cierto, los detalles de las líneas de comando que se muestran aquí dependen de nuestro entorno de software. Estos funcionan en mi computadora).
Por supuesto, puede ejecutar los tres comandos cada vez. Eso funcionaría, pero no se adapta bien a una pieza sustancial de software (como DOGS que toma más de 15 minutos compilar desde cero en mi MacBook).
En su lugar, podría escribir un archivo llamado
makefile
así:y solo escribe
make
en la línea de comando. Que realizará los tres pasos que se muestran arriba automáticamente.Las líneas no indentadas aquí tienen la forma "destino: dependencias" y le dicen a Make que los comandos asociados (líneas sangradas) deben ejecutarse si alguna de las dependencias es más nueva que el objetivo. Es decir, las líneas de dependencia describen la lógica de lo que debe reconstruirse para acomodar los cambios en varios archivos. Si
support.cc
cambia eso significa quesupport.o
debe ser reconstruido, perotool.o
puede dejarse solo. Cuando lossupport.o
cambiostool
deben ser reconstruidos.Los comandos asociados con cada línea de dependencia se activan con una pestaña (ver más abajo) que debe modificar el objetivo (o al menos tocarlo para actualizar el tiempo de modificación).
Variables, reglas incorporadas y otras cosas
En este punto, nuestro archivo MAKE simplemente está recordando el trabajo que debe realizarse, pero aún así teníamos que resolver y escribir todos y cada uno de los comandos necesarios en su totalidad. No tiene por qué ser así: Make es un lenguaje poderoso con variables, funciones de manipulación de texto y una gran cantidad de reglas incorporadas que pueden hacernos esto mucho más fácil.
Hacer variables
La sintaxis para acceder a una variable make es
$(VAR)
.La sintaxis para asignar a una variable Make es:
VAR = A text value of some kind
(oVAR := A different text value but ignore this for the moment
).Puede usar variables en reglas como esta versión mejorada de nuestro archivo MAKE:
que es un poco más legible, pero aún requiere mucho tipeo
Hacer funciones
GNU make admite una variedad de funciones para acceder a la información del sistema de archivos u otros comandos del sistema. En este caso, estamos interesados en
$(shell ...)
qué se expande a la salida de los argumentos y$(subst opat,npat,text)
qué reemplaza todas las instancias deopat
connpat
en el texto.Aprovechar esto nos da:
que es más fácil de escribir y mucho más legible.
Darse cuenta de
Reglas implícitas y de patrones
En general, esperaríamos que todos los archivos fuente de C ++ se traten de la misma manera, y Make proporciona tres formas de decir esto:
Las reglas implícitas están integradas, y algunas se discutirán a continuación. Las reglas de patrón se especifican en una forma como
lo que significa que los archivos de objetos se generan a partir de los archivos fuente de C ejecutando el comando que se muestra, donde la variable "automática" se
$<
expande al nombre de la primera dependencia.Reglas incorporadas
Make tiene una gran cantidad de reglas integradas que significan que, muy a menudo, un proyecto puede compilarse con un archivo MAKE muy simple.
La regla incorporada de creación de GNU para archivos fuente C es la que se muestra arriba. Del mismo modo, creamos archivos de objetos a partir de archivos fuente de C ++ con una regla como
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
.Los archivos de un solo objeto se vinculan utilizando
$(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)
, pero esto no funcionará en nuestro caso, porque queremos vincular varios archivos de objeto.Variables utilizadas por las reglas integradas
Las reglas integradas utilizan un conjunto de variables estándar que le permiten especificar información del entorno local (como dónde encontrar los archivos de inclusión ROOT) sin volver a escribir todas las reglas. Los más propensos a ser interesantes para nosotros son:
CC
- el compilador de C para usarCXX
- el compilador de C ++ para usarLD
- el enlazador para usarCFLAGS
- bandera de compilación para archivos fuente CCXXFLAGS
- banderas de compilación para archivos fuente C ++CPPFLAGS
- indicadores para el preprocesador c (normalmente incluyen rutas de archivo y símbolos definidos en la línea de comando), utilizados por C y C ++LDFLAGS
- banderas de enlaceLDLIBS
- bibliotecas para vincularUn Makefile Básico
Al aprovechar las reglas incorporadas, podemos simplificar nuestro archivo MAKE para:
También hemos agregado varios objetivos estándar que realizan acciones especiales (como limpiar el directorio de origen).
Tenga en cuenta que cuando se invoca make sin argumento, usa el primer objetivo encontrado en el archivo (en este caso, todos), pero también puede nombrar el objetivo para obtener cuál es el que hace que
make clean
eliminar los archivos de objeto en este caso.Todavía tenemos todas las dependencias codificadas.
Algunas mejoras misteriosas
Darse cuenta de
make
a continuación,ls -A
se ve un archivo con el nombre.depend
que contiene las cosas que se parecen a las líneas de dependencia maquillajeOtra lectura
Conozca errores y notas históricas
El idioma de entrada para Make es sensible al espacio en blanco. En particular, las líneas de acción que siguen a las dependencias deben comenzar con una pestaña . Pero una serie de espacios puede tener el mismo aspecto (y, de hecho, hay editores que convertirán silenciosamente las pestañas en espacios o viceversa), lo que da como resultado un archivo Make que se ve bien y aún no funciona. Esto se identificó como un error al principio, pero (según la historia ) no se solucionó, porque ya había 10 usuarios.
(Esto fue copiado de una publicación wiki que escribí para estudiantes de posgrado de física).
fuente
-pthread
El indicador hacegcc
que se definan las macros necesarias,-D_REENTRANT
es innecesario.root-config
). Debería proponerse una alternativa más general con la misma capacidad, en caso de existir, o simplemente dejarla fuera. No voté en contra debido a la lista y explicación de las macros make más utilizadas.Siempre pensé que era más fácil de aprender con un ejemplo detallado, así que así es como pienso en los makefiles. Para cada sección, tiene una línea que no está sangrada y muestra el nombre de la sección seguida de dependencias. Las dependencias pueden ser otras secciones (que se ejecutarán antes de la sección actual) o archivos (que si se actualizan harán que la sección actual se ejecute nuevamente la próxima vez que se ejecute
make
).Aquí hay un ejemplo rápido (tenga en cuenta que estoy usando 4 espacios donde debería estar usando una pestaña, Stack Overflow no me deja usar pestañas):
Cuando escriba
make
, elegirá la primera sección (a3driver). a3driver depende de a3driver.o, por lo que irá a esa sección. a3driver.o depende de a3driver.cpp, por lo que solo se ejecutará si a3driver.cpp ha cambiado desde la última vez que se ejecutó. Suponiendo que se haya ejecutado (o que nunca se haya ejecutado), compilará a3driver.cpp en un archivo .o, luego volverá a a3driver y compilará el ejecutable final.Como solo hay un archivo, incluso podría reducirse a:
La razón por la que mostré el primer ejemplo es que muestra el poder de los makefiles. Si necesita compilar otro archivo, simplemente puede agregar otra sección. Aquí hay un ejemplo con un secondFile.cpp (que se carga en un encabezado llamado secondFile.h):
De esta manera, si cambia algo en secondFile.cpp o secondFile.h y vuelve a compilar, solo volverá a compilar secondFile.cpp (no a3driver.cpp). O, alternativamente, si cambia algo en a3driver.cpp, no volverá a compilar secondFile.cpp.
Avíseme si tiene alguna pregunta al respecto.
También es tradicional incluir una sección llamada "all" y una sección llamada "clean". "all" generalmente compilará todos los ejecutables y "clean" eliminará "artefactos de compilación" como archivos .o y los ejecutables:
EDITAR: No me di cuenta de que estás en Windows. Creo que la única diferencia es cambiar el
-o a3driver
a-o a3driver.exe
.fuente
¿Por qué a todos les gusta enumerar los archivos fuente? Un simple comando de búsqueda puede encargarse de eso fácilmente.
Aquí hay un ejemplo de un simple Makefile de C ++. Simplemente colóquelo en un directorio que contenga
.C
archivos y luego escribamake
...fuente
Tenías dos opciones.
Opción 1: makefile más simple = NO MAKEFILE.
Cambie el nombre de "a3driver.cpp" a "a3a.cpp", y luego en la línea de comando escriba:
Y eso es. Si está utilizando GNU Make, use "make" o "gmake" o lo que sea.
Opción 2: un archivo MAKE de 2 líneas.
fuente
nmake
. Lalink
línea de comando también se ve muy específica para un compilador particular, y al menos debe documentar cuál.Su archivo Make tendrá una o dos reglas de dependencia dependiendo de si compila y vincula con un solo comando, o con un comando para la compilación y uno para el enlace.
La dependencia es un árbol de reglas que se ve así (tenga en cuenta que la sangría debe ser una TAB):
No debe haber una línea en blanco después de que los comandos para un objetivo, y debe no ser una línea en blanco antes de los comandos. El primer objetivo en el archivo MAKE es el objetivo general, y otros objetivos se construyen solo si el primer objetivo depende de ellos.
Entonces su archivo MAKE se verá más o menos así.
fuente
Sugiero (tenga en cuenta que la sangría es una TAB):
o
La última sugerencia es un poco mejor ya que reutiliza las reglas implícitas de GNU Make. Sin embargo, para que funcione, un archivo fuente debe tener el mismo nombre que el ejecutable final (es decir:
tool.c
ytool
).Aviso, no es necesario declarar fuentes. Los archivos de objetos intermedios se generan mediante una regla implícita. En consecuencia, esto
Makefile
funciona para C y C ++ (y también para Fortran, etc.).Observe también, por defecto, el uso de Makefile
$(CC)
como el vinculador.$(CC)
no funciona para vincular archivos de objetos C ++. ModificamosLINK.o
solo por eso. Si desea compilar código C, no tiene que forzar elLINK.o
valor.Claro, también puede agregar sus indicadores de compilación con variable
CFLAGS
y agregar sus bibliotecasLDLIBS
. Por ejemplo:Una nota al margen: si tiene que usar bibliotecas externas, sugiero usar pkg-config para configurar correctamente
CFLAGS
yLDLIBS
:El lector atento notará que esto
Makefile
no se reconstruye correctamente si se cambia un encabezado. Agregue estas líneas para solucionar el problema:-MMD
permite construir archivos .d que contienen fragmentos de Makefile sobre dependencias de encabezados. La segunda línea solo los usa.Por supuesto, un Makefile bien escrito también debe incluir
clean
ydistclean
reglas:Aviso,
$(RM)
es el equivalente derm -f
, pero es una buena práctica no llamarrm
directamente.La
all
regla también es apreciada. Para que funcione, debería ser la primera regla de su archivo:También puede agregar una
install
regla:DESTDIR
está vacío por defecto. El usuario puede configurarlo para instalar su programa en un sistema alternativo (obligatorio para el proceso de compilación cruzada). Los mantenedores de paquetes para distribución múltiple también pueden cambiarPREFIX
para instalar su paquete/usr
.Una última palabra: no coloque los archivos fuente en subdirectorios. Si realmente desea hacer eso, manténgalo
Makefile
en el directorio raíz y use rutas completas para identificar sus archivos (es decirsubdir/file.o
).Para resumir, su Makefile completo debería verse así:
fuente
make
que conozco (GNU Make y BSD Make) necesitan líneas vacías entre las reglas. Sin embargo, existen toneladas demake
implementaciones con sus propios errores ^ Wspecificities.Solía respuesta de friedmud . Investigué esto por un tiempo, y parece ser una buena manera de comenzar. Esta solución también tiene un método bien definido para agregar indicadores de compilación. Respondí nuevamente, porque hice cambios para que funcionara en mi entorno, Ubuntu y g ++. Más ejemplos de trabajo son el mejor maestro, a veces.
Los makefiles parecen ser muy complejos. Estaba usando uno, pero estaba generando un error relacionado con no vincular en las bibliotecas de g ++. Esta configuración resolvió ese problema.
fuente