ThrowTheSwitch / CMock

CMock - Mock/stub generator for C
http://throwtheswitch.org
MIT License
652 stars 269 forks source link

use strippables to remove macro #352

Open akohlsmith opened 3 years ago

akohlsmith commented 3 years ago

I have a header file with the following type of construct:

FOO_PACKED(
    typedef struct
{
    uint32_t var_1;
    uint32_t var_2;
    uint16_t var_3;
} foo_t; )

that cmock hates because of the FOO_PACKED() wrapper. I'm trying to use :strippables: to ignore this:

    :strippables:
    - '(?:__attribute__\s*\(+.*?\)+)'
    - '(?:FOO_PACKED\s*\(+.*?\)+)'

but I receive the same error, leading me to believe that I've got the matching wrong, although it seems to follow the pattern.

The last bit of the traceback I get looks like this:

1: from /path/to/repo/cmock/cmock/lib/cmock_header_parser.rb:43:in `block in parse' /path/to/repo/cmock/cmock/lib/cmock_header_parser.rb:566:in `parse_declaration': Failed Parsing Declaration Prototype! (RuntimeError)   declaration: 'FOO_PACKED()'   modifier: ''   return: { :type => '', :name => 'cmock_to_return', :str => ' cmock_to_return', :void? => false, :ptr? => false, :const? => false, :const_ptr? => false}   function: 'FOO_PACKED'   args: [] make: *** [test/mocks/layer.mk:64: test/mocks/mocks/src/foo_mocked_header.c] Error 1

Can someone help point me in the right direction to solving this?

akohlsmith commented 3 years ago

Essentially what I need is a way to tell cmock

#define FOO_PACKED(x) x

I tried to do that in a file listed in :includes: but that is a compile time thing, not a "create mocking" thing.

JuPrgn commented 3 years ago

If you can modify code you could use #ifdef TEST and change FOO_PACKED as we usualy do for STATIC ?

akohlsmith commented 3 years ago

I'm not sure I understand.

the cmock preprocessor seems to not run.

i.e. I added this to the header file:

#ifdef TEST
#ifdef FOO_PACKED
#undef FOO_PACKED
#define FOO_PACKED(x) x
#endif
#endif

And it acts the same.

JuPrgn commented 3 years ago

On your code you can use FOO_PACKED_GUARD instead and define :

#ifdef TEST
#define FOO_PACKED_GUARD(x) x
#else
#define FOO_PACKED_GUARD(x) FOO_PACKED(x)
#endif

Not sure this will work if your previous code doesn't.

akohlsmith commented 3 years ago

The issue seems to be that when creating the mock files, it sees FOO_PACKED() and thinks it's a function to be mocked. When scanning, it does not appear to execute any preprocessor macros, which would allow me to use the #define workaround you suggested.

akohlsmith commented 3 years ago

Actually I wonder if this is a multi-line thing...

JuPrgn commented 3 years ago

I think there is another way but can't remember well. I could tell you next week if you still have this issue.

JuPrgn commented 3 years ago

What about :

#ifndef TEST
FOO_PACKED(
#endif
typedef struct
{
    uint32_t var_1;
    uint32_t var_2;
    uint16_t var_3;
} foo_t; 
#ifndef TEST
)
#endif

And use FOO_PACKED_START and FOO_PACKED_END to have something nicer ?

Letme commented 3 years ago

CMOCK doesn't have preprocessor so anything you want to add around will mess with the regex checker. You could run your code through preprocessor and then use cmock to mock that?

akohlsmith commented 3 years ago

@JuPrgn I'm hoping not to have to change dozens of header files to wrap the start and end of the FOO_PACKED(), but it's nice to know that that is a fallback option.

@Letme I think that's going to be the most sane way forward. I'll confer with some colleagues about doing this.

Thank you both, I truly appreciate your attention here. I'm an absolute newb to cmock and my only experience with Ruby is with an ancient version of Redmine. :-)

akohlsmith commented 3 years ago

@Letme is there a way to ask cmock to take any files presented to it and pipe them through a (configurable) preprocessor? Our build system does not have a good way to do this, and it seems that an input filter might be easier to put in the input stream within a high level system such as cmock.

Do you have any ideas on how I might be able to easily pass every file through the preprocessor without losing information such as the filename or copying every file into a temporary location after preprocessing them?

Letme commented 3 years ago

CMock is just a regexp parser, not a full blown preprocessor. I do not know what build system you use, but for example Ceedling only needs 1 additional field in project.yml . In makefile you can basically create a target that preprocesses the header files and then point Cmock to those instead of originals.

To get preprocessed header files to stdout just put -E argument to your GCC compilation command (I think its same for clang). Make sure same defines and CFLAGS are included. I don't think you can pipe it to Cmock, so you can create a temporary file. Something like

gcc -E $(MYHEADER).h > tmp/preprocessed.h && CMOCK -o CMockConfig.yml tmp/preprocessed.h && mv tmp/preprocessed.h build/$(MYHEADER).h