-
Notifications
You must be signed in to change notification settings - Fork 1
/
gnumake.tex
340 lines (273 loc) · 13.5 KB
/
gnumake.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
%Autor: aplatanad
%aplatanad: 7
\chapter{GNU Make}\label{make}
\label{gnumake.tex}
A la hora de compilar un programa resulta evidente que utilizar la línea
de comandos es molesto, incluso cuando se dispone de unos pocos archivos
de código fuente. Por ello es conveniente utilizar el programa {\sf {\sf
GNU Make}} para ayudarnos en dicho proceso.
{\sf GNU Make} sabe que tareas debe realizar a través de una serie
de reglas descritas en un archivo de texto de nombre {\tt Makefile},
habitualmente situado en el directorio de nuestro código fuente. El
comando {\tt make}\index{make}, al ser invocado desde dicho directorio,
ejecuta las reglas del archivo {\tt Makefile}\index{Makefile},
recompilando sólo las partes que han sido modificadas desde la última
compilación, y enlazando los archivos de código objeto para generar el
ejecutable.
{\sf GNU Make} puede usarse con cualquier lenguaje de programación cuyas
tareas puedan lanzarse mediante la línea de comandos. En realidad, puede
usarse para manejar cualquier conjunto de archivos dependientes entre
sí, en los cuales algunos deban actualizarse mientras que otros no.
\section{Makefile sencillo}
Tomaremos como ejemplo uno de los programas del tema de GNU C/C++
(véase tema \ref{gnuc}). Concretamente, utilizaremos el programa
compilado y ejecutado en la página \pageref{holafuncm}. Un ejemplo de
{\tt Makefile}, para compilar y enlazar dicho programa, puede ser el
siguiente:
\begin{ejemplo}{makefile1.mak}{Makefile sencillo}
Makefile sencillo para compilar y enlazar el programa ejecutado en la
página \pageref{holafuncm}.
\end{ejemplo}
Las líneas que comienzan con {\tt \#} son comentarios, no se ejecutan.
El resto son reglas que definen como generar el programa {\tt
holamundo}. Cada regla empieza con un nombre seguido de {\tt :}. A
continuación de cada regla pueden haber una o más líneas, iniciadas
con tabulador, que especifican que comandos deben ser ejecutados para
cumplir con dichas reglas.
Si guardamos {\tt makefile1.mak} con el nombre {\tt Makefile} en el
directorio donde está el código fuente del programa, podemos generar y
probar nuestro programa de la siguiente manera:
\begin{verbatim}
$ make
$ ./holamundo
El que a buen árbol se arrima, buena sombra lo cobija,
y el coseno de 0.500000 es 0.877583.
\end{verbatim}
También es posible utilizar la opción {\tt -f nombre\_makefile} para
especificar un nombre de archivo diferente a {\tt Makefile}. Por
ejemplo:
\begin{verbatim}
$ make -f makefile1.mak
$ ./holamundo
El que a buen árbol se arrima, buena sombra lo cobija,
y el coseno de 0.500000 es 0.877583.
\end{verbatim}
Como se puede apreciar, {\tt make} simplifica notablemente el proceso de
compilación y enlazado de nuestra aplicación.
\section{Reglas en los Makefile}
Las reglas descritas en los {\tt Makefile} siempre tienen el siguiente
formato:
\begin{verbatim}
destino: requisito ...
comando
...
\end{verbatim}
\begin{description}
\item[Destino] Es el nombre de un archivo a crear. Normalmente un
ejecutable o un archivo de código objeto ({\tt *.o}). También puede ser
el nombre de una tarea a realizar. Por ejemplo, es habitual utilizar
{\em clean} como el {\em destino} de una regla que ejecuta los comandos
necesarios para eliminar los archivos de código objeto y otros archivos
de uso intermedio.
\item[Requisito] Es el nombre de un archivo del cual depende el
{\em destino} a crear. Un destino suele depender de varios archivos
requisito. Cuando el destino define el nombre de una tarea, no se
definen requisitos.
\item[Comando] Define una acción a realizar para generar un {\em
destino}. Puesto que la creación de un destino puede requerir varios
comandos, estos deben ser indicados en línea sucesivas, cada una de las
cuales debe comenzar con un tabulador (no sirven los espacios). Los
comandos son pasados al intérprete de comandos, el cual se encarga de
interpretarlos y ejecutarlos.
\end{description}
En el ejemplo anterior vemos un elemento adicional que no hemos
comentado, {\tt $\backslash$} es el caracter de continuación de línea.
Colocado al final de una línea, une dos líneas consecutivas como si
fueran una sola. Se utiliza para facilitar la lectura.
Cuando ejecutamos {\tt make} es necesario indicar el {\em destino} que
queremos que sea generado. De lo contrario, se generará el {\em destino}
por defecto, que es el definido en la primera regla del {\tt Makefile}.
Por ejemplo, si utilizamos el {\tt Makefile} anterior y ejecutamos {\tt
make} de la siguiente manera:
\begin{verbatim}
$ make clean
rm holamundo \
holamain.o holafuncm.o
\end{verbatim}
Tanto el ejecutable como los archivos de código objeto de nuestra
aplicación serán borrados. Sin embargo, si lo hacemos así:
\begin{verbatim}
$ make
gcc -c holamain.c
gcc -c holafuncm.c
gcc -lm -o holamundo holamain.o holafuncm.o
\end{verbatim}
El comando {\tt make} toma {\tt holamundo} como destino por defecto y
ejecuta los comandos adecuados para generarlo conforme a las reglas
descritas por el {\tt Makefile}. Evidentemente, se consigue el mismo
efecto si ejecutamos {\tt make holamundo}.
Una de las funcionalidades más importantes de {\tt make} es que evita
generar los {\em destinos} ya existentes, o cuyos archivos de {\em
requerimiento} no hayan cambiado. Para ello {\tt make} estudia las
dependencias entre {\em destinos}. Por ejemplo, si ejecutamos {\tt
make} con nuestro sencillo {\tt Makefile} de pruebas, el programa
detectará, a partir de las reglas, una dependencia entre el ejecutable
{\tt holamundo} y los archivos de código objeto {\tt holamain.o} y {\tt
holafuncm.o}. Y que estos a su vez dependen de {\tt holamain.c} y {\tt
holafunc.h}, y {\tt holafuncm.c} y {\tt holafunc.h} respectivamente. El
programa {\tt make} es capaz de detectar si alguno de los archivos de
código fuente ({\tt holamain.c}, {\tt holafuncm.c} u {\tt holafunc.h})
fue modificado después de la última ejecución. En caso de ser así, {\tt
make} invoca los {\em comandos} de la regla o reglas adecuadas para
garantizar que {\tt holamain.o} y {\tt holafuncm.o} existen y están
actualizados con respecto a los archivos de código fuente de los que
dependen. Hecho esto, pasa a verificar si el ejecutable {\tt holamundo}
existe y está actualizado con respecto a los archivos de código objeto
de los que depende. De no ser así, se ejecutan los {\em comandos}
definidos en su regla con el fin de actualizar el ejecutable. Por lo
tanto, {\sf GNU Make} garantiza que tengamos nuestro binario actualizado
empleando el mínimo número de pasos posible.
\section{Macros o variables}
Las macros nos permiten definir constantes que utilizamos frecuentemente
en nuestro archivo. La definición de una macro tiene la siguiente forma:
\begin{verbatim}
MACRO1 = cadena de texto
MACRO2 = otra cadena de texto
\end{verbatim}
Las macros pueden ser referenciadas indicando el nombre entre paréntesis
({\tt ()}) o llaves ({\tt \{\}}) precedidas por el signo del dolar ({\tt
\$}). Por ejemplo, las macros anteriores serían referenciadas como:
\begin{verbatim}
$(MACRO1)
${MACRO2}
\end{verbatim}
Algunas definiciones de macros válidas podrían ser:
\begin{verbatim}
LIBS = -lm
OBJECTS = holamain.o holafuncm.o
INCLUDES = holafunc.h
DEBUG_FLAG = # poner -g para depurar
\end{verbatim}
Los nombres de las macros pueden usar cualquier combinación de letras,
números y subrayados. Además, por convenio, suelen definirse en
mayúsculas. Los valores pueden ser nulos como en la definición anterior
de {\tt DEBUG\_FLAG}, que nos muestra como un comentario puede ir a
continuación de una definición. La propia definición de una macro puede
contener referencias a otras macros definidas anterior o posteriormente.
Una características interesante de {\sf GNU Make} es la posibilidad de
definir macros desde la línea de comandos. Por ejemplo, podemos definir
un valor para {\tt DEBUG\_FLAG}, que de otra forma tendría un valor
nulo.
\begin{verbatim} $ make DEBUG_FLAG=-g \end{verbatim}
Las definiciones que incluyan espacios deben ir encerradas entre
comillas dobles o simples, para que el intérprete de comandos se las
entregue a {\tt make} como un único argumento. Por ejemplo:
\begin{verbatim}
$ make "LIBS= -lm -lX11"
\end{verbatim}
Las variables de entorno están disponibles, durante la ejecución de {\tt
make}, como cualquier otra macro definida en el {\tt Makefile}.
\subsection{Macros especiales}
{\sf GNU Make} define algunas macros internas para simplificar nuestro
trabajo. Podemos invocar {\tt make} con la opción {\tt -p} para obtener
una lista de todas las macros, reglas y destinos que intervienen en la
ejecución de {\tt make}.
Además, hay unas pocas macros especiales que se definen en cada regla.
Las más útiles a la hora de crear nuestro {\tt Makefile} están descritas
en el Cuadro \ref{makemacro}.
\begin{table}[hbtp]
\begin{tabular}{|c|p{10cm}|}
\hline
Variable & Contenido \\
\hline
{\tt @} & El nombre del archivo de {\em destino} de la regla \\
\verb"<" & El nombre del primer {\em requisito} \\
{\tt ?} & Los nombres de todos los {\em requisitos} que son más nuevos
que el {\em destino}, con espacios entre ellos \\
\verb"^" & Los nombres de todos los {\em requisitos} \\
\hline
\end{tabular}
\caption{Macros internas especiales de {\sf GNU Make}}\label{makemacro}
\end{table}
Sabiendo todo esto podemos hacer uso de las macros para simplificar
nuestro {\tt Makefile} de ejemplo:
\begin{ejemplo}{makefile2.mak}{Makefile con macros}
Makefile para compilar y enlazar el programa ejecutado en la página
\pageref{holafuncm}. Se han utilizado las macros de {\tt make} para
simplificar y flexibilizar las reglas descritas en el archivo.
\end{ejemplo}
Las primeras líneas se utilizan para definir las macros que serán
utilizadas en el resto de nuestro programa. Por ejemplo, definimos en
{\tt LINK} el enlazador a utilizar, que será el definido internamente
por {\tt make} en {\tt CC}; habitualmente el {\tt gcc}. Mientras que las
variables {\tt CFLAGS} y {\tt LDFLAGS} especifican las opciones para el
compilador y el enlazador respectivamente. En nuestro caso, indicamos
con {\tt -lm} que queremos enlazar la biblioteca libm, con {\tt -g} que
deseamos incluir el código de depuración en el ejecutable de nuestro
programa, y con {\tt -Wall} que el compilador debe avisarnos a la más
mínima sospecha de un posible error en el programa.
Al utilizar el operador {\tt +=} durante la asignación, indicamos que
los nuevos valores deben añadirse a los que ya tuvieran dichas macros
previamente. Es decir, indicamos algo similar a:
\begin{verbatim}
CFLAGS = $(CFLAGS) -g -c
LDFLAGS = $(LDFLAGS) -lm
\end{verbatim}
Solo que expresarlo de esa manera no está permitido. Los valores previos
de las variables pueden haber sido definidos internamente por {\tt
make}, especificados desde la línea de comandos durante la invocación
del mismo, o a través de la definición de variables de entorno del mismo
nombre desde el intérprete de comandos.
\section{Reglas implícitas}
Ciertas formas de generar un {\em destino} son utilizadas
frecuentemente. Por ejemplo, la forma de obtener una archivo de código
objeto desde un archivo de código fuente usando un compilador de C como
{\tt gcc}.
Las reglas implícitas evitan tener que especificar de forma detallada
este tipo de dependencias cuando tienen que ser usadas. Por ejemplo,
habitualmente el nombre los archivos de código fuente en C termina
en {\tt .c}, mientras que el nombre de los archivos de código objeto
termina en {\tt .o}. Se puede crear una regla implícita que indique a
{\tt make} como compilar programas en C cuando detecta dichos sufijos en
los nombres de los archivos.
Un procedimiento para definir reglas implícitas es con la ayuda de
reglas de sufijo. Por ejemplo, la siguiente regla describe como generar
un archivos de código objeto ({\tt *.o}) a partir de un archivo de
código fuente en C ({\tt *.c}):
\begin{verbatim}
.c.o:
$(CC) $(CFLAGS) -c $<
\end{verbatim}
Sin embargo, en la actualidad se tiende a utilizar reglas de patrones
por ser mucho más flexibles.
\begin{verbatim}
%.o: %.c
$(CC) $(CFLAGS) -c $<
\end{verbatim}
Como podemos apreciar, en las reglas de patrones el caracter {\tt \%}
hace de comodín.
Con todo esto, nuevamente podemos simplificar nuestro {\tt Makefile}:
\begin{ejemplo}{makefile3.mak}{Makefile con reglas implícitas}
Makefile para compilar y enlazar el programa ejecutado en la página
\pageref{holafuncm}. Se han utilizado reglas implícitas para simplificar
el archivo y hacerlo más general.
\end{ejemplo}
Para ayudarnos a la hora de crear nuestro {\tt Makefile}, {\sf GNU
Make} incluye una serie de reglas implícitas definidas internamente.
Estas definiciones pueden consultarse en la documentación del programa,
ejecutando {\tt info make}, o con ayuda de la opción {\tt -p} del mismo.
En cualquier caso, las reglas más comunes, como la de compilar archivos
de código fuente en C, están ya definidas, por lo que nuestro {\tt
Makefile} puede simplificarse aún más.
\begin{ejemplo}{makefile4.mak}{Makefile con reglas implícitas internas}
Makefile para compilar y enlazar el programa ejecutado en la página
\pageref{holafuncm}. Se han utilizado reglas implícitas internas para
simplificar el archivo.
\end{ejemplo}
{\sf GNU Make} es un programa muy potente que no permite extender
nuestro {\tt Makefile} con nuevos {\em destinos} que se encarguen de
tareas como: generar la documentación de ayuda, que generar bibliotecas
de enlace dinámico con las funciones de uso más frecuente, que generar
otros ejecutables que formen parte de la aplicación, e incluso
empaquetar nuestro programa y lo dejarlo listo para su instalación en
cualquier sistema.