VSCode: Bridging headers with Intellisense in simple Embedded Project

Hey, I'm wanting to experiment with Embedded Swift, and the device I am targeting uses an SDK that relies on makefiles. Basing my project structure off a similar project using Embedded Swift, on a similar platform, which uses a similar SDK, from the same company, my project structure is as follows:

HelloWorld
┗ source
    ┗ Bridging-Header.h
    ┗ main.swift
┗ makefile

And the makefile has the following additions:

SWIFTC := /Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin/swiftc
SWIFTFLAGS := -wmo -parse-as-library -enable-experimental-feature Embedded -target armv7em-none-none-eabi -no-allocations -import-objc-header ../$(SOURCES)/Bridging-Header.h -I $(DEVKITARM)/include -I $(DEVKITARM)/arm-none-eabi/include -I $(DEVKITPRO)/libctru/include
%.o: %.swift
	@echo $(notdir $<)
	$(ADD_COMPILE_COMMAND) add $(SWIFTC) $(SWIFTFLAGS) "$(addprefix -Xcc , $(C_FLAGS)) -c $< -o $@" $<
	$(SILENTCMD)$(SWIFTC) $(SWIFTFLAGS) $(addprefix -Xcc , $(CFLAGS)) -c $< -o $@ $(ERROR_FILTER)

The project itself builds fine, with the swift code essentially a stub. However, Intellisense doesn't seem to import the bridging header for swift.

I have no idea how to tackle this as all instruction I can find is for projects that use SwiftPM and its project layout. The SDK I am using is not set up for that. As one can see above, it is a very simplistic project layout.

Only similar result for my issue here is VSC w/ ESP-IDF: how do I configure LSP to have autocompletion for bridged headers? with no resolution and something about declaring a Swift SDK which makes 0 sense?

I am quite a beginner at this; Not at using Swift as a whole, but at Swift Embedded, cross-platform Swift projects, and using Swift outside of Xcode. The headache of trying to configure a language server is frustrating as is. I already installed the latest Swift release, and pointed VSCode to use its sourcekit-lsp server.

My .vscode/settings.json is as follows:

{
    "swift.path": "/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin",
    "swift.swiftEnvironmentVariables": {
        "DEVELOPER_DIR": "/Applications/Xcode-beta.app"
    },
    "swift.sourcekit-lsp.serverArguments": [
        "-Xswiftc",
        "-parse-as-library"
    ]
}

The following was added to my .vscode/c_cpp_properties.json:

            "includePath": [
                "/opt/devkitpro/**",
                "/opt/devkitpro/libctru/include",
                "/opt/devkitpro/devkitARM/arm-none-eabi/include",
                "${workspaceFolder}/**"
            ]

And my .sourcekit-lsp/config.json reads as follows:

{
    "swiftPM": {
        "swiftCompilerFlags": [
            "-parse-as-library",
            "-enable-experimental-feature",
            "Embedded",
            "-target",
            "armv7em-none-none-eabi",
            "-no-allocations",
            "-import-objc-header",
            "/[ ... REDACTED ... ]/HelloWorld/source/Bridging-Header.h",
            "-I",
            "/opt/devkitPro/DEVKITARM/include",
            "-I",
            "/opt/devkitPro/DEVKITARM/arm-none-eabi/include",
            "-I",
            "/opt/devkitPro/libctru/include"
        ]
    }
}

All I want is for intellisense to pick up the bridging-header so I know how to properly refer to C functions in Embedded Swift. Without it, I'm basically stumbling in the dark guessing the equivalent Swift syntax.

My whole reason for wanting to use Embedded Swift for this is because Swift is easier to understand and syntactically more comfortable to me. C/C++/Obj-C are nightmare languages for me in comparison.

To reiterate, the project builds, but I can't really go further without code completion/intellisense.

If you know of an easy way to get sourcekit-lsp to pick up a bridging header, please, ELI5. I'm smart, but I'm wading uncharted territory here.

The problem you're running into here is that with the current setup, sourcekit-lsp has no way to know what settings to use for a particular file (so eg. main.swift here).

sourcekit-lsp has a few ways to retrieve this information - either through SwiftPM, a compilation databases, or a custom BSP server.

The second is what you're looking for in this case.

Unfortunately, there's no simple way to produce compile_commands.json with Make (with CMake you can set CMAKE_EXPORT_COMPILE_COMMANDS). You could probably get away with using compile_flags.txt today, but I would recommend just manually crafting compile_commads.json instead:

  1. Remove swift.sourcekit-lsp.serverArguments from .vscode/settings.json - these are arguments to provide to the language server (sourcekit-lsp) on launch, not compilation arguments.
  2. Remove .sourcekit-lsp/config.json - swiftPM is specifically for configuring SwiftPM projects further, which you don't have here.
  3. Add a compile_commands.json next to your makefile:
[
  {
    "directory": "/[ ... REDACTED ... ]/HelloWorld",
    "command": "swiftc source/main.swift -enable-experimental-feature Embedded -target armv7em-none-none-eabi -no-allocations -import-objc-header source/Bridging-Header.h -I /opt/devkitPro/DEVKITARM/include -I /opt/devkitPro/DEVKITARM/arm-none-eabi/include -I /opt/devkitPro/libctru/include",
    "file": "source/main.swift"
  }
]
  1. Open the workspace in VS Code
3 Likes

Thank you for the swift reply! (No pun intended.)

Do you know if compile_commands.json supports VSCode variables? E.G ${workspaceFolder} for the "directory"?

Nevermind, I answered my own questions, haha! (The answer is: no.) This actually worked, thank you!

2 Likes