Why won’t swift-hash build on the Swift Package Index?

all of swift-hash’s builds are failing on the Swift Package Index, except for the macOS and Linux builds. i can’t tell what the problem is based on the build logs, and i don’t have a Mac with XCode to debug locally.

anyone know what’s going on here?

There's an error further up:

workspace/Tests/SHA2/Tests.swift:2:8: error: no such module 'SHA2'
import SHA2

i saw that, that error does not make sense to me, because the SHA2 module is unconditionally built. so i don’t know why the iOS builder can’t find it…

The index is building your test tool, despite the fact that you have tried to not declare it as a target. I don't think your #if os pattern works the way you want in your Package.swift: the Package.swift is compiled on macOS, so we still attempt to declare the binary executables and associated target.

As to why the executable target fails to build on iOS, I have no idea: the dependency is certainly properly expressed. This may relate to Swift PM not being used to build binaries for iOS, but I don't know enough of the details here. The SwiftPM team may know more.

In the meantime, I recommend using platform conditionals to try to exclude or include targets.

Alternatively, your tests are simple enough that you could just use xctest and then everything would work fine.

isn’t that what #if os(iOS) does?

No. #if os(iOS) will be evaluated when your Package.swift itself runs. That'll be on your Mac. What you need is to evaluate some conditions like the target OS of your binary -- not the host OS where your compiler runs.

To do something conditional on the target platform, you'd do

          .when(platforms: [.iOS, .tvOS])

As a real-world example check swift-collection's Package.swift.


how do you apply platform conditions to a target, as opposed to a dependency or the entire package?

1 Like

Since there is no such thing as an executable for iOS, Xcode just runs off a cliff. A very long time ago, Xcode was smart enough to ignore non‐applicable targets, but it has not worked for years. For much of that time I used if ProcessInfo.processInfo.environment workarounds to remove development, test or example tools when a custom environment variable like TARGETING_IOS is present. That could solve things for your own workflow and CI, but was still useless for satisfying external indices that know nothing about your setup. Then more recently Xcode started caching manifests without respecting changes to the environment, and many use cases fell apart. I have mostly switched to making everything into a test instead, and then if I do not want it run all the time, I remove a letter from the test prefix so that it vanishes from test detection, but is still regularly checked for compilation validity. When I want to run it I restore the missing letter and then click to launch it.

Since plugins were added, this has started to bite just about everyone, since Xcode tries and fails to build every plugin for iOS, making iOS support and plugin use incompatible with each other. So I have restored hopes that it might actually be fixed someday.

P.S. when cannot not help you here, because even if you dissolve your target graph completely from something that resembles a pyramid into something that resembles scattered sand, it still contains every single target declared in the manifest. Since one of those targets is by its very existence invalid, any blanket attempt to build the package will still fail.

so there is no way to have an executable product and have successful iOS/tvOS/watchOS builds?

Exactly. (A fact I am in no way defending.)

If I understand correctly, what we (the Swift Package Index) could do is define an env variable, say SPI_BUILDER, when building packages and then package authors would have a chance to deploy your workaround.

We could also set a variable name that’s not SPI specific, say CI_RUN making it easier to use it in tandem with other setups. Although that bears the risk that we might inadvertently trigger behaviour that’s not intended for builds in our context.

Would that be useful, @taylorswift ?

1 Like

Just realised that that env variable would have to include the platform, which is feasible and actually already defined.

i don't think environment variables is the right way to go about this, right now i'm trying to follow the advice of Packages that contain tools (e.g. swift-markdown) don't seem to build on iOS · Issue #1299 · SwiftPackageIndex/SwiftPackageIndex-Server · GitHub and have the modules build individually through the schemes in .spi.yml.

so far it doesn’t seem to be working though. any tips on what im doing wrong would be helpful…

the SPM should just automatically exclude executables and plugins from deployment-only targets. i don't know why it doesn't.

I phrased that in past tense, because it no longer works very reliably. Since the changes to shared state and caching, the list of quirky ways to break the workaround keeps growing. For example, if Xcode is open and you try xcodebuild, parts of the build will borrow the (wrong) environment from Xcode instead of inheriting from the terminal session.

If you want to try anyway, some third party packages and development tools have already informally standardized on the Boolean environment flags TARGETING_MACOS, TARGETING_WINDOWS, TARGETING_WEB, TARGETING_UBUNTU, TARGETING_TVOS, TARGETING_IOS, TARGETING_ANDROID, TARGETING_AMAZON_LINUX and TARGETING_WATCHOS. See here for an example of it in use.

I have considered bringing it up around before, but it always felt like every use case was really just an indication of a bug that should be fixed instead, and that drawing attention to workarounds would just reduce the incentive to fix the bugs.

1 Like

Looks like @taylorswift has got it all working now :tada: but if anyone else has need for those environment variables, we could look into setting them. Best let us know via the discussions or by opening an issue.

One advantage we have is that there's not going to be an open Xcode. Our builds are purely driven by xcodebuild and swift build so maybe that would make it reliable enough.

1 Like

if it helps anyone else, i was following an old example and incorrectly appending the platform name to the XCode scheme in the .spi.yml configuration file. it should just be the product name (it looks like there's a maximum of 1 product that can be built this way).

it would be really helpful in the future if the .spi.yml format was documented somewhere!