Merge branch 'development' (SwiftPM & XCTest available)
This commit is contained in:
commit
aa82e622df
|
@ -1,5 +1,7 @@
|
|||
.DS_Store
|
||||
|
||||
.build/
|
||||
build/
|
||||
y-build/
|
||||
|
||||
*.xcodeproj/
|
||||
|
|
|
@ -15,10 +15,3 @@ NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPO
|
|||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====================================================================================================
|
||||
Includes swift-tap
|
||||
https://github.com/dankogai/swift-tap
|
||||
Copyright (c) 2016 Dan Kogai
|
||||
Released under the MIT License.
|
||||
- See "Sources/Test/TAP.swift"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// swift-tools-version:3.1
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "TimeSpecification",
|
||||
swiftLanguageVersions:[3]
|
||||
)
|
|
@ -30,7 +30,7 @@ public enum Clock {
|
|||
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`
|
||||
`swiftc ./your/project/main.swift -I/path/to/your/system/include -L/path/to/your/system/lib -lSwiftTimeSpecification`
|
||||
|
||||
# Sample Code
|
||||
```
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
// https://github.com/dankogai/swift-tap/blob/master/tap/tap.swift
|
||||
|
||||
//
|
||||
// tap.swift
|
||||
// tap
|
||||
//
|
||||
// Created by Dan Kogai on 1/21/16.
|
||||
// Copyright © 2016 Dan Kogai. All rights reserved.
|
||||
//
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Dan Kogai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#if os(Linux)
|
||||
import Glibc
|
||||
#else
|
||||
import Darwin
|
||||
#endif
|
||||
|
||||
open class TAP {
|
||||
var tests = 0, runs = [Bool]()
|
||||
public init() {}
|
||||
public init(tests:Int) {
|
||||
self.plan(tests)
|
||||
}
|
||||
/// sets the number of tests to run. call it before the first test
|
||||
open func plan(_ tests:Int) {
|
||||
self.tests = tests
|
||||
print("1..\(tests)")
|
||||
}
|
||||
/// ok if `predicate` is true
|
||||
@discardableResult
|
||||
open func ok(_ predicate:@autoclosure ()->Bool, _ message:String = "")->Bool {
|
||||
let ok = predicate()
|
||||
runs.append(ok)
|
||||
let ornot = ok ? "" : "not "
|
||||
print("\(ornot)ok \(runs.count) - \(message)")
|
||||
return ok
|
||||
}
|
||||
/// ok if `actual` == `expected`
|
||||
@discardableResult
|
||||
open func eq<T:Equatable>(_ actual:T, _ expected:T, _ message:String = "")->Bool {
|
||||
if ok(actual == expected, message) { return true }
|
||||
print("# got: \(actual)")
|
||||
print("# expected: \(expected)")
|
||||
return false
|
||||
}
|
||||
/// ok if `actual` == `expected`
|
||||
@discardableResult
|
||||
open func eq<T:Equatable>(_ actual:T?, _ expected:T?, _ message:String = "")->Bool {
|
||||
if ok(actual == expected, message) { return true }
|
||||
print("# got: \(actual as T?)")
|
||||
print("# expected: \(expected as T?)")
|
||||
return false
|
||||
}
|
||||
/// ok if arrays are `actual` == `expected`
|
||||
@discardableResult
|
||||
open func eq<T:Equatable>(_ actual:[T], _ expected:[T], _ message:String = "")->Bool {
|
||||
if ok(actual == expected, message) { return true }
|
||||
print("# got: \(actual)")
|
||||
print("# expected: \(expected)")
|
||||
return false
|
||||
}
|
||||
/// ok if dictionaries are `actual` == `expected`
|
||||
@discardableResult
|
||||
open func eq<K:Hashable,V:Equatable>(_ actual:[K:V], _ expected:[K:V], _ message:String = "")->Bool {
|
||||
if ok(actual == expected, message) { return true }
|
||||
print("# got: \(actual)")
|
||||
print("# expected: \(expected)")
|
||||
return false
|
||||
}
|
||||
@discardableResult
|
||||
/// ok if `actual` != `expected`
|
||||
open func ne<T:Equatable>(_ actual:T, _ expected:T, _ message:String = "")->Bool {
|
||||
if ok(actual != expected, message) { return true }
|
||||
print("# got: \(actual)")
|
||||
print("# expected: anthing but \(expected)")
|
||||
return false
|
||||
}
|
||||
/// ok if `actual` != `expected`
|
||||
@discardableResult
|
||||
open func ne<T:Equatable>(_ actual:T?, _ expected:T?, _ message:String = "")->Bool {
|
||||
if ok(actual != expected, message) { return true }
|
||||
print("# got: \(actual as T?)")
|
||||
print("# expected: anthing but \(expected as T?)")
|
||||
return false
|
||||
}
|
||||
/// ok if arrays are `actual` == `expected`
|
||||
@discardableResult
|
||||
open func ne<T:Equatable>(_ actual:[T], _ expected:[T], _ message:String = "")->Bool {
|
||||
if ok(actual != expected, message) { return true }
|
||||
print("# got: \(actual)")
|
||||
print("# expected: anthing but \(expected)")
|
||||
return false
|
||||
}
|
||||
/// ok if dictionaries are `actual` == `expected`
|
||||
@discardableResult
|
||||
open func ne<K:Hashable,V:Equatable>(_ actual:[K:V], _ expected:[K:V], _ message:String = "")->Bool {
|
||||
if ok(actual != expected, message) { return true }
|
||||
print("# got: \(actual)")
|
||||
print("# expected: anthing but \(expected)")
|
||||
return false
|
||||
}
|
||||
/// checks the test results, print stuff if neccesary,
|
||||
/// and `exit()` with code == number of failures
|
||||
@discardableResult
|
||||
open func done(dontExit nx:Bool = false)->[Bool] {
|
||||
if runs.count == 0 && nx != true {
|
||||
print("# no test run!")
|
||||
exit(-1)
|
||||
}
|
||||
if tests == 0 {
|
||||
print("1..\(runs.count)")
|
||||
}
|
||||
else if runs.count != tests {
|
||||
print("not ok \(runs.count + 1) - planned to run \(tests) but actually ran \(runs.count)")
|
||||
runs.append(false)
|
||||
if nx != true { exit(-1) }
|
||||
}
|
||||
if nx != true {
|
||||
let code = min(254, runs.filter{ $0 == false }.count)
|
||||
exit(Int32(code))
|
||||
}
|
||||
return runs
|
||||
}
|
||||
deinit {
|
||||
done(dontExit:true)
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/***************************************************************************************************
|
||||
test.swift
|
||||
© 2016 YOCKOW.
|
||||
Licensed under MIT License.
|
||||
See "LICENSE.txt" for more information.
|
||||
**************************************************************************************************/
|
||||
|
||||
// FIXME: These are irresponsible tests below.
|
||||
|
||||
#if os(Linux)
|
||||
import Glibc
|
||||
#elseif os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
|
||||
import Darwin
|
||||
#else
|
||||
// UNKNOWN OS
|
||||
#endif
|
||||
|
||||
import TimeSpecification
|
||||
|
||||
let test = TAP()
|
||||
|
||||
// Normalization Tests
|
||||
let N1 = TimeSpecification(seconds:0, nanoseconds:1_234_567_890)
|
||||
let N2 = TimeSpecification(seconds:-1, nanoseconds:-1_234_567_890)
|
||||
test.ok(N1.seconds == 1 && N1.nanoseconds == 234_567_890, "Normalization Test 1")
|
||||
test.ok(N2.seconds == -3 && N2.nanoseconds == 765_432_110, "Normalization Test 2")
|
||||
|
||||
// Comparison Tests
|
||||
let C1 = TimeSpecification(seconds:100, nanoseconds:100)
|
||||
let C2 = TimeSpecification(seconds: 98, nanoseconds:2_000_000_100)
|
||||
let C3 = TimeSpecification(seconds:200, nanoseconds:100)
|
||||
let C4 = TimeSpecification(seconds:100, nanoseconds:200)
|
||||
test.eq(C1, C2, "Comparison Test 1")
|
||||
test.ok(C2 < C3, "Comparison Test 2")
|
||||
test.ok(C2 < C4, "Comparison Test 3")
|
||||
|
||||
// IntegerLiteralConvertible Tests
|
||||
let I1 = TimeSpecification(integerLiteral:100)
|
||||
let I2 = TimeSpecification(integerLiteral:-100)
|
||||
test.eq(I1, TimeSpecification(seconds:100, nanoseconds:0), "Integer Literal Convertible Test 1")
|
||||
test.eq(I2, TimeSpecification(seconds:-100, nanoseconds:0), "Integer Literal Convertible Test 2")
|
||||
|
||||
// FloatLiteralConvertible Tests
|
||||
let F1 = TimeSpecification(floatLiteral:-1.1)
|
||||
//// How to test...
|
||||
|
||||
// Sum and Difference Tests
|
||||
let L1 = TimeSpecification(seconds:100, nanoseconds:123_456_789)
|
||||
let R1 = TimeSpecification(seconds:100, nanoseconds:987_654_321)
|
||||
test.eq(L1 + R1, TimeSpecification(seconds:201, nanoseconds:111_111_110), "Sum Test 1")
|
||||
test.eq(L1 - R1, TimeSpecification(seconds:0, nanoseconds:-864_197_532), "Difference Test 1")
|
||||
|
||||
test.done(dontExit:true)
|
||||
|
||||
// Others
|
||||
print("\nOTHERS")
|
||||
print("C time(): \(time(nil))")
|
||||
print("Calendar Clock: \(Clock.Calendar.timeSpecification()!)")
|
||||
print("System Clock: \(Clock.System.timeSpecification()!)")
|
|
@ -57,7 +57,7 @@ public func <(lhs:TimeSpecification, rhs:TimeSpecification) -> Bool {
|
|||
return false
|
||||
}
|
||||
|
||||
/* IntegerLiteralConvertible */
|
||||
/* ExpressibleByIntegerLiteral */
|
||||
extension TimeSpecification {
|
||||
public typealias IntegerLiteralType = Int64
|
||||
public init(integerLiteral value:Int64) {
|
||||
|
@ -66,7 +66,7 @@ extension TimeSpecification {
|
|||
}
|
||||
}
|
||||
|
||||
/* FloatLiteralConvertible */
|
||||
/* ExpressibleByFloatLiteral */
|
||||
extension TimeSpecification {
|
||||
public typealias FloatLiteralType = Double
|
||||
public init(floatLiteral value:Double) {
|
|
@ -0,0 +1,6 @@
|
|||
import XCTest
|
||||
@testable import TimeSpecificationTests
|
||||
|
||||
XCTMain([
|
||||
testCase(TimeSpecificationTests.allTests),
|
||||
])
|
|
@ -0,0 +1,59 @@
|
|||
/***************************************************************************************************
|
||||
SwiftTimeSpecificationTests.swift
|
||||
© 2016-2017 YOCKOW.
|
||||
Licensed under MIT License.
|
||||
See "LICENSE.txt" for more information.
|
||||
**************************************************************************************************/
|
||||
|
||||
// FIXME: These are irresponsible tests below.
|
||||
|
||||
import XCTest
|
||||
@testable import TimeSpecification
|
||||
|
||||
class TimeSpecificationTests: XCTestCase {
|
||||
func testNormalization() {
|
||||
let N1 = TimeSpecification(seconds:0, nanoseconds:1_234_567_890)
|
||||
let N2 = TimeSpecification(seconds:-1, nanoseconds:-1_234_567_890)
|
||||
|
||||
XCTAssertTrue(N1.seconds == 1 && N1.nanoseconds == 234_567_890, "Normalization Test 1")
|
||||
XCTAssertTrue(N2.seconds == -3 && N2.nanoseconds == 765_432_110, "Normalization Test 2")
|
||||
}
|
||||
|
||||
func testComparison() {
|
||||
let C1 = TimeSpecification(seconds:100, nanoseconds:100)
|
||||
let C2 = TimeSpecification(seconds: 98, nanoseconds:2_000_000_100)
|
||||
let C3 = TimeSpecification(seconds:200, nanoseconds:100)
|
||||
let C4 = TimeSpecification(seconds:100, nanoseconds:200)
|
||||
XCTAssertEqual(C1, C2, "Comparison Test 1")
|
||||
XCTAssertTrue(C2 < C3, "Comparison Test 2")
|
||||
XCTAssertTrue(C2 < C4, "Comparison Test 3")
|
||||
}
|
||||
|
||||
func testIntegerLiteral() {
|
||||
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() {
|
||||
let F1: TimeSpecification = 1.1
|
||||
XCTAssertEqual(F1, TimeSpecification(seconds:1, nanoseconds:100_000_000), "ExpressibleByFloatLiteral Test 1")
|
||||
}
|
||||
|
||||
func testSumAndDifference() {
|
||||
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)
|
||||
]
|
||||
}
|
329
build-install.rb
329
build-install.rb
|
@ -2,138 +2,154 @@
|
|||
|
||||
=begin
|
||||
build-install.rb
|
||||
© 2016 YOCKOW.
|
||||
© 2016-2017 YOCKOW.
|
||||
Licensed under MIT License.
|
||||
See "LICENSE.txt" for more information.
|
||||
=end
|
||||
|
||||
# Requirements #####################################################################################
|
||||
require 'fileutils'
|
||||
require 'json'
|
||||
require 'optparse'
|
||||
require 'pathname'
|
||||
require 'open3'
|
||||
require 'shellwords'
|
||||
|
||||
# Constants
|
||||
# Constants ########################################################################################
|
||||
OS = case RbConfig::CONFIG['host_os']
|
||||
when /darwin/i then :OS_X
|
||||
when /darwin/i then :macOS
|
||||
when /linux/i then :Linux
|
||||
else :Other
|
||||
end
|
||||
RequiredSwiftVersion = '3.0'
|
||||
SharedLibraryPrefix = 'lib'
|
||||
SharedLibrarySuffix = (OS == :OS_X) ? '.dylib' : '.so'
|
||||
SourcesDirectory = (Pathname(__FILE__).dirname + 'Sources').expand_path
|
||||
LibrarySources = [
|
||||
'Library'
|
||||
].map{|fn| Pathname(fn)}
|
||||
TestSources = [
|
||||
'Test'
|
||||
].map{|fn| Pathname(fn)}
|
||||
SharedLibrarySuffix = (OS == :macOS) ? '.dylib' : '.so'
|
||||
ModuleName = 'TimeSpecification'
|
||||
ModuleLinkName = 'SwiftTimeSpecification'
|
||||
ModuleLinkName = 'Swift' + ModuleName
|
||||
RootDirectory = Pathname(__FILE__).dirname.expand_path
|
||||
|
||||
## Default Values
|
||||
## Default Values ##
|
||||
Defaults = {
|
||||
:swiftc => Pathname('swiftc'),
|
||||
:build_directory => Pathname('build'),
|
||||
:swift => Pathname('swift'),
|
||||
:build_directory => Pathname('./build'),
|
||||
:install => false,
|
||||
:prefix => Pathname('/usr/local'),
|
||||
:debug => false,
|
||||
:skip_build => false,
|
||||
:skip_test => false,
|
||||
:sdk => 'macosx',
|
||||
:clean => false
|
||||
}
|
||||
|
||||
## Options
|
||||
## Options ##
|
||||
Options = {
|
||||
:swiftc => nil,
|
||||
: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 = [:ninja, :swift, :build_directory, :prefix]
|
||||
|
||||
# Functions
|
||||
# Functions ########################################################################################
|
||||
## Error ##
|
||||
def failed(message)
|
||||
$stderr.puts(message)
|
||||
exit
|
||||
$stderr.puts("!!ERROR!! #{message}")
|
||||
exit(false)
|
||||
end
|
||||
|
||||
def all_files_in_dir(dirname, basedir)
|
||||
files = []
|
||||
directory = basedir + dirname
|
||||
Dir.foreach(directory.to_s) {|filename|
|
||||
next if filename =~ /\A\.\.?\Z/
|
||||
path = directory + filename
|
||||
if path.directory?
|
||||
files.concat(all_files_in_dir(filename, directory))
|
||||
else
|
||||
files.push(path)
|
||||
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
|
||||
}
|
||||
return files
|
||||
end
|
||||
def all_files(list, rootdir)
|
||||
files = []
|
||||
list.each {|filename|
|
||||
path = rootdir + filename
|
||||
failed("#{path.to_s}: No such file or directory.") if !path.exist?
|
||||
if path.directory?
|
||||
files.concat(all_files_in_dir(filename, rootdir))
|
||||
elsif
|
||||
files.push(path)
|
||||
end
|
||||
}
|
||||
return files
|
||||
end
|
||||
|
||||
# Extends class
|
||||
# Extends Class(es) ################################################################################
|
||||
## Pathname ##
|
||||
class Pathname
|
||||
def escaped
|
||||
return Shellwords.shellescape(self.to_s)
|
||||
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
|
||||
|
||||
# Parsing Options
|
||||
# Parse Options ####################################################################################
|
||||
OptionParser.new(__FILE__){|parser|
|
||||
parser.on('--swiftc=PATH',
|
||||
'Path to the Swift compiler which you want to use.') {|path|
|
||||
Options[:swiftc] = Pathname(path)
|
||||
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.') {|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.') {|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.])
|
||||
elsif Options[:prefix].exist? && !Options[:prefix].directory?
|
||||
failed(%Q["#{path}" is not directory.])
|
||||
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('--sdk=VALUE',
|
||||
'(OS X Only) SDK name to be passed to `xcrun`.'){|value|
|
||||
Options[:sdk] = value
|
||||
failed("Invalid SDK Name: #{sdk}") if sdk !~ /\A[\.0-9A-Z_a-z]+\Z/
|
||||
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
|
||||
|
@ -146,43 +162,27 @@ OptionParser.new(__FILE__){|parser|
|
|||
end
|
||||
}
|
||||
|
||||
# Set SDK Name if it's nil
|
||||
Options[:sdk] = Defaults[:sdk] if OS == :OS_X && Options[:sdk].nil?
|
||||
# Determine Options ################################################################################
|
||||
UnsavableOptions.each{|key| Options[key] = Defaults[key] if Options[key].nil?}
|
||||
|
||||
# Check SDK Name
|
||||
if OS == :OS_X && !system(%Q[xcrun --sdk #{Options[:sdk]} --show-sdk-path >/dev/null 2>&1])
|
||||
if $?.exitstatus == 127
|
||||
failed("'xcrun' does not exist.")
|
||||
else
|
||||
failed("Invalid SDK Name: #{Options[:sdk]}")
|
||||
end
|
||||
end
|
||||
|
||||
# Build Directory
|
||||
def exit_if_path_is_not_directory(path)
|
||||
failed(%Q["#{path.to_s}" is Not Directory]) if path.exist? && !path.directory?
|
||||
end
|
||||
## Build Directory
|
||||
Options[:build_directory] = Defaults[:build_directory] if Options[:build_directory].nil?
|
||||
Options[:build_directory] = Options[:build_directory].expand_path if Options[:build_directory].relative?
|
||||
exit_if_path_is_not_directory(Options[:build_directory])
|
||||
Options[:build_directory] += Options[:sdk] if !Options[:sdk].nil?
|
||||
exit_if_path_is_not_directory(Options[:build_directory])
|
||||
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)
|
||||
|
||||
# Set Options from defaults
|
||||
cache_txt = Options[:build_directory] + 'build_options-cache.txt'
|
||||
## Save/Read Cache
|
||||
cache_json = Options[:build_directory] + 'build_options-cache.json'
|
||||
saved_defaults = nil
|
||||
if cache_txt.exist?
|
||||
saved_defaults = JSON.parse(File.read(cache_txt.to_s), {:symbolize_names => true})
|
||||
if cache_json.exist?
|
||||
saved_defaults = JSON.parse(File.read(cache_json.to_s), {:symbolize_names => true})
|
||||
saved_defaults.each_key{|key|
|
||||
if key == :build_directory || key == :swiftc || key == :prefix
|
||||
saved_defaults[key] = Pathname(saved_defaults[key])
|
||||
end
|
||||
saved_defaults[key] = Pathname(saved_defaults[key]) if PathnameOptions.include?(key)
|
||||
}
|
||||
end
|
||||
Defaults.each_pair{|key, value|
|
||||
next if key == :sdk || key == :build_directory || key == :clean
|
||||
next if UnsavableOptions.include?(key)
|
||||
if Options[key].nil?
|
||||
if !saved_defaults.nil? && !saved_defaults[key].nil?
|
||||
Options[key] = saved_defaults[key]
|
||||
|
@ -191,126 +191,59 @@ Defaults.each_pair{|key, value|
|
|||
end
|
||||
end
|
||||
}
|
||||
File.write(cache_txt.to_s, JSON.dump(Options))
|
||||
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? }
|
||||
|
||||
# Determine Swift Path
|
||||
if !system(%Q[which #{Options[:swiftc].escaped} >/dev/null])
|
||||
failed("`swiftc` is not found.")
|
||||
end
|
||||
swiftc_command =
|
||||
(OS == :OS_X) ? "xcrun --sdk #{Options[:sdk]} #{Options[:swiftc].escaped}"
|
||||
: Options[:swiftc].escaped
|
||||
# Swift? ###########################################################################################
|
||||
failed("#{Options[:swift]} is not found.") if !system(%Q[which #{Options[:swift].escaped} >/dev/null])
|
||||
|
||||
# Detect Swift Version
|
||||
swift_version = %x[#{swiftc_command} --version]
|
||||
if swift_version =~ /^(?:Apple\s+)?Swift\s+version\s+(\d+)((?:\.\d+)*)\s+/
|
||||
swift_version = Regexp.last_match(1) + Regexp.last_match(2)
|
||||
if (swift_version.split('.').map(&:to_i) <=> RequiredSwiftVersion.split('.').map(&:to_i)) < 0
|
||||
failed("The minimum required version of Swift is #{RequiredSwiftVersion}")
|
||||
end
|
||||
else
|
||||
failed("Cannot detect the version of Swift: #{swift_version}")
|
||||
end
|
||||
# Build! ###########################################################################################
|
||||
configuration = Options[:debug] ? 'debug' : 'release'
|
||||
|
||||
# Print Options
|
||||
puts("Options:")
|
||||
puts(%Q[ Swift Compiler: #{Options[:swiftc].to_s}])
|
||||
puts(%Q[ Swift Version: #{swift_version}])
|
||||
puts(%Q[ The Build Directory: "#{Options[:build_directory].to_s}"])
|
||||
if Options[:install]
|
||||
puts(%Q[ The Installation Prefix: "#{Options[:prefix].to_s}"])
|
||||
else
|
||||
puts(%Q[ No products will be installed.])
|
||||
end
|
||||
puts(%Q[ Skip building: #{Options[:skip_build] ? "Yes" : "No"}])
|
||||
puts(%Q[ Skip testing: #{Options[:skip_test] ? "Yes" : "No"}])
|
||||
libFilename = Pathname(SharedLibraryPrefix + ModuleLinkName + SharedLibrarySuffix)
|
||||
libPath = Options[:build_directory] + Pathname(configuration) + libFilename
|
||||
moduleFilename = Pathname(ModuleName + '.swiftmodule')
|
||||
modulePath = Options[:build_directory] + Pathname(configuration) + moduleFilename
|
||||
|
||||
# Create directories
|
||||
build_include_directory = Options[:build_directory] + 'include'
|
||||
build_lib_directory = Options[:build_directory] + 'lib'
|
||||
if !Options[:skip_build] || !Options[:skip_test]
|
||||
FileUtils.mkdir_p(Options[:build_directory].to_s) if !File.exists?(Options[:build_directory])
|
||||
FileUtils.mkdir_p(build_include_directory.to_s) if !File.exists?(build_include_directory)
|
||||
FileUtils.mkdir_p(build_lib_directory.to_s) if !File.exists?(build_lib_directory)
|
||||
end
|
||||
|
||||
# Let's Start!
|
||||
def try_exec(command)
|
||||
puts(" Execute: #{command}")
|
||||
if !system(command)
|
||||
failed("Command exited with status #{$?.exitstatus}")
|
||||
end
|
||||
end
|
||||
|
||||
## BUILD
|
||||
# Build
|
||||
if !Options[:skip_build]
|
||||
puts("===== BUILD =====")
|
||||
|
||||
library_sources = all_files(LibrarySources, SourcesDirectory)
|
||||
|
||||
create_module =
|
||||
"#{swiftc_command} " +
|
||||
library_sources.map{|file| file.escaped}.join(' ') + ' ' +
|
||||
"-module-name #{ModuleName} " +
|
||||
"-module-link-name #{ModuleLinkName} " +
|
||||
"-emit-module-path #{build_include_directory.escaped}/#{ModuleName}.swiftmodule "
|
||||
try_exec(create_module)
|
||||
|
||||
build_library =
|
||||
"#{swiftc_command} " +
|
||||
library_sources.map{|file| file.escaped}.join(' ') + ' ' +
|
||||
"-module-name #{ModuleName} " +
|
||||
"-emit-library " +
|
||||
"-o#{build_lib_directory.escaped}/#{SharedLibraryPrefix}#{ModuleLinkName}#{SharedLibrarySuffix}"
|
||||
try_exec(build_library)
|
||||
|
||||
puts("...DONE")
|
||||
puts("[Start Building...]")
|
||||
try_exec(["swift 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
|
||||
# Test
|
||||
if !Options[:skip_test]
|
||||
puts("===== TEST =====")
|
||||
|
||||
test_executable = Options[:build_directory] + 'test'
|
||||
test_sources = all_files(TestSources, SourcesDirectory)
|
||||
|
||||
if !test_executable.exist?
|
||||
puts("Start building an executable")
|
||||
build_test =
|
||||
"#{swiftc_command} " +
|
||||
test_sources.map{|file| file.escaped}.join(' ') + ' ' +
|
||||
"-o#{test_executable.escaped} " +
|
||||
"-I#{build_include_directory.escaped} " +
|
||||
"-L#{build_lib_directory.escaped}"
|
||||
try_exec(build_test)
|
||||
if !Options[:debug]
|
||||
$stderr.puts("** WARNING ** No tests will be executed when `release` mode is specified.\n")
|
||||
else
|
||||
puts("[Start Testing...]")
|
||||
try_exec(["swift test --build-path #{Options[:build_directory].escaped()}",
|
||||
"--configuration #{configuration}"].join(" "), 2)
|
||||
end
|
||||
|
||||
puts("Start tests")
|
||||
if !system(%Q[LD_LIBRARY_PATH=#{build_lib_directory.escaped} #{test_executable.escaped}])
|
||||
failed("TEST FAILED.")
|
||||
end
|
||||
|
||||
puts("..SUCCESSFUL")
|
||||
end
|
||||
|
||||
## INSTALL
|
||||
if Options[:install]
|
||||
puts("===== INSTALL =====")
|
||||
# Install
|
||||
if !Options[:install]
|
||||
puts("[Installing...]")
|
||||
|
||||
include_directory = Options[:prefix] + 'include'
|
||||
lib_directory = Options[:prefix] + 'lib'
|
||||
|
||||
FileUtils.mkdir_p(include_directory.to_s) if !File.exists?(include_directory)
|
||||
FileUtils.mkdir_p(lib_directory.to_s) if !File.exists?(lib_directory)
|
||||
|
||||
begin
|
||||
FileUtils.copy_entry(build_include_directory.to_s,
|
||||
include_directory.to_s)
|
||||
FileUtils.copy_entry(build_lib_directory.to_s,
|
||||
lib_directory.to_s)
|
||||
rescue
|
||||
failed("Installation Failed.")
|
||||
if Options[:debug]
|
||||
$stderr.puts("** WARNING ** DEBUG MODE. Products to be installed may not be optimized.\n")
|
||||
end
|
||||
|
||||
puts("...DONE.")
|
||||
libInstallPath = Options[:prefix] + Pathname('lib') + libFilename
|
||||
moduleInstallPath = Options[:prefix] + Pathname('include') + moduleFilename
|
||||
|
||||
try_exec("cp #{libPath.escaped()} #{libInstallPath.escaped()} && " +
|
||||
"cp #{modulePath.escaped()} #{moduleInstallPath.escaped()}", 2)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue