Is there a way to construct an #if statement that satisfies the following:
swift build
true
Package.swift opened directly in Xcode.
true
swift package generate-xcodeproj
false
I am aware of #if Xcode, but that seems to affect both of the bottom two rows.
The use case is a package that vends a subclass of XCTestCase. On iOS and tvOS, it compiles under the conditions marked true above, but fails to compile under the false cell (Unidentified symbol: _OBJC_CLASS_$_XCTestCAse). Clients should be able to use it where possible, but still be able to use the other vended types without a compiler failure in the remaining set‐ups.
You can pass a custom flag during project generation (swift package -Xswiftc -DDISABLE_TESTS generate-xcodeproj) but there's nothing builtin for this. However, your use case sounds like a bug in the generated project. Do you mind filing a JIRA?
In the end I found a way to differentiate between the two Xcode set‐ups, but it is rather roundabout and fragile.
I first stuck this temporarily into Package.swift:
import Foundation
fatalError("\(ProcessInfo.processInfo.environment)")
And with it there, I ran these two commands, noting the differences between the reported environments:
xcodebuild -list (with no Xcode project in the directory)
swift package generate-xcodeproj
Any difference can be used, but the thing that looked the most reliable to me was the fact that Xcode prepends itself to PATH when it is a superprocess. To turn this difference into a compile time condition, I added the following to the tail end of Package.swift:
import Foundation
let path = ProcessInfo.processInfo.environment["PATH"] ?? ""
let firstColon = path.range(of: ":")?.lowerBound ?? path.endIndex
let firstEntry = path[..<firstColon]
if firstEntry.hasSuffix("/Contents/Developer/usr/bin") {
let myTarget = package.targets.first(where: { $0.name == "MyTarget" })!
var settings = myTarget.swiftSettings ?? []
settings.append(.define("MANIFEST_LOADED_BY_XCODE"))
myTarget.swiftSettings = settings
}
Then #if MANIFEST_LOADED_BY_XCODE can be used in the target’s source code, yielding the following table:
swift build
#if !Xcode
Package.swift opened directly in Xcode
#if Xcode && MANIFEST_LOADED_BY_XCODE
swift package generate-xcodeproj
#if Xcode && !MANIFEST_LOADED_BY_XCODE
Note that while this is useful to evade Xcode bugs, deliberately designing a package to compile differently in these situations would generally be a bad idea.
If anyone out there finds a better workaround, let me know.