Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for dependencies on #defines?

Is there a best practice for supporting dependencies on C/C++ preprocessor flags like -DCOMPILE_WITHOUT_FOO? Here's my problem:

> setenv COMPILE_WITHOUT_FOO
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO>
  <Compiles nothing, since no source file has changed>

What I would like to do is have all files that rely on #ifdef statements get recompiled:

> setenv COMPILE_WITHOUT_FOO
> make
  g++ FileWithIfdefFoo.cpp

What I do not want to is have to recompile everything if the value of COMPILE_WITHOUT_FOO has not changed.

I have a primitive Python script working (see below) that basically writes a header file FooDefines.h and then diffs it to see if anything is different. If it is, it replaces FooDefines.h and then the conventional source file dependency takes over. The define is not passed on the command line with -D. The disadvantage is that I now have to include FooDefines.h in any source file that uses the #ifdef, and also I have a new, dynamically generated header file for every #ifdef. If there's a tool to do this, or a way to avoid using the preprocessor, I'm all ears.

import os, sys
def makeDefineFile(filename, text):
    tmpDefineFile = "/tmp/%s%s"%(os.getenv("USER"),filename) #Use os.tempnam?
    existingDefineFile = filename

    output = open(tmpDefineFile,'w')
    output.write(text)
    output.close()

    status = os.system("diff -q %s %s"%(tmpDefineFile, existingDefineFile))

    def checkStatus(status):
        failed = False
        if os.WIFEXITED(status):
            #Check return code
            returnCode = os.WEXITSTATUS(status)
            failed = returnCode != 0
        else:
            #Caught a signal, coredump, etc.
            failed = True
        return failed,status

    #If we failed for any reason (file didn't exist, different, etc.)
    if checkStatus(status)[0]:
        #Copy our tmp into the new file
        status = os.system("cp %s %s"%(tmpDefineFile, existingDefineFile))
        failed,status  = checkStatus(status)
        print failed, status
        if failed:
            print "ERROR: Could not update define in makeDefine.py"
            sys.exit(status)
like image 957
Walter Nissen Avatar asked Jul 28 '11 18:07

Walter Nissen


2 Answers

This is certainly not the nicest approach, but it would work:

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch

That will look through your source code for the macro COMPILE_WITHOUT_FOO, and "touch" each file, which will update the timestamp. Then when you run make, those files will recompile.

If you have ack installed, you can simplify this command:

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch
like image 110
Tim Avatar answered Sep 30 '22 20:09

Tim


I don't believe that it is possible to determine automagically. Preprocessor directives don't get compiled into anything. Generally speaking, I expect to do a full recompile if I depend on a define. DEBUG being a familiar example.

I don't think there is a right way to do it. If you can't do it the right way, then the dumbest way possible is probably the your best option. A text search for COMPILE_WITH_FOO and create dependencies that way. I would classify this as a shenanigan and if you are writing shared code I would recommend seeking pretty significant buy in from your coworkers.

CMake has some facilities that can make this easier. You would create a custom target to do this. You may trade problems here though, maintaining a list of files that depend on your symbol. Your text search could generate that file if it changed though. I've used similar techniques checking whether I needed to rebuild static data repositories based on wget timestamps.

Cheetah is another tool which may be useful.

If it were me, I think I'd do full rebuilds.

like image 43
Tom Kerr Avatar answered Sep 30 '22 21:09

Tom Kerr