Apinotes are ignored for a C module with Zephyr RTOS

I’ve been working on a project using Embedded Swift in Zephyr and I built a thin wrapper around some of its C APIs. I wanted to use apinotes to import some of the enums nicely into Swift, but I can’t get the apinotes file to be picked up.

Zephyr uses GCC to compile everything and swapping to Clang seems like it’s much more trouble than it’s worth, but as far as I know Swift still uses Clang to import C headers, so apinotes should work.

My module.modulemap in the include folder defines the module SwiftZephyrShims:

module SwiftZephyrShims {
  header "SwiftZephyrShims.h"
}

In the same folder I have a SwiftZephyrShims.apinotes file:

Name: SwiftZephyrShims
Tags:
- Name: gpio_flags_t
  SwiftName: GPIOFlags
  EnumExtensibility: closed

And my CMakeLists.txt defines the include directories and enables every setting that I found that relates to apinotes:

add_library(SwiftZephyrShims INTERFACE)
target_include_directories(SwiftZephyrShims INTERFACE
    "$<TARGET_PROPERTY:app,INCLUDE_DIRECTORIES>"
    include
)

file(REAL_PATH include SwiftZephyrShims_INCLUDE_DIR)
target_compile_options(SwiftZephyrShims INTERFACE
    "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -fapinotes>"
    "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -fapinotes-modules>"
    "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xcc -iapinotes-modules -Xcc ${SwiftZephyrShims_INCLUDE_DIR}>"
)

add_dependencies(SwiftZephyrShims syscall_list_h_target)

All of these flags are reflected in the compile_commands.json file for the main app target, so I’m sure that they get to the right place. Swift imports SwiftZephyrShims normally, but any modifications in the apinotes file are ignored. I can’t find any way to force clang to load an apinotes file or even a way to verify whether it loads it or not.

Is there any way to get apinotes working here or do I need to write the enum wrappers manually?

I am no expert in APINotes but I do have a project with Zephyr that uses API notes, and the setup is the same as yours and it works for me.

I think the problem is not that the API notes are not read, but that the definition of gpio_flags_t is:

typedef uint32_t gpio_flags_t;

So this is an integer not an Enum.

I can’t find a way to rename that though apinotes, but perhaps there is a way.

Can you test if a C enum like bt_security_t works, if you put something like this in your apinotes file:

Tags:
- Name: bt_security_t
  EnumKind: NSEnum
  SwiftName: BTSecurity

If you're building using ninja, you could try adding the -v flag to dump out the Swift commands generated and check to make sure CMake applied your flags. If so, you can then try passing in the flags -Xfrontend -dump-clang-diagnostics to see all the flags passed to the ClangImporter used by the Swift frontend, which should include yours.

This all assumes that your flags are getting lost in transit somewhere, which is possible but unlikely. More likely there is some problem in the compiler applying your flags, but first make sure they are getting through, by applying these debugging aids.

It looks like it was working all along, and this works fine with no changes:

GPIO flags is just laid out a bit weirdly. A Typedef entry in the apinotes lets me rename it, but all the flags are grouped in different enums, so I can’t import it as one Swift enum anyway.

Thanks for the help!

1 Like