tutorial make español entre ejecutar diferencia con compilar comando c file makefile

español - Varios archivos de origen en C- ¿Cómo funcionan exactamente los archivos make?



makefile linux c (6)

Sobre la compilación de C / C ++

Cuando tienes un conjunto de archivos, normalmente haces 2 cosas:

  • compilar cada archivo fuente (.c) en un archivo objeto (.o)
  • vincular todos los archivos de objetos en un ejecutable.

Los archivos de origen son independientes : necesita archivos de encabezado para poder proporcionar la "información" (declaraciones) sobre las funciones en un módulo determinado para que cualquier otro módulo pueda usarlas. Los archivos de encabezado no se compilan solos, son #include d como parte de los archivos de origen.

Eche un vistazo a continuación sobre cómo se ven los comandos para ese aspecto y cómo son manejados por make .

Acerca de makefiles

Makefile es un conjunto de objetivos y reglas para construirlos. Un objetivo es "algo que puede construirse y da como resultado un archivo dado". (También existen objetivos "falsos" que no dan como resultado un archivo y solo están allí para ejecutar comandos, uno común se llama clean para eliminar los resultados de la compilación).

Cada objetivo tiene 2 partes:

  • lista de dependencias , "lista de sensibilidad" (otros archivos y objetivos que son necesarios para este objetivo) (después de : separados por comas),
  • lista de comandos de shell que se ejecutan para construir este objetivo (debajo de lo anterior, con sangría)

Considera este ejemplo:

main: main.o module1.o module2.o g++ main.o module1.o module2.o -o main

Esto significa que: "Para compilar el archivo main , primero necesito asegurarme de que los objetivos main.o , module1.o y module2.o estén actualizados; luego debo llamar el siguiente comando ...".

Esto también puede ser reescrito como:

main: main.o module1.o module2.o gcc $^ -o $@

Las variables (todo lo que comienza con $ es una variable) se expandirán a la lista de dependencias y al nombre de destino, como espera.

Puedes definir tus propias variables y expandirlas de la siguiente manera:

OBJS = main.o module1.o module2.o main: $(OBJS) # code goes here

Usted compila unidades de traducción individuales de la siguiente manera:

main.o: main.c gcc -c $< -o $@ # note the -c option, which means: "compile, but don''t link" # $< will expand to the first source file

Puede agregar dependencias de encabezado para reconstruir main.o cuando se cambie main.c o cualquiera de sus encabezados:

main.o: main.c module1.h module2.h gcc -c $< -o $@

Para no escribir el mismo comando una y otra vez, puede definir una regla general y simplemente proporcionar las dependencias (si lo desea):

%.o: %.c gcc -c $< -o $@ main.o: main.c module1.h module2.h module1.o: module1.c module1.h module2.h

También hay algo de magia para generar las dependencias automáticamente (ver enlace) . Una de las desventajas de usar Make es que no lo hace por sí mismo (como hacen algunos sistemas de construcción, como SCons, que prefiero para C / C ++).

Soy un principiante relativo a C y necesito aprender cómo funcionan los makefiles y estoy un poco confundido sobre cómo funciona la combinación de archivos C. Digamos que tenemos un main.c, un foo.c, y un bar.c. ¿Cómo debe escribirse el código para que main.c reconozca funciones en los otros archivos? Además, en foo.c y bar.c, ¿está todo el código escrito en la función principal allí o necesitamos escribir otras funciones para lo que necesitamos que hagan? He leído tutoriales sobre cómo se escriben los makefiles, y tiene sentido en su mayor parte, pero todavía estoy un poco confundido en la logística básica de la misma.


¿Cómo funciona Makefile?

=> Cuando el comando make se ejecuta en el terminal, busca un archivo llamado makefile o Makefile en el directorio actual y construye un árbol de dependencias.

Si tiene varios Makefiles, entonces puede ejecutar específicos con el comando:

make -f MyMakefile

=> Según el destino de make especificado en makefile, verifique si existen los archivos de dependencia de ese destino. Y si existen, ya sean más recientes que el objetivo en sí mismo, comparando las marcas de tiempo de los archivos.

Here our first and default target is “all” which looks for main.o and function.o file dependencies. Second and third target is main.o and function.o respectively which have dependencies of main.c and function.c respectively.

=> Antes de ejecutar los comandos del destino correspondiente, se deben cumplir sus dependencias; cuando no se cumplen, los objetivos de esas dependencias se ejecutan antes del destino make dado, para proporcionar las dependencias faltantes.

=> Cuando un destino es un nombre de archivo, make compara las marcas de tiempo del archivo de destino y sus archivos de dependencia. Si el archivo de dependencia es más nuevo que el archivo de destino, el objetivo de ejecución no se ejecutará.

In our case, when first target “all” start executing it looks for main.o file dependency, if its not met. Then it goes to second target main.o which check for its dependency main.c and compare time-stamp with it. If target found main.c dependency is updated, then target execute else not. Same process is follow for next target function.o.

=> Por lo tanto, termina recursivamente comprobando todo el camino hacia el árbol de dependencias, hasta los archivos de código fuente. Mediante este proceso, ahorre tiempo, ejecutando solo los comandos que deben ejecutarse, en función de cuál de los archivos de origen (enumerados como dependencias) se han actualizado, y tienen una marca de tiempo más nueva que su objetivo.

=> Ahora, cuando un objetivo no es un nombre de archivo (al que llamamos "objetivos especiales"), obviamente no se pueden comparar las marcas de tiempo para verificar si las dependencias del objetivo son más nuevas. Por lo tanto, ese objetivo siempre se ejecuta.

In our Makefile, special targets are “all” and “clean”. As we discussed target “all” earlier, but we not discuss target clean. Target clean removes the all object files created during compilation and binary executable files according to command.

Para la ejecución de cada objetivo, realice impresiones de las acciones mientras las ejecuta. Tenga en cuenta que cada uno de los comandos se ejecuta en un entorno de sub-shell separado debido a la ejecución segura, por lo que no pueden cambiar el entorno de shell actual que puede afectar a otra ejecución de destino. Por ejemplo, si un comando contiene cd newdir, el directorio actual se cambiará solo para ese comando de línea, para el siguiente comando de línea, el directorio actual no se modificará.

Fuente: - http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/


En esencia un makefile se compone de reglas de la forma:

<this file> : <needs these files> <and is created by this command>

Normalmente tiene al menos un objetivo de alto nivel, si alguna de sus dependencias no existe, busque una regla que tenga ese archivo como objetivo. Hace esto de forma recursiva hasta que haya resuelto todas las dependencias del objetivo de nivel superior, antes de ejecutar el comando de nivel superior (si hay uno, ambas dependencias y comando son campos opcionales en una regla)

Un archivo make puede tener "reglas predeterminadas" basadas en patrones, y hay macros incorporadas para varios escenarios de coincidencia de archivos, así como también las macros definidas por el usuario y la inclusión de archivos make anidados.

He simplificado la forma de la regla anterior para el caso más habitual. De hecho, el comando no necesita crear el objetivo en absoluto, es simplemente un comando que se ejecutará una vez que todos los archivos en la dependencia estén presentes. Además, el objetivo tampoco tiene que ser un archivo. A menudo, el objetivo de nivel superior es un objetivo "ficticio" llamado "todos" o similar.

Por supuesto, hay muchas sutilezas y matices para hacer, todos detallados en el manual (GNU make específicamente, hay otras utilidades de make).


En general, lo que sucederá es que definirá sus funciones para los otros archivos en un archivo de encabezado, que luego se puede incluir en main.c. Por ejemplo, considere estos fragmentos:

C Principal:

#include "foo.h" int main(int argc, char *argv[]) { do_foo(); return 0; }

foo.h:

void do_foo();

foo.c:

#include <stdio.h> #include "foo.h" void do_foo() { printf("foo was done/n"); }

Lo que sucederá es que main.c se convertirá en un archivo de objeto (main.o), y foo.c se convertirá en un archivo de objeto (foo.o). Luego, el enlazador enlazará estos dos archivos juntos y ahí es donde la función do_foo() en main.c está ''asociada'' con la función en foo.o.

Ejemplo del comando GCC : gcc -o myprogram main.c foo.c

Ejemplo makefile

myprogam: main.o foo.o gcc -o myprogram main.o foo.o main.o: main.c foo.h gcc -c main.c foo.o: foo.c foo.h gcc -c foo.c


Las funciones en foo.c que deben llamarse desde fuera foo.c deben tener prototipos en foo.h Los archivos externos que necesitan llamar a esas funciones deben #include "foo.h" . foo.c y bar.c ni siquiera deberían tener una función main() si son parte del mismo programa que main.c

Makefiles definir objetivos. Para programas simples, puedes tener un solo objetivo que compile todo. Los programas más complejos (leer: más grande) pueden tener objetivos intermedios (como foo.o) que permitirán evitar una compilación innecesaria. La forma en que make determina si es necesario o no recompilar un objetivo dado es si observa los tiempos de modificación de todos los requisitos previos (las cosas después de los dos puntos) y si alguno de ellos llega después de la última vez que se modificó el archivo de destino, se reconstruye.

Aquí hay un ejemplo muy simple:

C Principal:

#include "foo.h" int main() { fooprint(12); return 0; }

foo.c:

#include "stdio.h" #include "foo.h" void fooprint(int val) { printf("A value: %d/n", val); }

foo.h:

void fooprint(int val);

Makefile:

main: main.c foo.o gcc -o main main.c foo.o foo.o: foo.c gcc -c foo.c

Luego puede ejecutar make main y compilará foo.c en foo.o luego compilará main.c y lo vinculará con foo.o. Si modifica main.c , simplemente compilará main.c y lo vinculará con el foo.o ya creado.


Make tiene poco que ver con la estructura de un programa en C Todo lo que hace es definir un árbol de dependencias y ejecutar comandos cuando encuentra que las dependencias están fuera de control. Mi dicho, en un makefile:

foo.exe : foo.c bar.c baz.c

simplemente sez: foo.exe depende de foo.c, bar.c y baz.c. Esto, sotto vocce , se expande, usando el conjunto de reglas predeterminado de make, a algo como:

foo.exe : foo.obj bar.obj baz.obj foo.obj : foo.c bar.obj : bar.c baz.obj : baz.c

Make simplemente recorre el árbol de dependencias a partir de su raíz (en este caso, foo.exe). Si un objetivo no existe o si uno de los objetos de los que depende es más nuevo que el objetivo, se ejecutan los comandos asociados. Para corregir la dependencia.

Consulte Gestión de proyectos con Make from O''Reilly para obtener más de lo que probablemente desee saber.

En cuanto a la segunda parte de su pregunta, la respuesta es solo dos letras: K y R. Su The C Programming Language es sin duda uno de los mejores libros de programación de computadoras jamás escrito.