Swift enables you to conditionally compile code using build configurations. These #if
-delimited tests let you decide whether to include or exclude code from compilation. There are the literals true
and false
, and you can test for command line flags (-D <#flag#>
), but you can also test for current operating system (os
), architecture (arch
), and swift language release (swift
, added in Swift 2.2).
In the latest source, Swift offers the following conditional tests for operating system: “OSX
“, “tvOS
“, “watchOS
“, “iOS
“, “Linux
“, “FreeBSD
“, “Windows
“, and “Android
“, which is quite a lot more options than the language started with.
#if os (OSX) // use Cocoa stuff #endif
Valid architectures include: “arm
“, “arm64
“, “i386
“, “x86_64
“, “powerpc64
“, “powerpc64le
“, and “s390x
“.
#if (arch(i386) || arch(x86_64)) && os(iOS) print("Probably simulator") #endif
Swift version tests are always >=
, and you supply a version number, e.g. #if swift(>=3.0)
. Use negative tests (the exclamation point) or else clauses to test for “earlier than” conditions.
In Swift 3.0, expect to see a new canImport
configuration test, based on the recently accepted SE-0075 proposal. You’ll be able to test whether a module is available for import before calling the import statement or using code that’s tied to that module.
Topics brought up on the Swift evolution list for additional configuration tests include testing for compilation endianness, Objective-C interoperation, OS versions, vendor, simulator/emulator vs hardware destinations, and whether debug assertions can fire.
Endianness
The pieces are already in place to test for endianness, even though there is no build configuration test available at this time. The following code is ready in lib/Basic/LangOptions.cpp:
static const StringRef SupportedConditionalCompilationEndianness[] = { "little", "big" };
ObjC Interop
The same can be said for interop availability:
if (EnableObjCInterop) addPlatformConditionValue("_runtime", "_ObjC"); else addPlatformConditionValue("_runtime", "_Native");
OS Versions
Both the demand for this configuration and the code are a bit lighter. The OS name and architecture are available but it’s unclear whether calls like getMacOSXVersion(major, minor, micro)
can be provided for universal configuration.
Vendor
There appears to be a getVendor()
call defined but no specific values beyond llvm::Triple::Apple
are called out in code.
Simulator/Emulator vs Hardware
In Platform.cpp, you’ll find tests for the iOS Simulator, Apple TV Simulator, watch simulator, and:
bool swift::tripleIsAnySimulator(const llvm::Triple &triple) { return tripleIsiOSSimulator(triple) || tripleIsWatchSimulator(triple) || tripleIsAppleTVSimulator(triple); }
Here, platform names are broken down to macosx
, iphoneos
, iphonesimulator
, appletvos
, appletvsimulator
, watchos
, and watchsimulator
. Configuration tests at this detail level are not available in Swift today.
Debug Assertions
Testing for debug state is tricky. As previous posts have made clear, there’s not a common consensus on when “debug builds” are taking place:
- There’s a general consensus that a debug state occurs when assertions can fire and are not disabled by compile-time optimizations.
- The concept of “debug” is nuanced enough that introducing a single #if debug build configuration test is insufficient for substantial set of community members who interacted in previous discussions and Swift developers who have sent me feedback outside this list.
- Conditioning debug on Xcode debug/release schemes is a no-go.
- Hidden helper functions already exist in Swift.
- Some members of the core team believe using build configurations is the wrong point to conditionalize code.
Swift offers three assertion configurations _isDebugAssertConfiguration
, _isReleaseAssertConfiguration
, and _isFastAssertConfiguration
, which are guaranteed to be constant-folded away before final code generation.
I’m still not entirely sold on whether there should be a build configuration test, but it’s easy enough to promote at least _isDebugAssertConfiguration
(if not all three) to public developer-consumable items and eliminate the need to add custom build flags (such as -D debug
) in order to conditionalize expensive tests and verbose logging.
I’m not sure exactly what subset of the already-implemented tests need SE proposals to change the user-facing APIs. I’m curious as to how you’d prioritize configuration tests for those items not yet adopted into the official language. Thanks for your feedback on that.
Update:
@ericasadun Now called "platform conditions", to avoid confusion with the Xcode feature of the same name. https://t.co/D43adpZL86
— Jordan Rose (@UINT_MIN) June 1, 2016
Comments are closed.