开发者

"Standard library" for building C/C++ projects with Make

Sometimes I need my project to use plain makefile, although it's a dated building technology not recommended for any use, but since make is available almost everywhere it sometimes makes sense.

However I want my Makefile to look something like

Include "../buildexec.mk"

TARG = my_exec

CPPFILES = file1.cpp \
           file2.cpp \

and have all the generic (and horrible) dependency tracking code being in buildexec.mk.

Is there such a "Make library"?

In Go, there's a standard Makefile you can include, and your Makefile looks as beautiful like this:

include $(GOROOT)/src/Make.inc

TARG=irc
GOFILES=irc.go irc_struct.go 开发者_JAVA技巧irc_callback.go

include $(GOROOT)/src/Make.pkg 

Anything similar for C++?

clarification: I know of tup, cmake and scons/ I know of waf and bjam and so on/ but I want my deps to so small/ so that compilation is no trouble at all. I specifically asked for Make support, not for Make alternative.


I would suggest generating the makefile with automake instead. Cmake also generates makefiles, while also being able to generate project files for several IDEs.

The problem is there is no one make. The very basic syntax is always the same, but anything slightly more complex (which you need for such includes) is not compatible even between GNU make and BSD make (not trying to mention nmake). Automake can however deal with several versions of make and provide automatic dependency rules where available (only some compilers support them).


You probably want to look at CMake or Premake.

With large C/C++ projects you tend to have additional requirements such as platform specific APIs or libraries that need customization.


This will do what you're asking for (at least in GNUMake).

makelib.mk:

$(TARG): $(CPPFILES:.cc=.o)
    $(CC) $^ -o $@

%.o : %.cc
    $(CC) -MD -o $@ $<                                               
    @cp $*.d $*.P; \                                                        
      sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \            
          -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \                 
      rm -f $*.d                                                       

-include $(CPPFILES:.cc=.P)

Makefile:

CPPFILES = foo.cc bar.cc
TARG = someTarget
include makelib.mk      # Note lower-case "i"

CPPFILES = baz.cc quartz.cc quince.cc
TARG = anotherTarget
include makelib.mk

...

But a word of advice: don't curse your tools so much. It's energy you could be using to either learn how to use them or switch to ones you like better.


There's a googlecode project doing exactly what I wanted:

http://code.google.com/p/nonrec-make/


There is none of my knowledge. There are really a lot of problems with make, but it is still the most available tool around, and once you get it running properly you should just focus on your development.

Until there, you will have to write makefiles. But instead of looking for a 'make library', a really simple solution (if you're actually initiated in Make language), often overlooked for being too obvious, is to implement your own. Create a make script containing a set of default pattern rules and variables for communication with the project makefile, and just include this script in every project's makefile. It's not hard, maybe a little time-consuming, but it often pays off really well, specially if you have lots of small projects to manage.

I work with such a design. I have a couple of GNU make scripts carefully designed to offer an almost trivial mechanism to create rather complex build systems: automatic dependency generation, handling of different languages, generation of language parsers, different build configurations (debug or release), build log generation, and so on. And the script is not cumbersome: the current version contains just about 250 lines of makefile code, excluding comments.

I will leave you with a sample of an older version of such system, handling C source code only, which contained a few features. It should handle the compilation of binaries and libraries (both static and dynamic). It also should help you track inter-project dependencies through the DEPS variable.

Call this $(ROOT)/project.mk:

# Remove the default suffix rules.
.SUFFIXES:

# Turn on the delete-on-error feature.
.DELETE_ON_ERROR:

# Set up additional command variables.
STRIP ?= strip

# Set up a global search path to locate prerequisites.
VPATH := $(VPATH) $(shell find -type d)

# Locate all source files from the default locations in the project tree.
SRC := $(SRC) $(shell find src -name '*.c')

# Set up the default dependency files.
DEP := $(DEP) $(addprefix dep/,$(addsuffix .d,$(basename $(notdir $(filter %.c,$(SRC))))))

# Set up the default object files.
OBJ := $(OBJ) $(addprefix obj/,$(addsuffix .o,$(basename $(notdir $(filter %.c,$(SRC))))))

# Set up a set of default flags for all commands used.
STRIPFLAGS ?= -p
CPPFLAGS ?= -DNDEBUG
CFLAGS ?= -Wall -Wextra -Werror -pedantic -O3 -march=native -fomit-frame-pointer -ffast-math
LDFLAGS ?= --as-needed -O1
ARFLAGS ?= -scr

# Set up the default include and library search paths.
override INCLUDES := \
    $(addprefix $(ROOT)/,$(addsuffix /include,$(DEPS))) \
    $(INCLUDES)
override LIBRARIES := \
    $(addprefix $(ROOT)/,$(addsuffix /lib,$(DEPS))) \
    $(LIBRARIES) lib

# The default rule to build every target in the project.
.PHONY: all
all: deps $(DEP) $(OBJ) $(BIN) $(LIB)

# Phony rule to recursively build the library dependencies.
.PHONY: deps
deps:
    @for dep in $(DEPS); do cd $(ROOT)/lib/$$dep && $(MAKE); done

# Secondary expansion is used to properly locate prerequisites.
.SECONDEXPANSION:

# Rule for dependency file generation.
%.d: $$(notdir $$*).c
    $(CC) -M $(CPPFLAGS) $(CFLAGS) -iquote include $(addprefix -I ,$(INCLUDES)) $< -MM -MG -MP -MT '$@ $(filter %/$(notdir $*).o,$(OBJ))' > $@

# Rule for compiling object files.
%.o: $$(notdir $$*).c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) -iquote include $(addprefix -I ,$(INCLUDES)) $< -o $@

# Rule for linking binaries.
%: $$(notdir $$*).c
    $(CC) $(CPPFLAGS) $(CFLAGS) $(addprefix -Xlinker ,$(LDFLAGS)) -iquote include $(addprefix -I ,$(INCLUDES)) $(addprefix -L ,$(LIBRARIES)) $(filter-out $<,$^) -o $@ $(addprefix -l,$(LDLIBS))
    $(STRIP) $(STRIPFLAGS) $@

# Rule for linking shared libraries.
%.so: $$(notdir $$*).c
    $(CC) $(CPPFLAGS) $(CFLAGS) $(addprefix -Xlinker ,$(LDFLAGS)) -iquote include $(addprefix -I ,$(INCLUDES)) $(addprefix -L ,$(LIBRARIES)) $(filter-out $<,$^) -o $@ -fpic -shared -Wl,-h,$(notdir $@) $(addprefix -l,$(LDLIBS))
    $(STRIP) $(STRIPFLAGS) $@

# Rule for generating static libraries.
%.a:
    $(AR) $(ARFLAGS) $@ $?

# Include all dependency files and remake them if necessary.
ifneq ($(MAKECMDGOALS),clean)
    include $(DEP)
endif

# Phony rule to clean the entire build tree.
.PHONY: clean
clean:
    @for dep in $(DEPS); do cd $(ROOT)/lib/$$dep && $(MAKE) clean; done
    $(RM) $(DEP) $(OBJ) $(BIN) $(LIB) $(CLEAN)

ROOT contains the path for your projects' directory (working copy of repository, for example), typically exported as an environment variable. You will also need a couple of directories (bin, dep, obj and src) in your projects.

An example Makefile using this system could be:

DEPS := mylib

BIN := bin/test
LIB := lib/libtest.a

include $(ROOT)/project.mk

bin/test: $(OBJ)
lib/libtest.a: obj/test1.o obj/test2.o

That is, you just write the minimum necessary about your project, and let the build system do the rest. You can always explicitly specify the value for a given variable (SRC, for example, or CFLAGS), but if you don't, then it gets a reasonable default.

The above was tailored to my needs, but it should be simple to adapt to yours, while keep things as easy as the examples you've mentioned.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜