Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catalyst support #34

Closed
dvkch opened this issue May 12, 2024 · 10 comments
Closed

Catalyst support #34

dvkch opened this issue May 12, 2024 · 10 comments

Comments

@dvkch
Copy link

dvkch commented May 12, 2024

Hi there!

First of all thank you for this amazing and easy to setup library :)

I've been using it for a couple apps using SPM and it works perfectly. I've only encountered one issue: when archiving a catalyst target it fails when running spp:

 /bin/sh -c /Users/user/Library/Developer/Xcode/DerivedData/App-fuvpklgnlnmjljbizexrsmuamxxm/Build/Intermediates.noindex/LicenseList.build/Release-maccatalyst/LicenseList.build/Script-9591152079458176390.sh

sandbox-exec: execvp() of '//Users/user/Library/Developer/Xcode/DerivedData/App-fuvpklgnlnmjljbizexrsmuamxxm/Build/Products/Release/spp' failed: No such file or directory
Command PhaseScriptExecution failed with a nonzero exit code

Indeed, spp exists but in the Release-maccatalyst folder, not Release.

For now I'm gonna do a simple symlink, but if you have any idea on how to fix this that would be amazing :)

EDIT:

it does look awfully similar to https://forums.swift.org/t/package-manager-executable-not-available-when-building-for-platforms-other-than-macos/56279/2, and the solution doesn't seem that simple to setup.

EDIT2:

Because the build folder always changes I had to be a bit creative. I created an aggregate build target, containing the following script, and added it as a dependecy for my main target

# Type a script or drag a script file from your workspace to insert its path.
SIMLINK_DIR=${BUILT_PRODUCTS_DIR/Release-maccatalyst/Release}
[ ! -d "$SIMLINK_DIR" ] && ln -s "$BUILT_PRODUCTS_DIR" "$SIMLINK_DIR"

This is...suboptimal, but at least lets the project archive for catalyst properly

@Kyome22
Copy link
Contributor

Kyome22 commented May 20, 2024

@dvkch Thank you for reporting this issue.
I also reproduced the issue.

I found that it is possible to build using the workaround below, but it does not solve the fundamental problem.

@main
struct PrepareLicenseList: BuildToolPlugin {
    // ...

    // This command works with the plugin specified in `Package.swift`.
    func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
+       var executablePath = try context.tool(named: "spp").path
+           .removingLastComponent()
+           .removingLastComponent()
+           .appending(["Debug-maccatalyst", "spp"])
        return [
            makeBuildCommand(
+               executablePath: executablePath,
                sourcePackagesPath: try sourcePackages(context.pluginWorkDirectory),
                outputPath: context.pluginWorkDirectory
            )
        ]
    }
}

https://github.com/cybozu/LicenseList/blob/support-catalyst/Plugins/PrepareLicenseList/main.swift

#if targetEnvironment(macCatalyst) preprocessor also doesn't work. X(

@dvkch
Copy link
Author

dvkch commented May 23, 2024

Oh! I didn't know you could modify the build commands like this, that's good to know! But as you say, the underlying issue is still there

@Kyome22
Copy link
Contributor

Kyome22 commented Nov 26, 2024

I will try this problem again.

@Kyome22
Copy link
Contributor

Kyome22 commented Nov 26, 2024

@dvkch
I tried it and it worked. #50

@dvkch
Copy link
Author

dvkch commented Nov 26, 2024

This seems great ! Do you think there would be a way to detect if we are building Release vs Debug ? Maybe something like this instead ? But honestly even if the env doesn't match, it seems already really great to have spp work for Catalyst !

   let macCatalystPath = try context.tool(named: "spp").path.string
      .replacingOccurrences(of: "/Debug/", with: "/Debug-maccatalyst/")
      .replacingOccurrences(of: "/Release/", with: "/Release-maccatalyst/")

@Kyome22
Copy link
Contributor

Kyome22 commented Nov 27, 2024

@dvkch I forgot about release builds.
Is the Debug-maccatalyst directory created even if you only do release builds?

And I realized that there is a problem with workaround.
#50 (comment)

@dvkch
Copy link
Author

dvkch commented Nov 27, 2024

I don't think it is, but it might still exist from a previous Debug build?

@Kyome22
Copy link
Contributor

Kyome22 commented Feb 14, 2025

This is possible if you have not executed cleaning build folder.
In any case, this is a Swift Package Manager specification issue and we may not able to resolve.

@Kyome22 Kyome22 closed this as completed Feb 14, 2025
@dvkch
Copy link
Author

dvkch commented Feb 22, 2025

I am surprised you are closing this since the patch above #34 (comment) should work, although i didn't have the time yet to look in more details. It may be improved by detecting the build config (since it is possible to have more than just Debug and Release) and use a regex to fix the tool path properly, but this should already handle most situations

@dvkch
Copy link
Author

dvkch commented Feb 22, 2025

I did take the time to look more into this and found what I think should be a sturdy fix, that worked on a clean build for me.

By replacing the createBuildCommands method with :

    func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
        fatalError(try! context.tool(named: "spp").url.absoluteString)
    }

I noticed that it returns ${BUILD_DIR}/${CONFIGURATION}/spp

I then added a script phase to my project to run

env
exit 1

and inspected the resulting available vars.

I confirmed that:

  • CONFIGURATION is equal to Debug and not Debug-maccatalyst
  • no specific variable is strictly equal to Debug-maccatalyst
  • a lot of paths were available in the env, the most promising one being TARGET_BUILD_DIR

Since we know that context.tool(name:...) can return an URL that uses env variables, I tried the following :

    func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
        return [
            makeBuildCommand(
                executableURL: URL(fileURLWithPath: "/${TARGET_BUILD_DIR}/spp"),
                sourcePackagesURL: try sourcePackages(context.pluginWorkDirectoryURL),
                outputURL: context.pluginWorkDirectoryURL.appending(path: "LicenseList.swift")
            )
        ]
    }

And i am happy to report that it works properly. the initial / is required, even though $TARGET_BUILD_DIR will end up adding another one, without the initial / it behaved like path relative to my source folder.

As an alternative I also though about replacing ${CONFIGURATION} by ${CONFIGURATION}-maccatalyst, to keep using context.tool(named: "spp").url and be more future proof; but I didn't see a way to determine if the plugin tool was building for catalyst or not (#if targetEnvironment(macCatalyst) doesnt seem to work there), and we can't know if the ${CONFIGURATION} variable will already have -maccatalyst in it or not (although i don't suppose it ever will) since we can't read the build env from the build tool (or at least i didn't find a way to achieve it)

I hope this can be integrated in the project, and if you have any more question I'd be more than happy to continue investigating this a bit more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants