views:

1294

answers:

10

What approach do C++ programmers on Unix platform use to create and manage Makefiles?

I was using hand made Makefiles for my projects but they don't handle header file changes and other dependencies. I googled around and found a good solution here.

But I ran into a problem here in the sed command -

    sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
        -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \

The problem is with the 3rd expression "-e 's/ *\$$//'. It doesn't work. Its supposed to remove trailing backslashes. I understand that there has to be double dollar there since this is part of a Makefile. Can someone tell me what wrong here?

Here's the complete Makefile -

CC=g++
CFLAGS=-g -Wall
LIBS=-lpthread

OBJS=file1.o file2.o
TARGET=testProg

$(TARGET) : $(OBJS)
        $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

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

-include $(OBJS:%.o=%.P)

clean :
        rm -f $(TARGET) $(OBJS)

all : $(TARGET)

Apart from the solution to this problem, I would also like some tips/pointers to my 1st question.

+3  A: 

In a make file anything you list on the dependency line is a dependency header files or other files included.

BSD tutorial on make Note: you can auto generate header dependency info with the -MM switch of GCC.

stonemetal
A: 

You can use qmake to generate Makefiles for a project even if that project is not using Qt.

Troubadour
+4  A: 
Beta
Use GCC's header dependancy generator switch instead. Visual Studio has one as well. That way you only depend on the headers that are actually included.
Vitali
Thanks Beta, I have figured out my problem. Sorry it was my mistake. That sed expression is working as it should (it's removing trailing backslashes). For some reason I thought that after removing trailing backslashes, other expressions in that command will append the lines and add a colon at the end. Colons are added at the end but lines are not appended, which is not necessary.
Happy Go Lucky
+1  A: 

I prefer to use CMake, even though it's not strictly the solution to your problem.

It's a project description language that'll generate your Makefiles, Visual Studio Project, Eclipse Project, KDevelop, etc for you. All the dependencies are done for you:

CMakeLists.txt

add_executable(my_exe file1.c file2.c)
target_link_libraries(my_exe my_library)
add_subdirectory(lib)

In lib/CMakeLists.txt

add_library(my_library file3.c file4.c)

This creates a my_exe from file1.c file2.c linked against my_library. I find this much simpler. It also has things like package discovery:

find_package(Qt4)
Vitali
A: 

I use BSD make (pmake?) which does lot of work for me (my lang is C, but I think no difference here). This is my common 'local.prog.mk', I never change it:

.PHONY: tags .depend

# .depend depends only on $(SRCS) in bsd.dep.mk, so we can't track changes of
# header's own dependencies properly. so .depend is .PHONY target here.

CSTD    ?=c99
WARNS   ?=9
.if !empty(PC_LIST)
PC_CF   !=pkg-config --cflags $(PC_LIST)
PC_LD   !=pkg-config --libs   $(PC_LIST)
.endif
CFLAGS  +=$(PC_CF) -fgnu89-inline
.if !defined(NO_DEBUG)
CFLAGS  +=-O0 -ggdb3
.endif
LDFLAGS +=$(PC_LD)
CTAGS   =exctags

NO_MAN=
NO_OBJ=
CLEANFILES+=$(PROG).core

.include <bsd.prog.mk>
$(PROG): $(SUBDIR)
build: clean cleandepend depend all
run: $(PROG)
    ./$(PROG)

Note 'bsd.prog.mk' inclusion -- this handles all, build, depend, clean targets. Project-specific BSDmakefiles are simple:

.SILENT:

PROG    =hello
SRCS    =hello.c world.c
PC_LIST =gtk+-2.0 gnet-2.0

.include "../local.prog.mk"

proto:
    cproto -siv `pkg-config --cflags $(PC_LIST)` $(SRCS) > prototypes
CLEANFILES+=prototypes

I just make depend every time I insert/remove any #include directives.

Artur
+1  A: 

I must be missing something. Why doesn't generating dependency files work for you?

n-alexander
Sorry, its working fine. My understanding was wrong.
Happy Go Lucky
A: 

The makedepend utility is installed on many systems and can be quite useful for generating dependency information.

Here is an example Makefile that uses the include directive (plus a little Perl magic) to incorporate the output from makedepend:

# the name of the executable that we'll build
TARGET = foo_prog
# our .cc source files
SRCS = foo.cc main.cc

# the .o versions of our source files
OBJS := $(patsubst %.cc, %.o, $(filter %.cc, $(SRCS)))
# some flags for compiling
CXXFLAGS = -Wall -Werror

# In order to build $(TARGET), we first build each of the $(OBJS).
# Then we use the given command to link those $(OBJS) into our
# $(TARGET) executable.  $^ is a shortcut for $(OBJS).  $@ is a
# shortcut for $(TARGET).
#
# The default compile rule will compile each of the $(OBJS) for us.
$(TARGET): $(OBJS)
        $(CXX) $(CXXFLAGS) $^ -o $@

# Use "make clean" to remove all of the support files.
clean:
        rm -f $(OBJS) $(TARGET) Makefile.depend *~

# This automatically uses the 'makedepend' utility to add any
# dependencies that our source files have, namely .h files.  This way,
# if the .h files change, the code will be re-compiled.
include Makefile.depend
Makefile.depend: $(SRCS)
        makedepend -f- -Y $(SRCS) 2> /dev/null | \
        perl -p -e "s/(^.*?:)/Makefile.depend \1/" > Makefile.depend

If both foo.cc and main.cc depend on foo.h, then the contents of Makefile.depend would be:

Makefile.depend foo.o: foo.h
Makefile.depend main.o: foo.h

The end result is that the dependency information from makedepend is injected into the Makefile as a series of rules. It's similar to the approach of using a .d file for each .cc file, but keeps the dependency information in one file instead of scattered all over the place.

Nate Kohl
+1  A: 

In Mozilla's build system, we use GCC's -MD switch to generate the dependency files: http://mxr.mozilla.org/mozilla-central/source/configure.in#7134 and then we use a script called mddepend.pl to check for removed header files, such that removing a header simply causes a rebuild, not an error: http://mxr.mozilla.org/mozilla-central/source/config/rules.mk#2066 http://mxr.mozilla.org/mozilla-central/source/build/unix/mddepend.pl

That script generates an .all.pp file containing all the dependencies, with extra foo.o: FORCE dependencies stuck in for missing header files. We then simply -include the .all.pp file in rules.mk right below there.

Ted Mielczarek
+3  A: 

gcc/g++ can generate dependencies for you with the -M family of options. The following works by specifying how to generate .depends files given a source file. By doing -include $(DEPS) $(DEPS) is recognized as a target and will be built/rebuilt when the source files change.

CXX      = g++
CXXFLAGS = -Wall -O3
LDFLAGS  =

TARGET = testcpp
SRCS   = main.cc x.cc foo.cc
OBJS   = $(SRCS:.cc=.o)
DEPS   = $(SRCS:.cc=.depends)


.PHONY: clean all

all: $(TARGET)

$(TARGET): $(OBJS)
        $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)

.cc.o:
        $(CXX) $(CXXFLAGS) -c $< -o $@

%.depends: %.cc
        $(CXX) -M $(CXXFLAGS) $< > $@

clean:
        rm -f $(OBJS) $(DEPS) $(TARGET)

-include $(DEPS)
John Ledbetter
A: 

Instead of the sed scripts, use gcc's -MT option to modify the target of the generated dependency rules. This blog post has more info.

Mark