5

I'm a student in computer engineering and I have yet another C program to implement. Until now I always create a new Makefile from my previous projects and this take time to setup every time. Is there a Makefile canvas I could use that is so powerful that all I would need to provide is the name of the c files containing a main function?

My idea would be that for each new project, I create a folder containing that Makefile, a bin folder and an src folder. I would then edit some variables to the Makefile (the C files containing a main() function) and then make would automatically build everything up, taking dependencies into account.

Do you know if such a Makefile exists?

[edit via Alexandre C.] : Automake/Autoconf are overkills for these kind of projects which use only standards libraries and run on standard unix os'. For the projects we need to implement, dependencies (for the files to be linked) can always be deduces from the ".h" includes and there is generally very few files involved.

Mat
  • 1,022
  • 2
  • 9
  • 24
  • 1
    I've come up with various Makefile templates in the past, with more or less stuff (automatic dependency generation, etc). The best is still to roll your own using the great documentation of Gnu Make. You can even use stuff like `src = $(shell find . -name "*.c")`, or `$(grep -H "main(")`. It's not very hard, and you feel like you control your environment. – Alexandre C. Oct 10 '12 at 14:33
  • @AlexandreC. could you provide a script or Makefile that I could use to write my own ? – Mat Oct 10 '12 at 15:14
  • 1
    @Mat: There are plenty on the internet (exemple [here](http://blog.vjeux.com/2009/makefile/makefile-automatic-dependencies-with-makedepend.html)). I strongly advise you to walk through the documentation of Gnu Make. It takes less than one afternoon to become a Make expert, and this knowledge will be very useful in your career. In particular, learn to use the builtin rules (so that you know when to drop them). – Alexandre C. Oct 10 '12 at 15:34
  • 1
    Related: http://stackoverflow.com/questions/313778/generate-dependencies-for-a-makefile-for-a-project-in-c-c – Alexandre C. Oct 10 '12 at 15:39

7 Answers7

3

The closest to a magical and perfect Makefile is to use the de facto standard for portable C programs and libraries: Automake.

As an example lifted from this page, this is the example of what you would add to Makefile.am:

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

The equivalent of a make target which we are adding here is called zardoz. The sources are specified under zardoz_SOURCES and any additional libraries which need to be linked are specified under zardoz_LDADD. You do not need to specify where the main function lives, because the linker will automatically find this at the linking stage. If it is not present, the link will simply fail.

Mike Kwan
  • 24,123
  • 12
  • 63
  • 96
  • 4
    @AlexBrown: Nobody can make a working example for automake :-( – Kerrek SB Oct 10 '12 at 14:29
  • 1
    Automake is probably overkill for student projects which will run on stereotypical environments. Autotools are for guaranteeing portability (and this has a big setup cost and learning curve). – Alexandre C. Oct 10 '12 at 14:30
  • Automake is too big for what I need. Usually the projects i'm working on are quite small and only require a dozen c files to generate at mose 2 executables and using very standard libraries (sockets, pthreads, ...). I'm quite sure that it would be possible to write a quite simple `Makefile` without needing automake or autoconf. – Mat Oct 10 '12 at 14:32
  • 1
    @Mat: What problem do you have with using the 'template' that you effectively currently use? – Mike Kwan Oct 10 '12 at 14:36
  • @MikeKwan : I feel like it is possible to use a simple generic Makefile that wouldn't require me to manually write the dependencies among the .o files (in case I have two exectuables not using the same .o files for example). I mean, anyone that ready my code (and particularly the #includes "abc.h" and "int main()") would know how to compile it. Why wouldn't the computer be able to do the work? – Mat Oct 10 '12 at 14:44
  • @Mat: See http://stackoverflow.com/questions/313778/generate-dependencies-for-a-makefile-for-a-project-in-c-c for dependency generation. There are many ways to do it (I used to use `gcc -MM` but now I rely on autotools for this), and this question will teach you one solution. – Alexandre C. Oct 10 '12 at 15:38
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 $@


Something like this will do the trick. I obviously haven't tested this.. You can optionally override the default compilation on a per-file basis.

1

You can easily write your own macro "package" that does this. For example, create this file as your boilerplate, call it program.mk and put it in a central location within your tree:

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,$$@,$$^ $$(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

Now, in each directory where you want to build a program, your makefile can be something like:

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

include path/to/program.mk

A similar library.mk can be used for libraries. This approach is quite powerful and very easy to extend.

Idelic
  • 14,976
  • 5
  • 35
  • 40
0

I too recommend autotools, you will learn to appreciate it eventually and it's definitely worth it, you can look for a tutorial on how to use it or, if you don't mind going through a small book, I suggest reading the autobook however, if you just want a simple Makefile template, I use this:

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 $<
iabdalkader
  • 17,009
  • 4
  • 47
  • 74
0

you can use autoconf to automatically generate a Makefile by the famous command ./configure as the most opensource project do. Refer to this link: configure file template to generate makefile

Community
  • 1
  • 1
MOHAMED
  • 41,599
  • 58
  • 163
  • 268
0

If you're interested in standard UNIX environments, why not simply stick with what POSIX make provides?

# 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 $@ def.c -l m

For most small projects, you shouldn't need anything more elaborate than the defaults; probably you should concentrate on the project itself instead of wasting time writing complicated makefile logic.

One limitation of POSIX make is that there are no pattern rules, and suffix rules cannot handle directories; thus, spreading source files in different directories will either require a single rule for each file, or recursive making. Personally, for small projects I don't bother to move source files elsewhere. Sticking to POSIX make will also provide portability across other POSIX-compatible build systems (BSD make, for example).

Regarding automatic dependency calculation, read about gcc -M. It generates makefiles containing dependency rules. You can then include them in your makefile. However, in my opinion, a better idea is to track all important dependencies in a single header file, and make every file depend on this header through a suffix rule. It is not much of a hassle, and you still save some portability.

Summarizing, my suggestion is to avoid worrying too much about complications. They rarely pay off. Particularly, I've never been fond of autotools, and I'll certainly never be. UNIX is all about simplicity. Complicating things unnecessarily is against the UNIX spirit. :)

alecov
  • 4,882
  • 2
  • 29
  • 55
0

I came up with the following solution:

# 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 $@ -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 \$$@ $$objs \$$(LIBS);" >> Makefile.d.tmp;\
        echo >> Makefile.d.tmp;\
    done;\
    mv Makefile.d.tmp $@;\

It is not very robust, so feel free to provide an amelioration. Here is how it works. The idea here is, for each .c file in $(SRCDIR), look for those containing a main function (grep "main(" src/*.c). Then, for each local include #include "myfile.h" add myfile.o into the object list for that executable and then write the lines above in Makefile.d

Edit This new version of the script supports dependencies for the linking of bin files.

Mat
  • 1,022
  • 2
  • 9
  • 24
  • Well... this still isn't perfect since files are not recursively loaded. If anyone has an idea... – Mat Oct 23 '12 at 23:01