2012-10-10 44 views
5

Soy estudiante de ingeniería informática y tengo otro programa C para implementar. Hasta ahora siempre creo un nuevo Makefile de mis proyectos anteriores y esto lleva tiempo configurarlo todo el tiempo. ¿Existe un lienzo de Makefile que pueda usar que sea tan poderoso que todo lo que necesite proporcionar es el nombre de los archivos c que contienen una función principal?Buscando el lienzo Makefile perfecto

Mi idea sería que para cada nuevo proyecto, creo una carpeta que contiene ese Makefile, una carpeta bin y una carpeta src. Luego, editaba algunas variables en el Makefile (los archivos C que contenían una función main()) y luego make creaba automáticamente todo, tomando en cuenta las dependencias.

¿Sabe si existe tal Makefile?

[Editar a través de Alexandre C.]: Automake/Autoconf son excesivas para este tipo de proyectos que usan solo bibliotecas de estándares y se ejecutan en un sistema operativo estándar. Para los proyectos que necesitamos implementar, las dependencias (para los archivos a vincular) siempre se pueden deducir de la ".h" incluida y generalmente hay muy pocos archivos involucrados.

+1

He creado varias plantillas de Makefile en el pasado, con más o menos cosas (generación automática de dependencias, etc.). Lo mejor sigue siendo hacer tuyo usando la gran documentación de Gnu Make. Incluso puedes usar cosas como 'src = $ (shell find. -name" * .c ")', o '$ (grep -H" main (")'. No es muy difícil, y sientes que controlas tu Entorno –

+0

@AlexandreC. ¿Podría proporcionar un script o un Makefile que podría usar para escribir el mío? – Mat

+1

@Mat: Hay muchos en Internet (ejemplo [aquí] (http://blog.vjeux.com/2009/ makefile/makefile-automatic-dependencies-with-makedepend.html)). Le recomiendo que revise la documentación de Gnu Make.Se necesita menos de una tarde para convertirse en un experto en Make, y este conocimiento será muy útil en su carrera. En particular, aprenda a usar las reglas integradas (para que sepa cuándo dejarlas caer). –

Respuesta

0

me ocurrió con la siguiente solución:

# Author Matthieu Sieben (http://matthieusieben.com) 
# Version 23/10/2012 
# 
# This Makefile is licensed under the Creative Commons Attribution 
# Partage dans les Mêmes Conditions 2.0 Belgique License. 
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/2.0/be/. 
# Use at your own risk. 
# 
# Use Makefile.inc to write you own rules or to overwrite the default values defined here. 

SRCDIR = ./src 
BINDIR = ./bin 

SHELL = /bin/bash 
CC  = /usr/bin/gcc 
LIBS = 
CFLAGS = -Wall -Wextra -Werror 
LDFLAGS = 

.PHONY: default all clean distclean 
.SUFFIXES: .c .o 

# make "all" the default target 
default: all 

-include Makefile.inc 
-include Makefile.d 

CFLAGS += -I$(SRCDIR) 
ifeq ($(DEBUG), 1) 
    CFLAGS += -DDEBUG=1 -ggdb 
else 
    CFLAGS += -DDEBUG=0 -O2 
    LDFLAGS += -O2 
endif 

%.o: %.c 
    @echo " Compiling `basename $<`"; 
    @$(CC) $(CFLAGS) -o [email protected] -c $<; 

clean: 
    @echo "Cleaning compiled files"; 
    @find $(SRCDIR) -name "*.o" -exec rm {} \; 
    @[ -e Makefile.d ] && rm Makefile.d; 

distclean: clean 
    @echo "Removing executables"; 
    @find $(BINDIR) -type f -exec rm {} \; 

Makefile.d: $(shell find $(SRCDIR) -type f -name "*.c") 
    @echo "Building dependencies"; 
    @for file in `find $(SRCDIR) -name "*.c" -print`; do\ 
     $(CC) $(CFLAGS) -MM -MT $${file/%.c/.o} $$file | tr -d "\n\\" >> Makefile.d.tmp;\ 
     echo -e "\n" >> Makefile.d.tmp;\ 
    done;\ 
    for file in `find $(SRCDIR) -name "*.c" -exec grep -Hs "main(" {} \; | cut -f1 -d':'`; do\ 
     execname=`basename $$file .c`;\ 
     objs="$${file/%.c/.o}";\ 
     for header in `$(CC) $(CFLAGS) -MM $$file | tr " " "\n" | grep ".h$$" | sort | uniq | tr "\n" " "`; do\ 
      if [ -f $${header/%.h/.c} ]; then\ 
       objs+=" $${header/%.h/.o}";\ 
      fi;\ 
      for obj in `grep "^$${header/%.h/.o}" Makefile.d.tmp | tr " " "\n" | grep ".h$$" | tr "\n" " "`; do\ 
       if [ -f $${obj/%.h/.c} ]; then\ 
        objs+=" $${obj/%.h/.o}";\ 
       fi;\ 
      done;\ 
     done;\ 
     objs=`echo -n "$$objs" | tr " " "\n" | sort | uniq | tr "\n" " "`;\ 
     echo "all: $$execname" >> Makefile.d.tmp;\ 
     echo "$$execname: $(BINDIR)/$$execname" >> Makefile.d.tmp;\ 
     echo "$(BINDIR)/$$execname: $$objs" >> Makefile.d.tmp;\ 
     echo " @echo \"Linking $$execname\";" >> Makefile.d.tmp;\ 
     echo " @\$$(CC) \$$(LDFLAGS) -o \[email protected] $$objs \$$(LIBS);" >> Makefile.d.tmp;\ 
     echo >> Makefile.d.tmp;\ 
    done;\ 
    mv Makefile.d.tmp [email protected];\ 

No es muy robusta, así que siéntete libre para proporcionar una mejora. Así es como funciona. La idea aquí es que, para cada archivo .c en $(SRCDIR), busque aquellos que contengan una función principal (grep "main(" src/*.c). Luego, para cada locales incluyen #include "myfile.h" añadir myfile.o en la lista de objetos para ese ejecutable y luego escribir las líneas más arriba en Makefile.d

Editar Esta nueva versión del script soporta dependencias para la vinculación de los archivos del compartimiento.

+0

Bueno ... esto todavía no es perfecto ya que los archivos no se cargan recursivamente. Si alguien tiene una idea ... – Mat

3

Lo más parecido a un mágico y perfecto Makefile es utilizar el estándar de facto para programas de C portátiles y bibliotecas: Automake.

Como un ejemplo sacado de this page, este es el ejemplo de lo que se puede añadir a Makefile.am:

bin_PROGRAMS = zardoz 
zardoz_SOURCES = main.c head.c float.c vortex9.c gun.c 
zardoz_LDADD = $(LIBOBJS) 

El equivalente a un objetivo de hacer lo que estamos añadiendo que aquí se llama zardoz. Las fuentes se especifican en zardoz_SOURCES y las bibliotecas adicionales que deben vincularse se especifican en zardoz_LDADD. No necesita especificar dónde vive la función main, ya que el vinculador la encontrará automáticamente en la etapa de enlace. Si no está presente, el enlace simplemente fallará.

+1

¿Puede proporcionar un ejemplo de trabajo? –

+4

@AlexBrown: Nadie puede hacer un ejemplo de trabajo para automake :-( –

+1

Automake es probablemente excesivo para proyectos de estudiantes que se ejecutarán en entornos estereotípicos. Las autotools son para garantizar la portabilidad (y esto tiene un gran costo de instalación y curva de aprendizaje) –

0

Yo también recomiendo autotools, usted aprenderá a apreciarlo con el tiempo y definitivamente vale la pena, puede buscar un tutorial sobre cómo usarlo o, si no le importa revisar un libro pequeño, sugiero leer la autobook sin embargo, si lo que desea es una plantilla Makefile sencillo, yo uso esto:

BIN  = <binary name> 
OBJ  = <object files *.o> 
LIB  = <libraries> 
CFLAGS = -O2 -Wall -I. 
LDFLAGS = <ld flags> 

CC = gcc 
LD = ld 
RM = rm -f 

all:: $(BIN) 

$(BIN): $(OBJ) 
    $(CC) $(LDFLAGS) $(OBJ) $(LIB) -o $(BIN) 

clean: 
    $(RM) $(BIN) *.o 

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


    PROGRAM = test-program 
    CFLAGS = -c -Wall 

    OBJECTS += $(patsubst %.c, %.o, $(wildcard ./src/*.c)) 
    OBJECTS += $(patsubst %.c, %.o, $(wildcard ./src/more-sources*.c)) 

    all: $(OBJECTS) 
     gcc -o ./bin/$(PROGRAM) $(OBJECTS) 

    %.o: %.c %.h 
     gcc $(CFLAGS) $< -o [email protected] 

 

Algo como esto hará el truco. Obviamente no he probado esto .. Opcionalmente puede anular la compilación predeterminada por archivo.

+0

Prefiero 'src = $ (shell find. -name" * .c ")', porque es más flexible que '$ (wildcard)', pero aquí hay muchas maneras. –

+0

Interesante. ¿Funcionaría eso en decir que Windows ejecuta MinGW o similar? – Honeymustard

+0

Necesita una capa de bourne-enough y el programa de búsqueda. Esto debería funcionar con cygwin, o lo que sea que proporcione bash y findutils. –

1

Puede escribir fácilmente su propio macro "paquete" que hace esto.Por ejemplo, crear este archivo como su repetitivo, lo llaman program.mk y lo puso en un lugar central dentro de su árbol:

lang.c.objs = $(patsubst %.c,%.o,$(1)) 
lang.c.link = $(CC) $(CFLAGS) $(LDFLAGS) -o $(1) $(2) 
lang.c++.objs = $(patsubst %.cpp,%.o,$(1)) 
lang.c++.link = $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(1) $(2) 

define make-program 
    program.$(1).lang ?= c 
    program.$(1).objects ?= $$(call lang.$$(program.$(1).lang).objs,$$(program.$(1).sources)) 
    $$(program.$(1).name): $$(program.$(1).objects) $$(program.$(1).extra-deps) 
     $$(call lang.$$(program.$(1).lang).link,[email protected],$$^ $$(program.$(1).ldlibs)) 
    CLEANABLE += $$(program.$(1).name) 
    ALL_PROGRAMS += $$(program.$(1).name) 
endef 

# If the user didn't specify a list of programs, build them all 
ifndef PROGRAMS 
    PROGRAMS = $(foreach p,$(filter program.%.name,$(.VARIABLES)),\ 
    $(patsubst program.%.name,%,$(p))) 
endif 

# Generate the rule to build each program 
$(foreach p,$(PROGRAMS),$(eval $(call make-program,$(p)))) 

.PHONY: all clean 
all: $(ALL_PROGRAMS) 

clean: ; rm -f $(CLEANABLE) 

.DEFAULT_GOAL := all 

Ahora, en cada directorio en el que desea construir un programa, su makefile puede ser algo como :

program.p.name = Program 
program.p.sources = Program1.c Program2.c 

include path/to/program.mk 

Se puede utilizar una biblioteca similar library.mk para bibliotecas. Este enfoque es bastante poderoso y muy fácil de ampliar.

0

Si está interesado en entornos UNIX estándar, ¿por qué no simplemente quedarse con lo que proporciona POSIX make?

# Lean and mean. 
.POSIX: 

CFLAGS = -DNDEBUG -O 
LDFLAGS = -s 

TARGETS = abc def 

all: $(TARGETS) 

clean: 
    rm -f $(TARGETS) 

# Some dependencies. 
abc: def.h 

# Some special rules. 
def: def.c 
    $(CC) $(CFLAGS) $(LDFLAGS) -o [email protected] def.c -l m 

Para la mayoría de los proyectos pequeños, no debería necesitar nada más elaborado que los valores predeterminados; probablemente deberías concentrarte en el proyecto en lugar de perder el tiempo escribiendo complicadas lógicas de archivo.

Una de las limitaciones de POSIX make es que no hay reglas de patrones, y las reglas de sufijos no pueden manejar directorios; por lo tanto, la distribución de los archivos de origen en diferentes directorios requerirá una única regla para cada archivo o una creación recursiva. Personalmente, para proyectos pequeños, no me molesto en mover archivos fuente a otra parte. El cumplimiento de POSIX make también brindará portabilidad a través de otros sistemas de compilación POSIX compatibles (BSD make, por ejemplo).

En cuanto al cálculo de la dependencia automática, lea acerca de gcc -M. Genera archivos make que contienen reglas de dependencia. A continuación, puede include en su archivo MAKE. Sin embargo, en mi opinión, una mejor idea es rastrear todas las dependencias importantes en un único archivo de encabezado y hacer que cada archivo dependa de este encabezado a través de una regla de sufijo. No es una molestia, y aún así puedes ahorrar algo de portabilidad.

Resumiendo, mi sugerencia es evitar preocuparse demasiado por las complicaciones. Ellos rara vez pagan. Particularmente, nunca me ha gustado el autotools, y ciertamente nunca lo seré. UNIX tiene que ver con la simplicidad. Complicar innecesariamente las cosas va en contra del espíritu de UNIX. :)