Hello everyone!
I've found myself in a spot of bother with Keil uVision 5.24.2.0 using ARM Compiler v5.06 update 5...although the build issues I have don't appear specific to these versions. Let me explain...
In my Keil project I have two targets: 'Release' and 'Debug'. The goal is to globally enable/disable various debug options and change formatted output simply by selecting the appropriate build target and compiling.
Here is how I tried to make it work:
- In 'Options for Target', C/C++ tab, 'Preprocessor Symbols' Define section, I have defined: 'DEBUG' for the Debug target 'noDEBUG' for the Release target all other settings are identical between the Debug and Release targets.
- I have a header file 'debug_config.h' in which I have enable/disable various debug options like so:
#ifndef __DEBUG_CONFIG_H #define __DEBUG_CONFIG_H #ifdef DEBUG #define DEBUG_BUILD 1 #else #define DEBUG_BUILD 0 #endif #if ( DEBUG_BUILD == 1 ) #define TEST_MODE_RECOGNIZER 1 #endif #endif
- In main.c and other source code files (which all #include "debug_config.h") I refer only to DEBUG_BUILD and TEST_MODE_* like this:
printf("IronMan Gen3.7 (%s)\r\n", (DEBUG_BUILD == 1) ? "Debug" : "Release"); #if ( TEST_MODE_RECOGNIZER == 1 ) printf("Jarvis 8.9b Test Mode\r\n"); #endif
With all that in place, if I 'Clean Targets', select 'Debug' target, then Build I will get the following output:
IronMan Gen3.7 (Debug) Jarvis 8.9b Test Mode
Perfect!
Now I switch to 'Release' target and without doing 'Clean Targets' I do Build. I find main.c is NOT compiled! Why not? Other files which don't #include "debug_config.h" and don't mention DEBUG, DEBUG_BUILD, or TEST_MODE_RECOGNIZER are compiled. Because main.c is not recompiled I get the same output as above! What I want of course is:
IronMan Gen3.7 (Release)
I understand that I did not change main.c source code, so from compiler perspective it thinks perhaps it doesn't need to retranslate it. However, I selected different Target, which had different Defines, so I expected that it would recompile main.c like it did with other files.
I note there is a 'Undefine' section in the Target C/C++ Preprocessor Symbols, but it is grayed out. I can't select or enter anything there, otherwise I would 'Undefine DEBUG' for the Release target.
So I am stumped...
I hope this provides clear example of what I would like to do. Obviously there's something I've done wrong or misunderstood...
Thanks for any advice or suggestions on this!
Best!
Several issues to note:
1) this way of conditionally defining a preprocessor switch macro:
#if ( DEBUG_BUILD == 1 ) #define TEST_MODE_RECOGNIZER 1 #endif
is a not-quite-optimal fit for this kind of testing the condition:
#if ( TEST_MODE_RECOGNIZER == 1 )
If your condition is #if instead of #ifdef/#if defined(), you should usually always #define the macro in question (to 1 or 0, accordingly). It'll still work the way you do it, because undefined macros will expand to 0 as a last-ditch effort, but you'll get flak from most code checking tools for that style.
2) having a header that relies on: which all #include "debug_config.h" is riskier than it needs to be. It's better to move all those individual, conditionally defined macros directly into the IDE instead, if only because it saves you from silly problems in case you ever forget putting that #include "debug_config.h" in one header or .c file. And it'll save you from warnings about including headers that the code doesn't actually use.
3) is your real issue: However, I selected different Target, which had different Defines, so I expected that it would recompile main.c like it did with other files. If you wanted that, you had better specify separate output folders for the two targets. It's arguably a bug that uVision doesn't automatically do that as soon as you set up the second target in the same project.
Thank you very much, Hans-Bernhard, your comment #3 helped me resolve my problem! As soon as I created an output folder for each target I got the behavior I was aiming for.
I'm still puzzled why many other .c files -- e.g. those with GPIO config with no dependency on debug_config.h or any of the #defines -- were recompiled for the different Target build, but main.c was not.
Also, I'm unsure why the 'Undefine' preprocessor symbol dialog box is grayed out at the Target level, but enabled at the Group and File levels. This option (discussed here http://www.keil.com/support/man/docs/uv4/uv4_dg_adscc.htm) says 'Undefine: Clears previous Define assignments that are entered in the options dialog of a higher Target or Group level.'
I had made my 'Debug' target the highest level, where I Defined 'DEBUG'. My 'Release' target is the second (and last) target. According to website above, it appears I should be able to Undefine 'DEBUG' for the (lower level) Release target. But, the dialog box is grayed out...
I appreciate your other comments, also. Here is my thoughts.
1) you should usually always #define the macro in question (to 1 or 0, accordingly) Good point! I will update my code to:
#if ( DEBUG_BUILD == 1 ) #define TEST_MODE_RECOGNIZER 1 #else #define TEST_MODE_RECOGNIZER 0 #endif
Hmmm...it seems you make this recommendation based on my choice to use #if instead of #ifdef/#if defined(). If so, why does this apply in my case, but not if I opted for #ifdef?
To be clear, if I had:
//#define TEST_MODE_RECOGNIZER 1 #ifdef TEST_MODE_RECOGNIZER printf("Jarvis 8.9b Test Mode\r\n"); #endif
Then wouldn't the undefined macro TEST_MODE_RECOGNIZER still expand to 0? I don't see how #ifdef would be more-optimal than my not-quiet-optimal use of #if. I feel I'm missing something. Could you explain a little further?
2) move all those individual, conditionally defined macros directly into the IDE instead Do you mean define the macros in the 'Options for...' C/C++ preprocessor symbols at the Target, Group or File level?
It seems cumbersome to define many macros in the Defines dialog box like: TEST_MODE_RECOGNIZER=1, TEST_MODE_JARVIS=2, etc. I don't think it will be easy to find/check/change one macro in a long list this way...seems like I would have to set the Defines on 'Options for File' for many different files to make it manageable. And then, perhaps I'd have to repeat the process for each target.
However, if that is the recommended way, how would I conditionally define a macro based on an earlier defined macro? That is, how to do the equivalent of:
#ifdef DEBUG #define DEBUG_BUILD 1 #else #define DEBUG_BUILD 0 #endif
If this is not at all what you meant, could you explain further? Much appreciated!
If so, why does this apply in my case, but not if I opted for #ifdef? Because if your decision mechanism do either define the macro or not, then you should check if it's defined or not --- and vice versa. If, OTOH, you want to run your check against a macro's value, it should always exist, so it always has a value.
I had made my 'Debug' target the highest level, where I Defined 'DEBUG'. My 'Release' target is the second (and last) target. I think you misunderstood what the "higher level" referred to by the documentation actually means. It refers to a higher level in the hierarchy of the structured grouping of settings (i.e. further left in the usual tree view). In other words, it speaks of outer, bigger groups, not groups that appear higher on your screen.
Then wouldn't the undefined macro TEST_MODE_RECOGNIZER still expand to 0? If you used it in a context where it had to be expanded to a particular value, such as your original "#if TEST_MODE_RECOGNIZER == 1" condition, it would. But #ifdef does not expand the macro. It really just checks if you ever #defined it or not.
It seems cumbersome to define many macros in the Defines dialog box like That's another shortcoming of the uVision IDE: its control facilities for preprocessor definitions don't easily support putting lots of individual switches in there, because they're all just squished into a single line of text entry field.
seems like I would have to set the Defines on 'Options for File' for many different files No. We were discussing options that you currently collect in a global header file that is meant to be included in every source file --- those options would just go into single, target-level options dialog of your Debug target. That's what the group hierarchy is for: to collect groups of files that all get the same options.
However, if that is the recommended way, how would I conditionally define a macro based on an earlier defined macro? You wouldn't. You would just put the second macro itself directly into the master Defines list instead.
As-is, your conditional chaining of debug macros does not really gain you much anyway, compared to a bog standard, single DEBUG_BUILD macro that you just query directly.
Thanks for the update!
1) Because if your decision mechanism... Okay, that makes sense! So if I do:
I pair it with conditional compilation test like this
#if ( TEST_MODE_RECOGNIZER == 1 ) ... #endif
Alternatively,
#define TEST_MODE_RECOGNIZER
paired with
#ifdef TEST_MODE_RECOGNIZER ... #endif
2) I think you misunderstood... Indeed! I had thought the order of the different Targets is what it meant, since it's possible to re-order them. Now it is clear why 'Undefine' preprocessor symbols is grayed out at the Target level! Can't undefine anything that hasn't been defined and a higher level...and Target level is at the top!
Thanks for picking up on that one! Silly me!
3) No. We were discussing options that you currently collect in a global header file... Ahh, thanks for the correction. I get it now. I should explore the Group hierarchy more. From your explanation I realize now I could better pair options to specific Groups; certainly options I introduce for my modules don't need to be applied to e.g. HAL modules provided by the the manufacturer.
4) You wouldn't. You would just put the second macro itself directly into the master Defines list instead. I see. That's easy enough for a few macros.
As-is, your conditional chaining of debug macros does not really gain you much anyway, compared to a bog standard, single DEBUG_BUILD macro that you just query directly. True. I had given an elementary example to illustrate my problem. In reality I have several levels of debug, starting with global DEBUG_BUILD under which there's TEST_MODE under which there's a list of individual TEST_*, each with several options to enable/disable individually. And that is just TEST_MODE.
With the conditional chaining of macros I find I can quickly and easily control the debug output. I like I can see in the code exactly what is enabled or disabled without having to Alt+F7 to get the 'Options for...' dialog.
But now that I understand how the Target/Group/File hierarchy works better, I will see if it can work well for me.
A wish to Keil... It would be super helpful if the Keil uVision IDE could track the current value of #define macros (& values) without compiling and then apply syntax highlighting/shading in the IDE code editor that reflects what would be compiled given the current state of Target/Group/File preprocessor symbol Defines/Undefines (in 'Options for...' dialog) and #defines in .h/.c files.