Open i-adamov opened 1 year ago
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:
:treat_inlines: :include
is enabled, a modified header file needs to be generated (obviously because static/inline is removed from the header code as well). The modified header file can be found in test/mocks
and is prioritized over the original header. The generation of this modified header happens in this line: https://github.com/ThrowTheSwitch/CMock/pull/261/files#diff-04520bc1e2e09e1dd300c2060876f6341f8b76093d60f845c52b8021bf36c5f1R66 as part of CMock.:use_test_preprocessor: TRUE
is also enabled in Ceedling any header code provided to CMock is "filtered" through the actual GCC preprocessor. That preprocessor does a lot of stuff and among them is the removal of all macros - obviously because these macros are usually applied by the GCC preprocessor.Here are two solutions that came to my mind:
use_preprocessor_directives
sounds pretty much like that, but did not resolve the problem described above.test/mocks
. This is completely legitimate because once it actually compiles the stuff with gcc the preprocessor will run over the header again and is able to apply all the macros which are still stored. I will create a pull request where @mvandervoord can check if this is a good/okay solution.@laurensmiers as the creator of the original feature (https://github.com/ThrowTheSwitch/CMock/pull/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?
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.
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 test/mocks
.
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:
:paths:
:source:
- .
- hal # etc
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. But even the "smarter" behavior would still be fatal: Chances that important macros and inline functions are in the same header are pretty high.
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 :treat_inlines
now work together as they should. I am going to leave this issue open to collect any followup. For anyone following this thread, please let us know if this problem has been corrected.
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.
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.