Migrate to Swift5 & Change APIs.
This commit is contained in:
parent
2db7d4147c
commit
8923a2458c
44
.travis.yml
44
.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}
|
||||
|
|
|
@ -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]
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
// swift-tools-version:3.1
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "TimeSpecification",
|
||||
swiftLanguageVersions:[3]
|
||||
)
|
45
README.md
45
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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import XCTest
|
||||
@testable import TimeSpecificationTests
|
||||
|
||||
XCTMain([
|
||||
testCase(TimeSpecificationTests.allTests),
|
||||
])
|
||||
import TimeSpecificationTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += TimeSpecificationTests.__allTests()
|
||||
|
||||
XCTMain(tests)
|
||||
|
|
|
@ -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)
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
296
build-install.rb
296
build-install.rb
|
@ -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
|
||||
|
Loading…
Reference in New Issue