giginet / Scipio

A new build tool to generate XCFramework
https://giginet.github.io/Scipio/documentation/scipio
MIT License
481 stars 31 forks source link

Generated XCFrameworks missing Swift Macros #117

Open darrarski opened 6 months ago

darrarski commented 6 months ago

I'm trying to use Scipio to make XCFramework for Composable Architecture. At first, it seemed to be working fine, as I was able to use the generated XCFramework in Xcode project. However, I can't use Swift Macros that Composable Architecture exposes. When trying to do so, I get the following error:

External macro implementation type 'ComposableArchitectureMacros.ReducerMacro' could not be found for macro 'Reducer()'

I'm unsure how to debug the issue, but I found that Tuist does some extra work when creating Xcode targets from swift packages. It adds an additional build phase for each swift package that contains macros. The build scheme looks like this:

Copy Swift Macro executable into $BUILT_PRODUCT_DIR

#  This build phase serves two purposes:
#  - Force Xcode build system to compile the macOS executable transitively when compiling for non-macOS destinations
#  - Place the artifacts in the "Debug" directory where the built artifacts for the active destination live. We default to "Debug" because otherwise the Xcode editor fails to resolve the macro references.
if [[ -f "$BUILD_DIR/$CONFIGURATION/ComposableArchitectureMacros" && ! -f "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/ComposableArchitectureMacros" ]]; then
mkdir -p "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/"
cp "$BUILD_DIR/$CONFIGURATION/ComposableArchitectureMacros" "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/ComposableArchitectureMacros"
fi

Input Files: $BUILD_DIR/$CONFIGURATION/ComposableArchitectureMacros Output Files: $BUILD_DIR/Debug-$EFFECTIVE_PLATFORM_NAME/ComposableArchitectureMacros

I think the above build phase is crucial for the Swift Macros to work, when integrated in a different way than through SPM.

It would be great to use Scipio for generating XCFrameworks, and being able to use Swift Macros. I'm happy to help with further investigation and research possible solutions, but I'm not even sure where to start at the moment.

darrarski commented 6 months ago

I made some progress. It's nowhere close to a proper solution, but I made it work with some manual hacking, so I guess it's worth sharing. Here are the steps I take to use the XCFramework generated from the Composable Architecture package, along with contained Swift Macros:

  1. I built the Composable Architecture package.
  2. I copied these binary files from the build / derived data directory into some directory, so I can find them later easily:
    • CasePathsMacros
    • ComposableArchitectureMacros
    • DependenciesMacrosPlugin
    • PerceptionMacros
  3. I switched to my Xcode project (the one that uses Composable Architecture XCFramework).
  4. I've added the following script as a build phase to my target. It copies the binary files from the directory mentioned in point 2. into the project's build directory:
    cp "path/to/some/directory/*Macros" "$BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/"
  5. I've added the following "Other Swift Flags" (in the build settings) to my target:

    -load-plugin-executable $BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/ComposableArchitectureMacros#ComposableArchitectureMacros -load-plugin-executable $BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/DependenciesMacrosPlugin#DependenciesMacrosPlugin -load-plugin-executable $BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/PerceptionMacros#PerceptionMacros -load-plugin-executable $BUILD_DIR/Debug$EFFECTIVE_PLATFORM_NAME/CasePathsMacros#CasePathsMacros

Now, I'm able to use Composable Architecture XCFramework and use Swift Macros like @Reducer when running my app in iOS Simulator.

The only issue I found so far, is that when I use the macros in my code, Xcode previews are no longer working - fail to build due to the following error:

external macro implementation type 'ComposableArchitectureMacros.ReducerMacro' could not be found for macro 'Reducer()'

The broken previews need investigation, it may be a matter of telling Xcode where to find the macro binaries when building for previews.

I'm not sure if the above process could be fully automated with Scipio. It may require some manual work when integrating generated XCFramework with an Xcode project (like adding a build phase and updating swift compiler flags manually). I think that at least some parts of the process could be automated, like extracting the included macro binaries when generating XCFramework.

giginet commented 6 months ago

Hi. Thank you for opening this issue.

As you said, I've noticed the problem that Scipio couldn't build the package it requires a macro.

Adding -load-plugin-executable options will help us. I'm planning the following steps.

  1. Build macro target to the host OS as a regular executable
  2. Pass -load-plugin-executable as OTHER_LINKER_FLAGS

Scipio uses PIF; the format to represent the Xcode project generated by SwiftPM. We have to investigate the behavior to build such packages with Xcode build.

swift build --build-system xcode

I'm going to support the Swift Macro. I'll use TCA to test the feature.

darrarski commented 6 months ago

I did some research and put my findings into a repository. It contains a simple bash script that uses Scipio to build TCA and output XCFrameworks with Swift Macros binaries. There's also an example swift package where I try to integrate the generated frameworks and macros. It mostly works, but there are some issues. I may be missing some steps when building the frameworks, or when integrating with the example swift package.

https://github.com/darrarski/tca-frameworks