-
Notifications
You must be signed in to change notification settings - Fork 246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue when combining use_test_preprocessor and CMock's treat_inlines #706
Comments
First of all thanks for writing this issue as it helped me to figure out my problem (which is exactly the same). I can confirm the issue and also provide info why this is the case and propose a solution. Here is the problem step-by-step:
Here are two solutions that came to my mind:
@laurensmiers as the creator of the original feature (ThrowTheSwitch/CMock#261) you might also be interested in this issue. |
The 2. solution is not what you want. The point of the use_test_preprocessor is to remove (well more like expand) the macros by preprocessor, so that CMock and other tools can correctly parse and generate only C valid (used) C code. In that part the 3. is actually what you would expect. The top issue more points out that the path of the file included should be dependant on the preprocessed file and/or built file instead of original, or that inclusion of the file should actually be compiler argument include path dependant (not include directive dependant). It should also have include guard (which I agree is most probably the bug), but that will not solve the problem of removing the macro (which is what preprocessor does - not cmock, as you found out). So why do you want to use preprocessor if you do not want macros from header files to be removed? |
I fear you didnt fully grasp my description. CMock will still get the correctly preprocessed header to operate normally. Only the very specific part of generating a copy of the original header without the static/inline keywords operates on the original file and not on the preprocessed file. But I have to admit this issue is pretty much brainfuck. |
So you do not forward the preprocessed file to the CMock for stripping static inline functions, but the original file is passed to the function? |
My solution forwards both parts to CMock: The preprocessed header file and the original file. All usual operations run as always in CMock only for the specific part of creating the copy with the static/inline keywords stripped the original file is fed. Here are the decision tree options:
|
And (check if I am correct):
|
Yes and no. There is a copy created which is preprocessed but not what I consider as a copy in the description above. So not a copy which is placed in the include path under Yes: There is a cache copy somewhere and the preprocessed data is passed to CMock. No: This preprocessed header is not used when compiling the header later. When compiling the unittests the original header is used again and therefore naturally preprocessed by GCC and all macros are available. If this would be different the problem would be much bigger. So to be more specific:
|
You may add -I. so that #include "driver/hal/hal_bbb.h" be successful. You can do that in project.yml:
But it's strange and this shouldn't be needed. |
I am very surprised that treat_inlines creates a new header file even if the original header does not have any inline functions!?! I understand the need to create a header file if the inline implementation has to be mocked. But why is this a global setting and not depending on the content of the header? |
Probably because its easier. Just do a batch processing of all files instead of actually looking into them and create a copy only if required. |
Adding to your solution 2 from January 12th 2023: Wouldn't it be good to have the strippables also be applied to the modified header (if your compiler does not understand some options)? As far as I reverse engineered it this is only applied to the mocked files? |
Phew ... the modified headers only exist for the sake of treat_inlines. I am not sure if my solution meddles with strippables in any way. I hope the behavior of strippables just stays exactly the same as without treat_inlines. As also mentioned in my PRs: I am not very happy with the solution I created there. It was just the best shot I had in a few tries with very limited knowledge of ruby & rake. |
@i-adamov I believe the latest prerelease of Ceedling 1.0.0 (formerly 0.32) fixes the problems documented in this issue. Ceedling's much improved preprocessing and CMock's |
This is great news and will make Ceedling 1.0 the framework we are absolutely looking forward to. I will try this out as soon as I can (but this might need till begin of August) and provide feedback, if this works well in our setup. |
Unfortunately the issue continues to exist according to the description with the latest ceedling version. Compare comment #868 (comment) |
@i-adamov and @M-Bab So very sorry for the slow response. After staring at everything for a while and scratching my head, I think I finally properly grasp the various problems at play. And, yes, this is not fixed as I thought it was. The current handling in 1.0.0 does work correctly for some limited cases but not the sophisticated cases typically at play here. @M-Bab Your suggestion of using CMock to strip If you have ideas, please share them. We're discussing how to handle all this before finally publishing 1.0.0. I think there may be the appropriate options in the GCC preprocessor we could use with a scan-modify-stitch approach that gets us to 100% handling of all cases. But, I do not know yet. |
Referencing #938 by @MichaelBMiner to collect related issues here |
Sorry for ghosting this tread for almost two years but I see @M-Bab has taken the torch and has done a great job of replicating the issue and tracking its progress. I try to keep up with the updates here but I've been too busy to try newer versions to see if the issue is fixed. |
For all those playing along at home… @mvandervoord and I have looked at this problem long and hard now. We think we have a solution that will handle all cases. We are working on implementing it and testing it now. If nothing else it should support everything but oddball edge cases. This seems to be one of the most important advanced needs of power users, so, we think it's best to tackle this as the very last need before releasing 1.0.0. The whole idea of mocking is especially valuable to anyone working with complex header files, and Wish us luck… |
Dear @mkarlesky and @mvandervoord of course I wish you all the luck in the world for this. Again I can offer you my assistance, if I can be of any help. Also reading my comments/MRs can help to grasp the full scale of the problem.
On the other hand this issue also easily leads to overthinking and I want to make sure you don't get any headaches about this: All you want is having a header copy where all the inlines are stripped without actually using the preprocessor before. Therefore I am still convinced that my concept of the 2 merge requests was very valid. I am not sure if you are able to construct a header where it actually fails. All you need is remove the
Can you give me an example where you actually see difficulties incoming? You should rather think of it as an easy search & replace operation before your whole ceedling stuff is ongoing 😉 . As said I am very convinced the concept of handling inlines was already quite fine in my MRs. On the other hand I was never too happy with the way I implemented it code quality wise. But I think I already emphasized this in the MRs itself. The way it was distributed among Ceedling and CMock was also not very neat. |
These were my MRs to cover the issue:
The trick there was to sneak the original header past the preprocessor to present it to the treat_inlines handling (and only to that feature). |
Okay well yeah I see it now. If you use Macros to actually create your header lines you are in for a bad time. Not sure if this is a great style though. IDE parsers trying to help you with autocompletion might also crunch on that. So you are thinking of something like this: #define GENERATE_INLINE_FUNCTION_DECL(x) inline void (x)(void)
GENERATE_INLINE_FUNCTION_DECL(myfunc); Can't remember if I have ever seen this. Most probably headers are rather auto generated with some scripts instead of doing such evil things. Well my suggestion here would probably be ... #if UNIT_TESTING
#define GENERATE_INLINE_FUNCTION_DECL(x) void (x)(void)
#else
#define GENERATE_INLINE_FUNCTION_DECL(x) inline void (x)(void)
#endif
GENERATE_INLINE_FUNCTION_DECL(myfunc); But I am also curious about your generic solution which will handle it all. I will certainly test it with our Code base as a quite complex benchmark. |
@M-Bab Yup. Exactly. You got it on your example generator macro. I'll grant you that that's ugly enough that it causes a variety of other problems. It's absolutely possible that we may have to fall back to a less universal solution such as what you've proposed. Our unfortunate reality is that (A) embedded development is the primary use case for Ceedling (B) the embedded code Ceedling is asked to work with is often a bit crazy and remarkably ugly (C) the more universal any solution can be the more easily we can support other core needs in favor of time consuming edge cases (even just keeping up with questions). So, we're gonna go for it with the solution we hope might work. If we're right, it'll handle even nutso cases — which means it will easily handle common, reasonable cases like yours too. If it's not going to work we should know soon enough. |
Update: The first working version of the (hopefully) universal solution to this problem is in a branch and successfully running. It produces preprocessed header files that CMock is able to mock with optional handling of This all needs some further cleanup, optimization, and documentation before it's ready to be fully exercised. I'm not sure when it will be available, but it looks like we're close to a proper solution. |
@M-Bab The latest prerelease (1.0.0-13d0e3d) is available for testing. It preserves macro definitions and pragmas in a header file that CMock copies and modifies when |
Hi @mkarlesky ! Okay I did test it with
I hope this can somehow help you to do another step. I am certain that this was already one of the faults I got with the previous version I tested. Slightly off-topic. I build Ceedling with a cloned repo and gem. But before ceedling actually worked I had to
Is it possibled that these modules are somehow missing in your requirements specifications? |
@M-Bab We did do some gem housekeeping recently. Thor is definitely in the Gemfile and gemspec. Not sure why it's failing. Erb is curious for other reasons. We will look into it. |
@M-Bab Could you kindly change the generated osal.h to use the proper list of I can think of two options:
|
I am working on a big firmware project where Ceedling is used to run unittests and mock driver headers.
The issue I am having is that if I enable the test preprocessor feature and have CMock configured to treat header files with inline functions (of which we have some) the compilation of the unittest fails.
Additional info specific to our project is that the unittests (and project.yml file) are located in a separate directory so I am doing
CEEDLING_MAIN_PROJECT_FILE=./unittests/project.yml
before calling Ceedling.I was able to recreate the issue using a simple example project that contains only a few files - https://github.com/i-adamov/ceedling-issue-example
I have a module which I need to test (
./src/example_file.c
and./inc/example_file.h
) which includes a driver header (./driverv/drv_bbb.h
) and in turn the driver header includes a HAL header (./driver/hal/hal_aaa.h
). This simulates how our project is structured.There is another header (
./inc/other_header.h
) which also includes the driver header and it is also included by theexample_file
module. It simulates the header of another module that may interact with the module I am testing.When running Ceedling without the preprocessing it works fine:
export CEEDLING_MAIN_PROJECT_FILE=./unittests/project.yml ; ceedling clobber test:all
However if I set
:use_test_preprocessor: TRUE
the build fails:What I see as a difference is that the driver header file (
unittests/build/test/mocks/drv_aaa.h
) which is processed by CMock to make the inline functions testable has changes to the include macros in the top of the file. It is also missing its include guard.When running without preprocessor:
When running with preprocessor (empty lines truncated):
The use of this
driver/hal/hal_bbb.h
filepath makes it impossible for the compiler to locate the file as the root directory is not used as an include path. However if I add it to the:paths: :include:
section of the project.yml file, I get another issue with undefined macros:What am I doing wrong? Do I need to enable some of the other Ceedling setting?
I need to be able to preprocess macros and to mock static functions in header files.
The text was updated successfully, but these errors were encountered: