Swift Package - Distribute standalone executable with bundle - swift

I'm writing a Swift script as a standalone Swift Package. Part of this script needs to generate some files, for which I have templates.
Now, these templates are not compilable (they're HTML files); so in order to include them in the package I've included them as .copy("Templates").
When debugging my script, I can access the templates just fine, but when I try to archive the product, the executable doesn't have access to them anymore.
Here's my Package.swift:
let package = Package(
name: "flucs",
products: [
.executable(name: "Flucs",
targets: ["flucs"])
],
dependencies: [
.package(url: "https://github.com/MrSkwiggs/Netswift", .exact(.init(0, 3, 1))),
],
targets: [
.target(
name: "flucs",
dependencies: ["Netswift"],
path: "Sources",
resources: [
.copy("Templates")
]),
.testTarget(
name: "flucsTests",
dependencies: ["flucs"]),
]
)
My folder structure:
How can I distribute my script so that it also includes its resources?

SPM compiles your resources to a separate bundle but command line tool is just an executable without a bundle and any resources you add to your executable is simply ignored by Xcode (Build Phases > Copy Bundle Resources) for Release(Archive) builds.
If you look inside Bundle.module you can find:
...
// For command-line tools.
Bundle.main.bundleURL,
...
Where Bundle.main.bundleURL is a valid file url for the directory containing your command line executable so that it looks for your bundle next to your executable. And it works for Debug because XCode just compiles your resource bundle near your executable.
The simplest way to get Release executable with compiled .bundle file is build your package from command line:
swift build --configuration release
And then you can find them both in .build/release folder.

Related

Swift Package depend on local Xcode project

Is it possible for a Local Swift Package to depend on a Xcode Project (that creates a xcframework) in the same workspace?
I have tried things in the targets section in Package.swift file to point to the path where the local Xcode project is.
targets: [
.target(
name: "SomeJourney",
dependencies: ["OurUIFramework"]),
.binaryTarget(name: "OurUIFramework", path: "../../Frameworks/OurUIFramework")
]

Swift Package - How to exclude files in root git directory from the actual Swift Package?

I am creating a Swift Package that is essentially a wrapper for multiple XCFrameworks generated from Objective-C frameworks so they can be installed via SPM.
Everything works fine as far as creating the SP and ability to add it as a dependency to an app. But I have a bunch non-essential files included in the SP's repository that I don't want to include in the actual SP - i.e. They shouldn't show up in Xcode's navigator when the SP is added as a dependency.
(These consist of the source Obj-C Frameworks, README, Changelog, Xcode Workspace for demo app, Script files for generating the XCFrameworks, etc).
Is this even possible?
Or will SPM always checkout the entire repo and make all files visible to the user?
I have tried using various permutations of the Target specifiers: source, path, exclude but to no avail.
Here is the closest I can get with a valid manifest, but when I check out the SP in a dummy Xcode app, I can still see all the files from the repo included:
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WrapperSwiftPackage",
platforms: [.iOS(.v13)],
products: [
.library(name: "WrapperSwiftPackage", targets: ["WrapperSwiftPackage"])
],
dependencies: [],
targets: [
.target(
name: "WrapperSwiftPackage",
dependencies: [
"ObjCFramework1",
"ObjCFramework2"
],
path: "", // Set to root directory so we can exclude files below
exclude: [
"CHANGELOG.md",
"Dangerfile.swift",
"README.md",
"Workspace.xcworkspace",
"Scripts/generate-xcframework.sh",
"Scripts/link_git_hooks.sh",
"Objective-C Frameworks/"
],
sources: [
"Sources/WrapperSwiftPackage/main.swift",
"XCFrameworks/ObjCFramework1.xcframework",
"XCFrameworks/ObjCFramework2.xcframework"
]
),
.binaryTarget(name: "ObjCFramework1", path: "XCFrameworks/ObjCFramework1.xcframework"),
.binaryTarget(name: "ObjCFramework2", path: "XCFrameworks/ObjCFramework2.xcframework")
]
)
Not sure if that isn't a bug though, but I've accidentaly came up to one solution for this.
If you put an empty Package.swift (I mean, one like this):
// swift-tools-version:5.5
import PackageDescription
let package = Package()
into one of project subfolders, then even though SPM is checking the subfolder out, it's excluded from Xcode navigator, and thus, from the project visibility.
I would like to know if that's a bug or is it documented somewhere, every hint is appreciated.
Works with local and remote dependencies.

Cant run/find Executable Swift Package

Bottom line I am trying to run an executable from a swift package I created and linked to my target.
I think I'm missing something here, so I've created an executable swift package with the content
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "AutoLocalized",
platforms: [
.macOS(.v10_15), .iOS(.v12)
],
products: [
.executable(name: "autolocalized", targets: ["AutoLocalized"])
],
targets: [
.target(name: "AutoLocalized", dependencies: []),
.testTarget(name: "AutoLocalizedTests", dependencies: ["AutoLocalized"]),
]
)
so as I understand adding the ".executable" outputs a file I can then run via Xcode.
I am trying to run a build phase script using that output but I dont know how to access it.
When I try to add dependencies to my target I can find the "autolocalized" executable
but building the project I get an error:
After a lot of searching I found A way, maybe not the best but it workes.
So what I did is adding the package via SPM but without a dependency to any target, that means I had to make sure I build the package manually, finding it in the Build folder and using it directly from there, this was the only way I found to run mac os scripts using build phases in Xcode, I am positive others tried but no one found a good answer, so here it is.

Two almost identical targets in Vapor Xcode project

I want to configure Package.swift so that one target would be an extension to another, both of them should share the same code from the one folder, but for the "extended" version there is an additional subfolder. But configuration I try with path fails with "overlapping sources" error. So, how can I make two target with the same source folder?
.target(name: "App", dependencies: [ "Vapor" ... ], exclude: [ "Subfolder" ])
.target(name: "Extended", dependencies: [ "Vapor", ... ], path: "./Sources/App")
swift build ... error: target 'Extended' has sources overlapping sources...
SwiftPM is strict about one target gets to own the files. So you will need to set up a proper dependency chain for your files.
It sounds like Extended adds more functionality to App in this case. If so you want to have App all the things it currently is. Then have Extended depend on App and build all of the things exclusive to it.
This allows 1 target to own the source files and allows Extended to use the one implementation of those files.
In my case I had one of my executableTargets path set to the root "."
.executableTarget(
name: "ServiceA",
dependencies: [Vapor..],
path: "."),
.executableTarget(
name: "ServiceB",
dependencies: [Vapor..]),
After I removed the path from the constructor in ServiceA. I was able to set the proper folders and Xcode was able to infer the path of my executable targets
Sources
|_ServiceA
|_ServiceB
.executableTarget(
name: "ServiceA",
dependencies: [Vapor..])
.executableTarget(
name: "ServiceB",
dependencies: [Vapor..]),

How to generate a framework from a SwiftPM-generated XCode project?

I used SwiftPM to set up an XCode project for a framework; based on
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "MyThing",
products: [
.library(
name: "MyThing",
targets: ["MyThing"]),
],
dependencies: [
],
targets: [
.target(
name: "MyThing",
dependencies: [])
)
I ran swift package generate-xcodeproj and then pod install (based on a Podfile whose content shouldn't matter). I obtain MyThing.xcworkspace.
Now I figured that
xcodebuild -workspace MyThing.xcworkspace -scheme MyThing clean build
should create the .framework -- but it doesn't, only a binary file appears. I suspect some automatism is at work here since the source folder contains a file named main.swift, among others.
What do I have to do to get the framework built?
I need to script the whole process, to please no manual workarounds.
As of Swift 4.0 swift package generate-xcodeproj doesn't automatically generate schemes for all targets, but still makes those targets accessible from Xcode.
If creating a new scheme manually once is acceptable, you can do so and add your framework target as a scheme build target.
Otherwise, new schemes can be created programmatically with libraries like xcodeswift/xcproj, which allow you to parse a newly generated Xcode project and generate a new scheme with its XCSharedData class.
After that new framework scheme is created you can run the build with xcodebuild -workspace MyThing.xcworkspace -scheme MyThingFramework clean build.
Mixing script files and framework code in one target seems to confuse SwiftPM. Moving main.swift out of the source folder clears up things.
Scheme MyThing-Package creates all build products as specified in Package.swift, including the framework.
If you want to have the script files in the same XCode Workspace (for editing convenience), put them in their own target. You can even create an executable product (which won't create anything useful since an executable can't link to dynamic frameworks).