diff --git a/.travis.yml b/.travis.yml index ab4289e..c72197f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,28 @@ -matrix: - include: - - os: linux - dist: trusty - sudo: required - language: ruby - before_install: - - export SWIFT_ARCHIVE_URL="https://swift.org/builds/swift-4.0-release/ubuntu1404/swift-4.0-RELEASE/swift-4.0-RELEASE-ubuntu14.04.tar.gz" - - curl ${SWIFT_ARCHIVE_URL} -o ../swift.tar.gz - - mkdir ../swift - - tar xzf ../swift.tar.gz -C ../swift --strip-components 1 - - export PATH="$(pwd)/../swift/usr/bin:${PATH}" - - os: osx - osx_image: xcode9 - sudo: required - language: objective-c +branches: + except: + - /^\d+\.\d+\.\d+(-\S*)?$/ +os: + - linux + - osx +language: generic +sudo: required +osx_image: xcode10.2 +dist: xenial +env: + - SWIFT_BUILD_OPTION="-Xswiftc -swift-version -Xswiftc 4" + - SWIFT_BUILD_OPTION="-Xswiftc -swift-version -Xswiftc 4.2" + - SWIFT_BUILD_OPTION="-Xswiftc -swift-version -Xswiftc 5" +before_install: + - | + if [ $(uname) = "Linux" ]; then + export SWIFT_ARCHIVE_URL="https://swift.org/builds/swift-5.0-release/ubuntu1604/swift-5.0-RELEASE/swift-5.0-RELEASE-ubuntu16.04.tar.gz" + curl ${SWIFT_ARCHIVE_URL} -o ../swift.tar.gz + mkdir ../swift + tar xzf ../swift.tar.gz -C ../swift --strip-components 2 + export PATH="$(dirname $(pwd))/swift/bin:$PATH" + fi script: - - swift build - - swift test + - swift build ${SWIFT_BUILD_OPTION} + - swift build --configuration release ${SWIFT_BUILD_OPTION} + - swift test ${SWIFT_BUILD_OPTION} diff --git a/Package.swift b/Package.swift index 3727227..f2a2d69 100644 --- a/Package.swift +++ b/Package.swift @@ -1,15 +1,23 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "TimeSpecification", - products: [.library(name: "SwiftTimeSpecification", type:.dynamic, targets: ["TimeSpecification"])], - dependencies: [], - targets: [ - .target(name: "TimeSpecification", dependencies: [], path:".", sources:["Sources"]), - .testTarget(name: "TimeSpecificationTests", dependencies: ["TimeSpecification"]) + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library(name: "SwiftTimeSpecification", type: .dynamic, targets: ["TimeSpecification"]), ], - swiftLanguageVersions:[3, 4] + dependencies: [ + // Dependencies declare other packages that this package depends on. + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target(name: "TimeSpecification", dependencies: []), + .testTarget(name: "TimeSpecificationTests", dependencies: ["TimeSpecification"]), + ], + swiftLanguageVersions: [.v4, .v4_2, .v5] ) + diff --git a/Package@swift-3.swift b/Package@swift-3.swift deleted file mode 100644 index b3bd2ce..0000000 --- a/Package@swift-3.swift +++ /dev/null @@ -1,8 +0,0 @@ -// swift-tools-version:3.1 - -import PackageDescription - -let package = Package( - name: "TimeSpecification", - swiftLanguageVersions:[3] -) diff --git a/README.md b/README.md index a522704..d0c024c 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,29 @@ # What is SwiftTimeSpecification? -SwiftTimeSpecification is an implementation of `struct timespec` (`struct mach_timespec` on OS X) in Swift Programming Language. +SwiftTimeSpecification is an implementation of `struct timespec` (`struct mach_timespec` on OS X) in Swift. Its prototype is [YOCKOW's Gist](https://gist.github.com/YOCKOW/12d9607cb30f40b79fb2). -[![Build Status](https://travis-ci.org/YOCKOW/SwiftTimeSpecification.svg?branch=master)](https://travis-ci.org/YOCKOW/SwiftTimeSpecification) - -## Class, Structure, Enumeration -``` -public struct TimeSpecification { - public var seconds:Int64 = 0 - public var nanoseconds:Int32 = 0 - /* ... */ -} -public enum Clock { - case calendar - case system - - public var timeSpecification: TimeSpecification? { - /* ... */ - } -} -``` - -# How to use -Build and install: -`./build-install.rb --install-prefix=/path/to/your/system --install` -Then, you can use it in your project: -`swiftc ./your/project/main.swift -I/path/to/your/system/include -L/path/to/your/system/lib -lSwiftTimeSpecification` - -# Sample Code +## Sample Code ``` import TimeSpecification func time(_ body:() -> Void) { - guard let start = Clock.system.timeSpecification else { return } + let start = TimeSpecification(clock:.stystem) body() - guard let end = Clock.system.timeSpecification else { return } + let end = TimeSpecification(clock:.stystem) let duration = end - start print("\(duration)") } ``` + + +# Requirements + +- Swift 5 (including compatibility mode for 4, 4.2) +- macOS or Linux + + +# License + +MIT License. +See "LICENSE.txt" for more information. diff --git a/Sources/TimeSpecification.swift b/Sources/TimeSpecification.swift deleted file mode 100644 index 696df66..0000000 --- a/Sources/TimeSpecification.swift +++ /dev/null @@ -1,131 +0,0 @@ -/*************************************************************************************************** - TimeSpecification.swift - © 2016-2017 YOCKOW. - Licensed under MIT License. - See "LICENSE.txt" for more information. - **************************************************************************************************/ - -#if os(Linux) - import Glibc - private typealias CTimeSpec = timespec -#elseif os(OSX) || os(iOS) || os(watchOS) || os(tvOS) - import Darwin - private let mach_task_self:() -> mach_port_t = { return mach_task_self_ } - private typealias CTimeSpec = mach_timespec_t -#else - // UNKNOWN OS -#endif - -public struct TimeSpecification { - public var seconds:Int64 = 0 - public var nanoseconds:Int32 = 0 { - didSet { self.normalize() } - } - - public init(seconds:Int64, nanoseconds:Int32) { - self.seconds = seconds - self.nanoseconds = nanoseconds - self.normalize() - } - - public mutating func normalize() { - // `nanoseconds` must be always zero or positive value and less than 1_000_000_000 - if self.nanoseconds >= 1_000_000_000 { - self.seconds += Int64(self.nanoseconds / 1_000_000_000) - self.nanoseconds = self.nanoseconds % 1_000_000_000 - } else if self.nanoseconds < 0 { - // For example, - // (seconds:3, nanoseconds:-2_123_456_789) - // -> (seconds:0, nanoseconds:876_543_211) - self.seconds += Int64(self.nanoseconds / 1_000_000_000) - 1 - self.nanoseconds = self.nanoseconds % 1_000_000_000 + 1_000_000_000 - } - } -} - -/// Comparable -extension TimeSpecification: Comparable { - public static func ==(lhs:TimeSpecification, rhs:TimeSpecification) -> Bool { - return (lhs.seconds == rhs.seconds && lhs.nanoseconds == rhs.nanoseconds) ? true : false - } - public static func <(lhs:TimeSpecification, rhs:TimeSpecification) -> Bool { - if lhs.seconds < rhs.seconds { return true } - if lhs.seconds > rhs.seconds { return false } - // then, in the case of (lhs.seconds == rhs.seconds) - if (lhs.nanoseconds < rhs.nanoseconds) { return true } - return false - } -} - -/// ExpressibleByIntegerLiteral -extension TimeSpecification: ExpressibleByIntegerLiteral { - public typealias IntegerLiteralType = Int64 - public init(integerLiteral value:Int64) { - self.seconds = value - self.nanoseconds = 0 - } -} - -/// ExpressibleByFloatLiteral -extension TimeSpecification: ExpressibleByFloatLiteral { - public typealias FloatLiteralType = Double - public init(floatLiteral value:Double) { - self.seconds = Int64(floor(value)) - self.nanoseconds = Int32((value - Double(self.seconds)) * 1.0E+9) - } -} - -/// Sum And Difference -extension TimeSpecification { - public static func +(lhs:TimeSpecification, rhs:TimeSpecification) -> TimeSpecification { - var result = lhs - result.seconds += rhs.seconds - result.nanoseconds += rhs.nanoseconds // always normalized - return result - } - public static func -(lhs:TimeSpecification, rhs:TimeSpecification) -> TimeSpecification { - var result = lhs - result.seconds -= rhs.seconds - result.nanoseconds -= rhs.nanoseconds // always normalized - return result - } - public static func +=(lhs:inout TimeSpecification, rhs:TimeSpecification) { - lhs = lhs + rhs // always normalized - } - public static func -=(lhs:inout TimeSpecification, rhs:TimeSpecification) { - lhs = lhs - rhs // always normalized - } -} - -/* Clock */ -extension TimeSpecification { - fileprivate init(_ cts:CTimeSpec) { - self.seconds = Int64(cts.tv_sec) - self.nanoseconds = Int32(cts.tv_nsec) - } -} -public enum Clock { - case calendar - case system - - public var timeSpecification: TimeSpecification? { - var c_timespec:CTimeSpec = CTimeSpec(tv_sec:0, tv_nsec:0) - - let clock_id:CInt - var retval:CInt = -1 - - #if os(Linux) - clock_id = (self == .calendar) ? CLOCK_REALTIME : CLOCK_MONOTONIC - retval = clock_gettime(clock_id, &c_timespec) - #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - var clock_name: clock_serv_t = 0 - clock_id = (self == .calendar) ? CALENDAR_CLOCK : SYSTEM_CLOCK - retval = host_get_clock_service(mach_host_self(), clock_id, &clock_name) - guard retval == 0 else { return nil } - retval = clock_get_time(clock_name, &c_timespec) - _ = mach_port_deallocate(mach_task_self(), clock_name) - #endif - - return (retval == 0) ? TimeSpecification(c_timespec) : nil - } -} diff --git a/Sources/TimeSpecification/TimeSpecification.swift b/Sources/TimeSpecification/TimeSpecification.swift new file mode 100644 index 0000000..59e1cff --- /dev/null +++ b/Sources/TimeSpecification/TimeSpecification.swift @@ -0,0 +1,153 @@ +/*************************************************************************************************** + TimeSpecification.swift + © 2016-2019 YOCKOW. + Licensed under MIT License. + See "LICENSE.txt" for more information. + **************************************************************************************************/ + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +import Darwin +private let mach_task_self:() -> mach_port_t = { return mach_task_self_ } +private typealias CTimeSpec = mach_timespec_t +#else +import Glibc +private typealias CTimeSpec = timespec +#endif + +/// The representation for the time in nanoseconds. +public struct TimeSpecification { + public var seconds: Int64 = 0 + private var _nanoseconds: Int32 = 0 +} + +extension TimeSpecification { + private mutating func _normalize() { + //`nanoseconds` must be always zero or positive value and less than 1_000_000_000 + if self._nanoseconds >= 1_000_000_000 { + self.seconds += Int64(self._nanoseconds / 1_000_000_000) + self._nanoseconds = self._nanoseconds % 1_000_000_000 + } else if self._nanoseconds < 0 { + // For example, + // (seconds:3, nanoseconds:-2_123_456_789) + // -> (seconds:0, nanoseconds:876_543_211) + self.seconds += Int64(self._nanoseconds / 1_000_000_000) - 1 + self._nanoseconds = self._nanoseconds % 1_000_000_000 + 1_000_000_000 + } + } + + public var nanoseconds: Int32 { + get { + return self._nanoseconds + } + set { + self._nanoseconds = newValue + self._normalize() + } + } +} + +extension TimeSpecification { + public init(seconds:Int64, nanoseconds:Int32) { + self.seconds = seconds + self.nanoseconds = nanoseconds // will be normalized + } +} + +extension TimeSpecification: Equatable { + public static func ==(lhs:TimeSpecification, rhs:TimeSpecification) -> Bool { + return lhs.seconds == rhs.seconds && lhs.nanoseconds == rhs.nanoseconds + } +} + +extension TimeSpecification: Comparable { + public static func < (lhs: TimeSpecification, rhs: TimeSpecification) -> Bool { + if lhs.seconds < rhs.seconds { return true } + if lhs.seconds > rhs.seconds { return false } + // Then, in the case of (lhs.seconds == rhs.seconds) ... + return lhs.nanoseconds < rhs.nanoseconds + } +} + +extension TimeSpecification: Hashable { + public func hash(into hasher:inout Hasher) { + hasher.combine(self.seconds) + hasher.combine(self.nanoseconds) + } +} + +extension TimeSpecification: ExpressibleByIntegerLiteral { + public typealias IntegerLiteralType = Int64 + public init(integerLiteral value: Int64) { + self.init(seconds: value, nanoseconds: 0) + } +} + +extension TimeSpecification: ExpressibleByFloatLiteral { + public typealias FloatLiteralType = Double + public init(floatLiteral value:Double) { + let double_seconds = floor(value) + self.init(seconds: Int64(double_seconds), nanoseconds: Int32((value - double_seconds) * 1.0E+9)) + } +} + +// sum and difference +extension TimeSpecification { + public static func +(lhs:TimeSpecification, rhs:TimeSpecification) -> TimeSpecification { + var result = lhs + result.seconds += rhs.seconds + result.nanoseconds += rhs.nanoseconds // always normalized + return result + } + + public static func -(lhs:TimeSpecification, rhs:TimeSpecification) -> TimeSpecification { + var result = lhs + result.seconds -= rhs.seconds + result.nanoseconds -= rhs.nanoseconds // always normalized + return result + } + + public static func +=(lhs:inout TimeSpecification, rhs:TimeSpecification) { + lhs = lhs + rhs // always normalized + } + + public static func -=(lhs:inout TimeSpecification, rhs:TimeSpecification) { + lhs = lhs - rhs // always normalized + } +} + +extension TimeSpecification { + public enum Clock { + /// Calendar Clock + /// + /// Note: This means `CLOCK_REALTIME` on Linux, `CALENDAR_CLOCK` on macOS. + case calendar + + /// System Clock + /// + /// Note: This means `CLOCK_MONOTONIC` on Linux, `SYSTEM_CLOCK` on macOS. + case system + } + + private init(_ cts:CTimeSpec) { + self.init(seconds:Int64(cts.tv_sec), nanoseconds:Int32(cts.tv_nsec)) + } + + /// Initialze with an instance of `Clock`. + public init(clock:Clock) { + var c_timespec:CTimeSpec = CTimeSpec(tv_sec:0, tv_nsec:0) + let clock_id:CInt + + #if os(Linux) + clock_id = (clock == .calendar) ? CLOCK_REALTIME : CLOCK_MONOTONIC + _ = clock_gettime(clock_id, &c_timespec) + #elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + var clock_name: clock_serv_t = 0 + clock_id = (clock == .calendar) ? CALENDAR_CLOCK : SYSTEM_CLOCK + _ = host_get_clock_service(mach_host_self(), clock_id, &clock_name) + _ = clock_get_time(clock_name, &c_timespec) + _ = mach_port_deallocate(mach_task_self(), clock_name) + #endif + + self.init(c_timespec) + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 8a75922..6590ab4 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,6 +1,8 @@ import XCTest -@testable import TimeSpecificationTests -XCTMain([ - testCase(TimeSpecificationTests.allTests), -]) +import TimeSpecificationTests + +var tests = [XCTestCaseEntry]() +tests += TimeSpecificationTests.__allTests() + +XCTMain(tests) diff --git a/Tests/TimeSpecificationTests/TimeSpecificationTests.swift b/Tests/TimeSpecificationTests/TimeSpecificationTests.swift index 6720385..b68095f 100644 --- a/Tests/TimeSpecificationTests/TimeSpecificationTests.swift +++ b/Tests/TimeSpecificationTests/TimeSpecificationTests.swift @@ -1,6 +1,6 @@ /*************************************************************************************************** SwiftTimeSpecificationTests.swift - © 2016-2017 YOCKOW. + © 2016-2019 YOCKOW. Licensed under MIT License. See "LICENSE.txt" for more information. **************************************************************************************************/ @@ -11,7 +11,7 @@ import XCTest @testable import TimeSpecification class TimeSpecificationTests: XCTestCase { - func testNormalization() { + func test_normalization() { let N1 = TimeSpecification(seconds:0, nanoseconds:1_234_567_890) let N2 = TimeSpecification(seconds:-1, nanoseconds:-1_234_567_890) @@ -19,7 +19,7 @@ class TimeSpecificationTests: XCTestCase { XCTAssertTrue(N2.seconds == -3 && N2.nanoseconds == 765_432_110, "Normalization Test 2") } - func testComparison() { + func test_comparison() { let C1 = TimeSpecification(seconds:100, nanoseconds:100) let C2 = TimeSpecification(seconds: 98, nanoseconds:2_000_000_100) let C3 = TimeSpecification(seconds:200, nanoseconds:100) @@ -29,31 +29,22 @@ class TimeSpecificationTests: XCTestCase { XCTAssertTrue(C2 < C4, "Comparison Test 3") } - func testIntegerLiteral() { + func test_integerLiteral() { let I1: TimeSpecification = 100 let I2: TimeSpecification = -100 XCTAssertEqual(I1, TimeSpecification(seconds:100, nanoseconds:0), "ExpressibleByIntegerLiteral Test 1") XCTAssertEqual(I2, TimeSpecification(seconds:-100, nanoseconds:0), "ExpressibleByIntegerLiteral Test 2") } - func testFloatLiteral() { + func test_floatLiteral() { let F1: TimeSpecification = 1.1 XCTAssertEqual(F1, TimeSpecification(seconds:1, nanoseconds:100_000_000), "ExpressibleByFloatLiteral Test 1") } - func testSumAndDifference() { + func test_sumAndDifference() { let L1 = TimeSpecification(seconds:100, nanoseconds:123_456_789) let R1 = TimeSpecification(seconds:100, nanoseconds:987_654_321) XCTAssertEqual(L1 + R1, TimeSpecification(seconds:201, nanoseconds:111_111_110), "Sum Test 1") XCTAssertEqual(L1 - R1, TimeSpecification(seconds:0, nanoseconds:-864_197_532), "Difference Test 1") - } - - static var allTests = [ - ("Normalization", testNormalization), - ("Comparison", testComparison), - ("ExpressibleByIntegerLiteral", testIntegerLiteral), - ("ExpressibleByFloatLiteral", testFloatLiteral), - ("+/-", testSumAndDifference) - ] } diff --git a/Tests/TimeSpecificationTests/XCTestManifests.swift b/Tests/TimeSpecificationTests/XCTestManifests.swift new file mode 100644 index 0000000..62abb27 --- /dev/null +++ b/Tests/TimeSpecificationTests/XCTestManifests.swift @@ -0,0 +1,22 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension TimeSpecificationTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__TimeSpecificationTests = [ + ("test_comparison", test_comparison), + ("test_floatLiteral", test_floatLiteral), + ("test_integerLiteral", test_integerLiteral), + ("test_normalization", test_normalization), + ("test_sumAndDifference", test_sumAndDifference), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(TimeSpecificationTests.__allTests__TimeSpecificationTests), + ] +} +#endif diff --git a/build-install.rb b/build-install.rb deleted file mode 100755 index ac92d48..0000000 --- a/build-install.rb +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env ruby - -=begin -build-install.rb - © 2016-2017 YOCKOW. - Licensed under MIT License. - See "LICENSE.txt" for more information. -=end - -ModuleName = 'TimeSpecification' -ModuleVersion = "0.0.0" - -# Requirements ##################################################################################### -require 'fileutils' -require 'json' -require 'optparse' -require 'pathname' -require 'open3' -require 'shellwords' - -# Constants ######################################################################################## -OS = case RbConfig::CONFIG['host_os'] - when /darwin/i then :macOS - when /linux/i then :Linux - else :Other -end -SharedLibraryPrefix = 'lib' -SharedLibrarySuffix = (OS == :macOS) ? '.dylib' : '.so' -ModuleLinkName = 'Swift' + ModuleName -RootDirectory = Pathname(__FILE__).dirname.expand_path - -## Default Values ## -Defaults = { - :swift => Pathname('swift'), - :build_directory => Pathname('./.build'), - :install => false, - :prefix => Pathname('/usr/local'), - :debug => false, - :skip_build => false, - :skip_test => false, - :clean => false -} -## Options ## -Options = { - :swift => nil, - :build_directory => nil, - :install => nil, - :prefix => nil, - :debug => nil, - :skip_build => nil, - :skip_test => nil, - :sdk => nil, - :clean => nil -} -## Canceled ## -Canceled = { - :debug => nil, - :skip_build => nil, - :skip_test => nil -} -### Qualifications ### -UnsavableOptions = [:build_directory, :sdk, :clean] -PathnameOptions = [:swift, :build_directory, :prefix] - -# Functions ######################################################################################## -## Error ## -def failed(message) - $stderr.puts("!!ERROR!! #{message}") - exit(false) -end - -## Run Shell Script -def try_exec(command, indent = 0) - puts(' ' * indent + "Execute: #{command}") - Open3.popen3(command) {|stdin, stdout, stderr, wait_thread| - stdin.close - stdout.each {|line| $stdout.puts(' ' * indent * 2 + line) } - stderr.each {|line| $stderr.puts(' ' * indent * 2 + line) } - - status = wait_thread.value.exitstatus - failed("Command exited with status #{status}") if status != 0 - } -end - -# Extends Class(es) ################################################################################ -## Pathname ## -class Pathname - def escaped(type = :shell) - return Shellwords.shellescape(self.to_s) if type == :shell - return self.to_s.gsub(/\$/, '$$').gsub(/\s/, '$\&') if type == :ninja - return self.to_s - end - def exit_if_i_am_file - failed("#{self.to_s} is not directory.") if self.exist? && !self.directory? - end -end - -# Parse Options #################################################################################### -OptionParser.new(__FILE__){|parser| - parser.on('--swift=PATH', - 'Path to `swift` which you want to use.') {|path| - path = Pathname(path) - path += 'swift' if path.directory? - Options[:swift] = Pathname(path) - } - parser.on('--build-dir=PATH', '--build-directory=PATH', - 'Name of the directory where the build products will be placed. ' + - 'Default: ' + Defaults[:build_directory].to_s) {|path| - Options[:build_directory] = Pathname(path) - } - - parser.on('--install', - 'Whether to install products or not.') {|value| - Options[:install] = value - } - parser.on('--prefix=PATH', '--install-prefix=PATH', - 'The installation prefix. (Only used when `--install` is specified.) ' + - 'Default: ' + Defaults[:prefix].to_s) {|path| - Options[:prefix] = Pathname(path) - Options[:prefix]= Options[:prefix].expand_path if path =~ /\A~\// - if !Options[:prefix].absolute? - failed(%Q[The installation prefix must be absolute path.]) - end - Options[:prefix].exit_if_i_am_file - Options[:install] = true # install library if prefix is specified. - } - - parser.on('--debug', - 'Debug builds') {|value| - Options[:debug] = value - } - parser.on('--release', - 'Release builds; default is on') {|value| - Canceled[:debug] = value - } - - parser.on('--skip-build', - 'Whether to skip building or not.') {|value| - Options[:skip_build] = value - } - parser.on('--do-build', - 'Cancel skipping building') {|value| - Canceled[:skip_build] = value - } - - parser.on('--skip-test', - 'Whether to skip testing or not.') {|value| - Options[:skip_test] = value - } - parser.on('--do-test', - 'Cancel skipping testing') {|value| - Canceled[:skip_test] = value - } - - parser.on('--clean', - 'Whether to clean up or not.') {|value| - Options[:clean] = value - } - - begin - parser.parse!(ARGV) - rescue OptionParser::ParseError => error - failed(error.message + "\n" + parser.help) - end -} - -# Determine Options ################################################################################ -UnsavableOptions.each{|key| Options[key] = Defaults[key] if Options[key].nil?} - -## Build Directory -Options[:build_directory] = Defaults[:build_directory] if Options[:build_directory].nil? -Options[:build_directory] = RootDirectory + Options[:build_directory] if Options[:build_directory].relative? -Options[:build_directory].exit_if_i_am_file -FileUtils.rm_r(Options[:build_directory].to_s) if Options[:clean] -FileUtils.mkdir_p(Options[:build_directory].to_s) - -## Save/Read Cache -cache_json = Options[:build_directory] + 'build_options-cache.json' -saved_defaults = nil -if cache_json.exist? - saved_defaults = JSON.parse(File.read(cache_json.to_s), {:symbolize_names => true}) - saved_defaults.each_key{|key| - saved_defaults[key] = Pathname(saved_defaults[key]) if PathnameOptions.include?(key) - } -end -Defaults.each_pair{|key, value| - next if UnsavableOptions.include?(key) - if Options[key].nil? - if !saved_defaults.nil? && !saved_defaults[key].nil? - Options[key] = saved_defaults[key] - else - Options[key] = Defaults[key] - end - end -} -Canceled.each_pair{|key, value| - Options[key] = false if !value.nil? && value -} -File.write(cache_json.to_s, JSON.dump(Options)) -Options.each_pair {|key, value| Options[key] = Defaults[key] if value.nil? } - -# Swift? ########################################################################################### -failed("#{Options[:swift]} is not found.") if !system(%Q[which #{Options[:swift].escaped()} >/dev/null]) - -# Build! ########################################################################################### -configuration = Options[:debug] ? 'debug' : 'release' - -binPath = Pathname(%x[#{Options[:swift].escaped()} build --build-path #{Options[:build_directory].escaped()} --configuration #{configuration} --show-bin-path].chomp) -libFilename = Pathname(SharedLibraryPrefix + ModuleLinkName + SharedLibrarySuffix) -libPath = binPath + libFilename -moduleFilename = Pathname(ModuleName + '.swiftmodule') -modulePath = binPath + moduleFilename - -# Build -if !Options[:skip_build] - puts("[Start Building...]") - try_exec(["#{Options[:swift].escaped()} build --build-path #{Options[:build_directory].escaped()}", - "--configuration #{configuration}", - "-Xswiftc -emit-library -Xswiftc -o#{libPath.escaped()}", - "-Xswiftc -module-link-name -Xswiftc #{ModuleLinkName}", - "-Xswiftc -module-name -Xswiftc #{ModuleName}", - "-Xswiftc -emit-module-path -Xswiftc #{modulePath.escaped()}"].join(" "), - 2) - puts() -end - -# Test -if !Options[:skip_test] - puts("[Start Testing...]") - if !Options[:debug] - $stderr.puts("** WARNING ** No tests will be executed unless debug mode is specified.") - else - try_exec(["#{Options[:swift].escaped()} test --build-path #{Options[:build_directory].escaped()}", - "--configuration #{configuration}"].join(" "), 2) - end - puts() -end - -# Install -if Options[:install] - puts("[Installing...]") - - if Options[:debug] - $stderr.puts("** WARNING ** DEBUG MODE. Products to be installed may not be optimized.\n") - end - - if ModuleVersion =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:\-(.+))?/ - majorVersion = $1 - minorVersion = $2 - patchVersion = $3 - prereleaseIdentifiers = $4 - - suffixes = [ - ".#{majorVersion}.#{minorVersion}", - ".#{majorVersion}", - "" - ] - if prereleaseIdentifiers - suffixes.unshift(".#{majorVersion}.#{minorVersion}.#{patchVersion}") - end - - libInstallDirPath = Options[:prefix] + Pathname('lib') - moduleInstallDirPath = Options[:prefix] + Pathname('include') - originalLibFilename = Pathname(SharedLibraryPrefix + ModuleLinkName + SharedLibrarySuffix + '.' + ModuleVersion) - originalModuleFilename = Pathname(ModuleName + '.swiftmodule' + '.' + ModuleVersion) - originalLibInstallPath = libInstallDirPath + originalLibFilename - originalModuleInstallPath = moduleInstallDirPath + originalModuleFilename - - try_exec("mkdir -p #{libInstallDirPath.escaped()}", 2) - try_exec("rm -f #{originalLibInstallPath.escaped()}", 2) - try_exec("cp -f #{libPath.escaped()} #{originalLibInstallPath.escaped()}", 2) - try_exec("mkdir -p #{moduleInstallDirPath.escaped()}", 2) - try_exec("rm -f #{originalModuleInstallPath.escaped()}", 2) - try_exec("cp -f #{modulePath.escaped()} #{originalModuleInstallPath.escaped()}", 2) - - suffixes.each_with_index{|suffix, ii| - sourceSuffix = (ii == 0) ? ('.' + ModuleVersion) : suffixes[ii - 1] - - libSourceFilename = Pathname(SharedLibraryPrefix + ModuleLinkName + SharedLibrarySuffix + sourceSuffix) - moduleSourceFilename = Pathname(ModuleName + '.swiftmodule' + sourceSuffix) - libDestFilename = Pathname(SharedLibraryPrefix + ModuleLinkName + SharedLibrarySuffix + suffix) - moduleDestFilename = Pathname(ModuleName + '.swiftmodule' + suffix) - - libSourcePath = libInstallDirPath + libSourceFilename - moduleSourcePath = moduleInstallDirPath + moduleSourceFilename - libDestPath = libInstallDirPath + libDestFilename - moduleDestPath = moduleInstallDirPath + moduleDestFilename - - try_exec("ln -fs #{libSourcePath.escaped()} #{libDestPath.escaped}", 2) - try_exec("ln -fs #{moduleSourcePath.escaped()} #{moduleDestPath.escaped}", 2) - } - else - $stderr.puts("Invalid Version") - end -end -