blob: d28778121e868107f062847ceea77e9d24922f7c [file] [log] [blame]
// Copyright 2017 The Tulsi Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Valid Apple Platform Types.
/// See https://docs.bazel.build/versions/master/skylark/lib/apple_common.html#platform_type
public enum PlatformType: String {
case ios
case macos
case tvos
case watchos
var buildSettingsDeploymentTarget: String {
switch self {
case .ios: return "IPHONEOS_DEPLOYMENT_TARGET"
case .macos: return "MACOSX_DEPLOYMENT_TARGET"
case .tvos: return "TVOS_DEPLOYMENT_TARGET"
case .watchos: return "WATCHOS_DEPLOYMENT_TARGET"
}
}
var simulatorSDK: String {
switch self {
case .ios: return "iphonesimulator"
case .macos: return "macosx"
case .tvos: return "appletvsimulator"
case .watchos: return "watchsimulator"
}
}
var deviceSDK: String {
switch self {
case .ios: return "iphoneos"
case .macos: return "macosx"
case .tvos: return "appletvos"
case .watchos: return "watchos"
}
}
/// Path of where the test host is expected to be built for each available platform.
func testHostPath(hostTargetPath: String, hostTargetProductName: String) -> String? {
switch self {
case .ios: return "$(BUILT_PRODUCTS_DIR)/\(hostTargetPath)/\(hostTargetProductName)"
case .macos: return "$(BUILT_PRODUCTS_DIR)/\(hostTargetPath)/Contents/MacOS/\(hostTargetProductName)"
case .tvos: return "$(BUILT_PRODUCTS_DIR)/\(hostTargetPath)/\(hostTargetProductName)"
case .watchos: return nil
}
}
}
/// Target platform and os version to be used when generating the project.
public struct DeploymentTarget : Equatable {
let platform: PlatformType
let osVersion: DottedVersion
public static func ==(lhs: DeploymentTarget, rhs: DeploymentTarget) -> Bool {
return lhs.platform == rhs.platform && lhs.osVersion == rhs.osVersion
}
}
/// Construct to represent a dotted version for comparison. Supports strings of the form "x.x.x".
public struct DottedVersion : Comparable, CustomStringConvertible {
// TODO(b/68662759): Keep storage limited to a String, instead of maintaining an additional array.
/// Version represented as an array of Ints, each indice representing position by periods.
private let storedVersion: [Int]
/// Version represented as a String.
private let storedVersionString: String
/// Character set to separate version information, which is merely a period.
// TODO(b/31809759): Needed for NSString.components; remove when Tulsi requires Xcode 8.3 minimum.
static private let dotSet = CharacterSet(charactersIn: ".")
/// Optional initializer; returns nil if the string passed is not correctly formatted.
init?(_ versionString: String) {
var storedVersion = [Int]()
// Validate to make sure the version is in the expected lexiographic format.
// TODO(b/31809759): Switch to String.split(...) when Tulsi requires Xcode 8.3 minimum.
for versionNumber in (versionString as NSString).components(separatedBy: DottedVersion.dotSet) {
// Allow for empty strings; treat as 0s to maintain ordinal form.
if versionNumber.isEmpty {
storedVersion.append(0)
continue
}
// Every string split by periods should be able to be expressed as an integer.
guard let versionSegment = Int(versionNumber) else {
print("For versionString of \(versionString), \(versionNumber) is not a number.")
return nil
}
// Construct the array representation as we pass each stage of validation.
storedVersion.append(versionSegment)
}
// Store the array representation.
self.storedVersion = storedVersion
// Compose the string representation from the array, removing leading 0s and trailing periods.
self.storedVersionString = storedVersion.map(String.init).joined(separator: ".")
}
/// Outputs version without new lines or extraneous characters, suitable for target names.
public var description: String {
return self.storedVersionString
}
/// If lengths don't match, extend the short array with "0"s to match longest's length.
private static func extendToGreatestLength(_ x: [Int], _ y: [Int]) -> (x: [Int], y: [Int]) {
// Use difference to determine how we need to pad the strings.
let xDifference = x.count - y.count
if xDifference == 0 {
// Return copies of the originals if no height adjustments need to be made.
return (x, y)
}
var xOut = x
var yOut = y
if xDifference > 0 {
// Add 0s to the Y array if the X array was found to be greater.
for _ in Array(repeating: 0, count: xDifference) {
yOut.append(0)
}
} else {
// Add 0s to the X array if the Y array was found to be greater.
for _ in Array(repeating: 0, count: -xDifference) {
xOut.append(0)
}
}
// Return copies with adjusted heights.
return (xOut, yOut)
}
/// Determine if the strings are equal. Covering that X.X.0 and X.X should be considered equal.
public static func == (x: DottedVersion, y: DottedVersion) -> Bool {
// TODO(b/68662759): Optimize to do direct string comparisons without requiring extra storage.
// Extend lengths to match for doing integer comparison at each location with ==<Element>.
let versionArrays = extendToGreatestLength(x.storedVersion, y.storedVersion)
return versionArrays.x == versionArrays.y
}
/// Do a lexiographic comparison between versions. To cover the X.X.X case for comparing version.
public static func < (x: DottedVersion, y: DottedVersion) -> Bool {
// TODO(b/68662759): Optimize to do direct string comparisons without requiring extra storage.
// Extend lengths to match for doing integer comparison at each location with zip(...).
let versionArrays = extendToGreatestLength(x.storedVersion, y.storedVersion)
for (xVersion, yVersion) in zip(versionArrays.x, versionArrays.y) {
// At the first inequality of integers, starting from leftmost position...
if xVersion != yVersion {
// Return if the leftmost location is less than the rightmost.
return xVersion < yVersion
}
}
// No inequality was found; return false.
return false
}
}