migueldeicaza / SwiftGodot

New Godot bindings for Swift
https://migueldeicaza.github.io/SwiftGodotDocs/tutorials/swiftgodot-tutorials/
MIT License
1.07k stars 71 forks source link

Request: Create package plugin for export #130

Open alicerunsonfedora opened 11 months ago

alicerunsonfedora commented 11 months ago

Ideally, I'd like to have a package plugin that will build the library and copy them over accordingly. Something like:

swift package --allow-writing-to-package-directory export-godot --path ../Shounin/bin/

Note: I also have this request internally for myself, since I might look into this for my codebase (IOH-28).

migueldeicaza commented 11 months ago

For my editor work, I am assuming that the top-level directory for a project would contain a Package.swift, and that files would be stored into Sources/Directory as expected by Package.swift

So perhaps such a target could directly target the bin/ directory, and copy the binaries with the desired target platform (with the new SDK extension, this could also produce all the native platform payloads)

Matt-Is-Confused commented 11 months ago

This is not meant to be a solution to this issue in any way. It is also the first command plugin I have ever written and is lacking a lot. I am just sharing it as a convenient starting point for people to use while iterating. I have this bound to a keybind in vscode so I can build and copy the lib as I am working. It has just enough to work and complain if it doesn't.

I keep my library and Godot project in separate folders so the tool needs to take a drop off path as an argument. Since the command edits files outside of the package directory it needs to be run with the sandbox disabled The command ends up looking like this, swift package --disable-sandbox Build PATHSTRING

import Foundation
import PackagePlugin

// TODO: ADD SUPPORT FOR A (release/debug) INPUT

enum ToolError: Error {
    case tooFewArguments
    case tooManyArguments
    case missingDestinationPath
    case failedToBuild
}

@main
struct Build: CommandPlugin {
    func performCommand(context _: PackagePlugin.PluginContext, arguments: [String]) async throws {
        // This sucks
        guard arguments.count == 1 else {
            if arguments.count > 1 { throw ToolError.tooManyArguments }
            if arguments.count < 1 { throw ToolError.tooFewArguments }
            return
        }

        let destinationPath = Path(arguments[0])
        let pathExists = FileManager.default.fileExists(atPath: destinationPath.string)
        print("Read path as \(destinationPath) it Exists: \(pathExists)")
        guard pathExists else { throw ToolError.missingDestinationPath }

        // Ask the plugin host (SwiftPM or an IDE) to build our product.
        let result = try packageManager.build(
            .product("XI"),
            parameters: .init(configuration: .debug, logging: .concise)
        )

        // Check the result. Ideally this would report more details. // TODO HOW?
        guard result.succeeded else { throw ToolError.failedToBuild }

        for item in result.builtArtifacts {
            print("Found: \(item.path.string)")
            let destItem = destinationPath.appending(subpath: item.path.lastComponent)
            do {
                try FileManager.default.removeItem(atPath: destItem.string)
            } catch {
                print(error)
            }
            do {
                try FileManager.default.copyItem(atPath: item.path.string, toPath: destItem.string)
            } catch {
                print(error)
            }
        }
    }
}

If/when I need a better solution I would likely replace this with a wrapper for CommandPlugin that takes a configuration builder class. Possibly even generating/modifying the gdextension file from that config. Another improvement would be cross compiling. I don't know how to do that but I'll need to figure it out as building my library blue screens my windows machine