Best practice for #defines dependencies?

Is there a best practice for supporting C / C ++ preprocessor flag dependencies like -DCOMPILE_WITHOUT_FOO ? Here is my problem:

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

I would like all files that rely on #ifdef statements to #ifdef recompiled:

 > setenv COMPILE_WITHOUT_FOO > make g++ FileWithIfdefFoo.cpp 

I do not want to recompile everything if the COMPILE_WITHOUT_FOO value has not changed.

I have a primitive Python script working (see below) that basically writes the header file FooDefines.h and then delimits it to see something else. If so, it replaces FooDefines.h , and then the usual dependency of the source file begins to be used. The definition is not passed on the command line with -D . The downside is that now I have to include FooDefines.h in any source file that uses #ifdef , and also I have a new dynamically generated header file for each #ifdef . If there is a tool for this or a way to avoid using a 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) 
+7
source share
6 answers

This, of course, is not the most pleasant approach, but it will work:

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

This will scan the source code for the COMPILE_WITHOUT_FOO macro and β€œtap” each file that will update the timestamp. Then, when you run make, these files will be recompiled.

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

 ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch 
+3
source

I do not think that this can be determined automatically. Preprocessor directives are not compiled into anything. Generally speaking, I expect to do a complete recompilation if I depend on the definition. DEBUG is a familiar example.

I do not think there is a right way to do this. If you can't do it right, then the dumbest way is probably your best bet. Text search COMPILE_WITH_FOO and create dependencies in this way. I would classify this as shenin, and if you write general code, I would recommend looking for a pretty significant purchase from your colleagues.

CMake has some features that can make this easier. To do this, you will create a custom goal. Here you can trade problems by maintaining a list of files depending on your symbol. Your text search could generate this file if it changed. I used similar methods, checking if I need to restore static data repositories based on wget timestamps.

Cheetah is another tool that can be useful.

If it were me, I think I’ll make a complete overhaul.

+2
source

The problem is to handle it with autoconf and autoheader , writing the values ​​of the variables to the config.h file. If this is not possible, consider reading the -D directives from a file and writing flags to this file.

In any circumstances, you need to avoid assemblies that depend only on environment variables. You cannot tell when the environment has changed. There is a definite need to store variables in a file, the cleanest way would be to use autoconf, autoheader and the source and several assembly trees; the second is the cleanest way by rec configure -ing for each compilation context switch; and the third - the cleanest way - a file containing all mutable compiler keys on which all objects depending on these switches depend.

When you decide to implement the third method, remember to not update this file unnecessarily, for example. building it in a temporary place and copying it conditionally to diff, and then the rules will be able to conditionally rebuild your files depending on the flags.

+1
source

One way to do this is to save each #define previous value in a file and use conditional expressions in your makefile to force the file to be updated when the current value does not match the previous one. Any files that depend on this macro will contain the file as a dependency.

Here is an example. It will update file.o if either file.c changes or the file.c variable COMPILE_WITHOUT_FOO different from the last. It uses $(shell ) to compare the current value with the value stored in the envvars/COMPILE_WITHOUT_FOO . If they are different, then it creates a command for this file, which depends on force , which is always updated.

 file.o: file.c envvars/COMPILE_WITHOUT_FOO gcc -DCOMPILE_WITHOUT_FOO=$(COMPILE_WITHOUT_FOO) $< -o $@ ifneq ($(strip $(shell cat envvars/COMPILE_WITHOUT_FOO 2> /dev/null)), $(strip $(COMPILE_WITHOUT_FOO))) force: ; envvars/COMPILE_WITHOUT_FOO: force echo "$(COMPILE_WITHOUT_FOO)" > envvars/COMPILE_WITHOUT_FOO endif 

If you want to support undefined macros, you will need to use the ifdef or ifndef , and the file will indicate that this value was undefined at the last run.

+1
source

create triggers on timestamps in files. A dependent file, which is newer than what depends on it, causes its recompilation. You will need to put your definition for each option in a separate .h file and make sure that these dependencies are presented in the make file. Then, if you change the setting, files that depend on it will be automatically recompiled.

If you consider accounting files that include files, you don’t have to change the source structure. You can include the "BuildSettings.h" file, which includes all the individual settings files.

The only difficult problem is if you make it smart enough to analyze enable guards . I saw compilation problems due to the inclusion of file name conflicts and directory search order.

Now that you have mentioned this, I have to check and check if my IDE is enough to automatically create these dependencies for me. Sounds like a great thing to add to your development environment.

0
source

Jay noted that "create triggers on timestamps on files."

Theoretically, you can have your main makefile, call it m1, include variables from the second makefile called m2. m2 will contain a list of all preprocessor flags.

You may have a make rule for your m2-dependent program that is being updated.

The rule for creating m2 would be to import all environment variables (and therefore #include directives).

there would be a trick, the rule for make m2 would detect if there was a difference with the previous version. If this is the case, it will enable a variable that will make it "do everything" and / or make it clean for the main purpose. otherwise, it will simply update the timestamp on m2 and will not start a full remake.

finally, the rule for the normal purpose (to do everything) will be used in the preprocessor directives from m2 and apply them as necessary.

it sounds simple / possible in theory, but in practice GNU Make is much harder to get this type of work to work. I'm sure it can be done, though.

0
source

All Articles