platformio / platform-espressif32

Espressif 32: development platform for PlatformIO
https://registry.platformio.org/platforms/platformio/espressif32
Apache License 2.0
901 stars 607 forks source link

Unable to build Swift library by adding custom target and dependency #1401

Open oleksandr-kosylov opened 3 months ago

oleksandr-kosylov commented 3 months ago

In order to support ability to use Swift programming language with Embedded platform support, I've adjusted CMakeLists according to Example at https://github.com/apple/swift-embedded-examples/tree/main/esp32-led-strip-sdk and installed dev snapshot of swift toolchain that supports embedded platforms.

Project structure: src/ Main.swift Dummy.c BridgingHeader.h CMakeLists.txt

platform.ini [env:esp32-c6-devkitc-1] platform = espressif32 @ 6.7.0 board = esp32-c6-devkitc-1 framework = espidf

Main idea of approach is to build Swift sources and link to main target. It's being achieved by next steps:

  1. creating of custom command (add_custom_command) that produces library.o object.
  2. creating of custom target that depends on library.o (add_custom_target)
  3. linking of _idf_main with library.o
  4. setting dependency of _idf_main on custom target (add_dependencies)

This works fine from terminal by idf.py build BUT looks like custom command and target are not being built in VS Code with PlatformIO, I've tried to replace swift build command with test one and it's net being executed.

Build fails because of missing implementation for app_main function that is declared in Main.swift, so because of library wasn't built.

Please advice what can cause an ignoring of custom target and command by build system. I see that build.ninja contains build commands for custom target and command based on CMakeLists.txt but looks they are not executed.

CMakeLists.txt

valeros commented 3 months ago

Hi @oleksandr-kosylov, thanks for reaching out. The current ESP-IDF integration is meant to be used only with generic ASM/C/C++ based projects. Anyway, the problem you're experiencing is caused by limited CMake's API which currently doesn't export any information about custom build targets. It means that all extra build steps need to be implemented using PlatformIO build API manually. In case with your example, I believe it's possible implement a small workaround using an extra script that will compile Swift sources and inject the resulting binary into the linker command. Please beware, I haven't tested the code below, but it should look something like this:

[platformio]
; Use the common IDF build folder to make CMake happy
src_dir = main

[env:esp32-c6-devkitc-1]
platform = espressif32
framework = espidf
monitor_speed = 115200
board = esp32-c6-devkitc-1

; Compile and inject the Swift library
extra_scripts = 
    compile_swift.py

compile_swift.py:


import os

from platformio.builder.tools.piomaxlen import _file_long_data

Import("env")

# Swift files are located in the `main` build folder
SWIFT_SOURCE_FILES = ("Main.swift", "LedStrip.swift")

def compile_swift_sources(swift_sources):
    assert len(swift_sources) > 0, "Missing Swift source files"
    swift_obj = env.Command(
        os.path.join("$BUILD_DIR", "_swiftcode.o"),
        swift_sources,
        env.VerboseAction(
            " ".join(
                [
                    "swiftc",
                    "-target",
                    "riscv32-none-none-eabi",
                    "-Xfrontend",
                    "-function-sections",
                    "-enable-experimental-feature",
                    "Embedded",
                    "-wmo",
                    "-parse-as-library",
                    "-Osize",
                    "-import-bridging-header",
                    os.path.join("$PROJECT_SRC_DIR", "BridgingHeader.h"),
                    "$SOURCES",
                    "-c",
                    "-o",
                    "$TARGET",
                    '@"%s"'
                    % _file_long_data(
                        env,
                        " ".join(
                            ["-I%s" % inc for inc in env.get("CPPPATH", [])]
                        ),
                    ),
                ]
            ),
            "Compiling Swift `$SOURCES` files to `$TARGET`",
        ),
    )

    return swift_obj

swift_sources = [
    os.path.join(env.subst("$PROJECT_SRC_DIR"), f)
    for f in SWIFT_SOURCE_FILES
]

swift_obj_lib = compile_swift_sources(swift_sources)
env.Prepend(LINKFLAGS=[swift_obj_lib])
env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", swift_obj_lib)