Thread: Conditional compilation for object files in Makefile?

  1. #1
    Registered User
    Join Date
    Nov 2007
    Posts
    33

    Conditional compilation for object files in Makefile?

    Hi,

    I have a Makefile like this:
    http://graphics.csie.ntu.edu.tw/~jon.../Makefile.html

    When I type "make", it compile each object files correctly and link to a binary file.

    However, how can I type "make debug"
    and the Makefile compile with a "-g" option automatically
    and compile with "-O2" when I "make optimize"?

    Simply duplicate the code for object files with different options seems doesn't work...
    No matter what the target is, only one copy of the rules will be apply.

    Thanks in advance!

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    You need to set up separate targets for compiling the individual object files with and without optimisation and have debug and optimise targets depend on those. I normally do it with helper files (i.e. when compiling a .cc file to a .o file with debugging flags, create a .debug file as well) to keep track of things.

    Code:
    # object file names derived from .cc file names in SOURCES
    OBJS = $(SOURCES:%.cc=%.o)
    
    #helper files
    DEBUG_HELPERS=$(SOURCES:%.cc=%.debug)
    OPTIMIZE_HELPERS=$(SOURCES:%.cc=%.optimize)
    
    %.debug: %.cc
     	$(CXX) $(CXXFLAGS) -g $(@:%.debug=%.cc)
    	rm -f $(@.debug=%.optimize)
     	touch -f $@
    
    %.optimize: %.cc
     	$(CXX) $(CXXFLAGS) -O2 $(@:%.optimize=%.cc)
     	rm -f $(@.optimize=%.debug)
     	touch -f $@
    
    debug: $(DEBUG_HELPERS)
    	$(CXX) $(OBJS) -o $(PROGRAM) $(LIBS) 
    
    optimize: $(OPTIMIZE_HELPERS)
    	$(CXX) $(OBJS) -o $(PROGRAM) $(LIBS) 
    
    # cleaning output text files for debugging & intermediate files
    clean:
    	rm -f $(PROGRAM) *.txt *.o *.optimize *.debug
    The logic of this is that the "debug" target depends on a lot of .debug files, which have the same name as the source (.cc) files, but with a .debug extension. The rule for building a .debug file is to compile the source file (which creates the .o file as a side effect) with required compiler settings, and touch the .debug file. Similar logic for the "optimize" target

    The linking for both the debug target and the optimize target is (usually) identical: the real magic happens in producing the .debug or .optimize files on which they depend.

    Since the optimize and debug targets (and therefore the sub-dependencies) are mutually exclusive (and both produce .o files as a side effect), it is necessary for the process of producing a .debug file to delete .optimize files and vice versa. The rule for the "clean" target needs to delete all of the .o files, .debug files, .optimize files, the program, etc.

    Note that I typed the above makefile fragment from memory. I normally use gnu make (which may have slight differences from other make programs) and I may have introduced some errors. So use the example to illustrate the idea, it may not work if you cut/paste it directly into your makefile.

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I get the feeling that there should be a better way of doing it, but I can't think of any.

    I'd just use Boost.Build instead of make. It comes with this stuff built-in.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    Registered User
    Join Date
    Nov 2007
    Posts
    33
    Thanks for help.

    Now my Makefile looks like this:
    http://graphics.csie.ntu.edu.tw/~jon...akefile.1.html

    However, when I "make", I got the following error message:
    g++ -c main.cpp
    g++ -c ImageBox.cpp
    g++ -c DoFunc.cpp
    g++ -c SeamCarving.cpp
    g++ -c GraphCut/graph.cpp
    g++ -c GraphCut/maxflow.cpp
    g++ -c GraphCut/GraphCut.cpp
    g++ main.o ImageBox.o DoFunc.o SeamCarving.o GraphCut/graph.o GraphCut/maxflow.o GraphCut/GraphCut.o -o composition `fltk-config --cxxflags --ldflags --use-images` -lgil2 -lANN
    g++: GraphCut/graph.o: No such file or directory
    g++: GraphCut/maxflow.o: No such file or directory
    g++: GraphCut/GraphCut.o: No such file or directory
    make: *** [all] Error 1

    It seems that the object files are correctly compiled into [somesource].o but not [subdir/somesource].o, so the compiler & the linker are using different naming convention for the object files? How to fix it?

    Also, what is the function of touch in the end of %.debug & %.optimize?

    Thanks a lot!
    Last edited by jutirain; 12-10-2007 at 11:37 PM.

  5. #5
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Add the text "-o $(@:%.optimize=%.o)" to the compiler command under the "%.optimize" target: this tells the compiler the exact name of the output .o file. Similar fix for other targets that are intended to produce helper files that produce .o files as a side effect.

    You'll need to do minor fixes to the clean target as well, to ensure it cleans all .o files correctly.

  6. #6
    Registered User
    Join Date
    Nov 2007
    Posts
    33
    Do you mean this one?
    http://graphics.csie.ntu.edu.tw/~jon...akefile.2.html

    Code:
    whether line 27 have ${CXX} or not will output error message:
    g++ main.o ImageBox.o DoFunc.o SeamCarving.o GraphCut/graph.o GraphCut/maxflow.o GraphCut/GraphCut.o -o composition `fltk-config --cxxflags --ldflags --use-images` -lgil2 -lANN   
    g++: main.o: No such file or directory
    g++: ImageBox.o: No such file or directory
    g++: DoFunc.o: No such file or directory
    g++: SeamCarving.o: No such file or directory
    g++: GraphCut/graph.o: No such file or directory
    g++: GraphCut/maxflow.o: No such file or directory
    g++: GraphCut/GraphCut.o: No such file or directory
    I guess the error in "make" have nothing to do with target "%.optimize"...

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    If you are using gnumake [the common make system for Linux for example], you can use if-statements in the makefile, and do something like this:
    Code:
    ....
    $debug ?= n    // Default to no debug - don't set debug if it already has a value.
    
    ifeq($debug, y)   // If debug was set
       CCFLAGS += -g
    else
       CCFLAGS += -O2
    endif
    ...
    Use this with:
    $make debug=y sometarget

    This is not necessarily portable to other flavours of makefile.

    Another, more portable solution:
    Two-step makefile. You create a and two targets:
    Code:
    optimize: 
        make -f makefile.step2 CCFLAGS=-O2
    
    debug:
        make -f makefile.step2 CCFLAGS=-g
    makefile.step2 is the "actual makefile".

    I have used both (and other solutions, including "generating" makefiles on the fly, based on variables in the original makefile, using for example cpp, sed or awk to process a "template" makefile).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by jutirain View Post
    Do you mean this one?
    No. I said "add it to the compile line", not "add an additional line that does compilation".

    Code:
    $(CXX) ${CXXFLAGS} -g $(@:%.optimize=%.cpp) -o (@.%.optimize=%.o)
    If a target is named x.optimize, then "$(@:%.optimize=%.cpp)" expands to "x.cpp" and "(@.%.optimize=%.o)" expands to "x.o", so (for CXX being g++ and CXXFLAGS being -c), this means that make will execute the command "g++ -c -g x.cpp -o x.o"

    Incidentally if you type "make -n" it tells you what commands it will execute -- useful for debugging makefiles.

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    If you use gnu make, here's the manual.
    http://www.gnu.org/software/make/manual/make.html

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    Registered User
    Join Date
    Nov 2007
    Posts
    33
    Quote Originally Posted by matsp View Post
    Code:
    ....
    $debug ?= n    // Default to no debug - don't set debug if it already has a value.
    
    ifeq($debug, y)   // If debug was set
       CCFLAGS += -g
    else
       CCFLAGS += -O2
    endif
    ...
    Code:
    optimize: 
        make -f makefile.step2 CCFLAGS=-O2
    
    debug:
        make -f makefile.step2 CCFLAGS=-g
    I'm using make 3.81 on ubuntu 7.04

    I used the method #1 and the shell prompt:
    Code:
    ifeq (debug, y)
    /bin/sh: Syntax error: word unexpected (expecting ")")
    make: *** [all] Error 2
    I've checked the manual of make, and I've tried $(debug) or debug, y or "y"
    but the syntax seems to be legal:
    The syntax of the conditional-directive is the same whether the conditional is simple or complex; after an else or not. There are four different directives that test different conditions. Here is a table of them:

    ifeq (arg1, arg2)
    ifeq 'arg1' 'arg2'
    ifeq "arg1" "arg2"
    ifeq "arg1" 'arg2'
    ifeq 'arg1' "arg2"
    If I use method #2 the shell will prompt:
    Code:
    make -f makefile.step2 CCFLAGS=-O2
    make[1]: Entering directory `/home/jutirain/Research/Semi-automatic_Composition/Code/trunk'
    make[1]: makefile.step2: No such file or directory
    make[1]: *** No rule to make target `makefile.step2'.  Stop.
    make[1]: Leaving directory `/home/jutirain/Research/Semi-automatic_Composition/Code/trunk'
    make: *** [optimize] Error 2
    So, how to generate makefile "on the fly"?

    Why is it so hard to write makefile...

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    It's not really that hard to write makefiles. What part is it you are struggling with?

    Obviously, you can generate complete or partial makefiles on the fly. For example, gcc can generate the dependency list for your input files, using the -M switch (-MM is probably the best option). You can use the gcc output as an include file for your actual makefile.

    Obviously your "step2" makefile is essentially your original makefile, just that it's expecting things like CCFLAGS to be set as an argument, rather than as part of the makefile.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Registered User
    Join Date
    Nov 2007
    Posts
    33
    Hi Mats,

    Thanks for answering.

    And how to fix the error in method #1, #2, respectively?
    It seems I've followed the syntax of ifeq...

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    This makefile works.
    Code:
    ifeq (${debug}, y)
      CCFLAGS = -g
    else
      CCFLAGS = -O2
    endif
    
    CC = g++
    
    t.exe: t.cc
    	echo ${debug}
    	${CC} ${CCFLAGS} -o $@ $<
    Your #1 "question" appears to be that you are trying to run a makefile as a shell-script, rather than using make.

    And #2 fails because you don't actually have a makefile.step2 file - which in my incomplete example would have been your original makefile renamed as makefile.step2

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    Registered User
    Join Date
    Nov 2007
    Posts
    33
    You're right.

    Thanks a lot!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. I Need To Know Some Things That I Can Put Into A Batch File
    By TheRealNapster in forum A Brief History of Cprogramming.com
    Replies: 1
    Last Post: 10-20-2003, 08:12 PM
  2. Dos commands hehe
    By Carp in forum A Brief History of Cprogramming.com
    Replies: 2
    Last Post: 01-17-2003, 02:51 PM
  3. conditional compilation
    By Garfield in forum C Programming
    Replies: 4
    Last Post: 10-21-2001, 09:08 AM
  4. Conditional Compilation
    By Peachy in forum C Programming
    Replies: 1
    Last Post: 10-10-2001, 08:54 PM
  5. Preproccessor conditional compilation
    By Garfield in forum C Programming
    Replies: 5
    Last Post: 09-28-2001, 09:28 AM