How to find all C functions starting with a prefix in a library

Viewed 66

For a small test framework, I want to do automatic test discovery. Right now, my plan is that all tests just have a prefix, which could basically be implemented like this

#define TEST(name) void TEST_##name(void)

And be used like this (in different c files)

TEST(one_eq_one) { assert(1 == 1); }

The ugly part is that you would need to list all test-names again in the main function.

Instead of doing that, I want to collect all tests in a library (say lib-my-unit-tests.so) and generate the main function automatically, and then just link the generated main function against the library. All of this internal action can be hidden nicely with cmake.

So, I need a script that does:

1. Write "int main(void) {"
2. For all functions $f starting with 'TEST_' in lib-my-unit-tests.so do
  a) write "extern void $f(void);"
  b) write "$f();
3. Write "}"

Most parts of that script are easy, but I am unsure how to reliably get a list of all functions starting with the prefix. On POSIX systems, I can try to parse the output of nm. But here, I am not sure if the names will always be the same (on my MacBook, all names start with an additional '_'). To me, it looks like it might be OS/architecture-dependent which names will be generated for the binary. For windows, I do not yet have an idea on how to do that.

So, my questions are:

  1. Is there a better way to implement test-discovery in C? (maybe something like dlsym)
  2. How do I reliably get a list of all function-names starting with a certain prefix on a MacOS/Linux/Windows

A partial solution for the problem is parsing nm with a regex:

for line in $(nm $1) ; do
    # Finds all functions starting with "TEST_" or "_TEST_"
    if [[ $line =~ ^_?(TEST_.*)$ ]] ; then
        echo "${BASH_REMATCH[1]}"
    fi
done

And then a second script consumes this output to generate a c file that calls these functions. Then, cmake calls the second script to create the test executable

  add_executable(test-executable generated_source.c)
  target_link_libraries(test-executable PRIVATE library_with_test_functions)
  add_custom_command(
        OUTPUT generated_source.c
        COMMAND second_script.sh library_with_test_functions.so > generated_source.c
        DEPENDS second_script.sh library_with_test_functions)

I think this works on POSIX systems, but I don't know how to solve it for Windows

1 Answers

You can write a shell script using the nm or objdump utilities to list the symbols, pipe through awk to select the appropriate name and output the desired source lines.

Related