Introduced Swift Package Manager and XCTest instead of swift-tap

This commit is contained in:
YOCKOW 2017-09-02 10:36:00 +09:00
parent 3c86124ed7
commit 50d04aad0c
10 changed files with 209 additions and 416 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
.DS_Store .DS_Store
.build/
build/ build/
y-build/ y-build/
*.xcodeproj/

View File

@ -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 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 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. 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"

8
Package.swift Normal file
View File

@ -0,0 +1,8 @@
// swift-tools-version:3.1
import PackageDescription
let package = Package(
name: "TimeSpecification",
swiftLanguageVersions:[3]
)

View File

@ -30,7 +30,7 @@ public enum Clock {
Build and install: Build and install:
`./build-install.rb --install-prefix=/path/to/your/system --install` `./build-install.rb --install-prefix=/path/to/your/system --install`
Then, you can use it in your project: 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 # Sample Code
``` ```

View File

@ -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)
}
}

View File

@ -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()!)")

View File

@ -57,7 +57,7 @@ public func <(lhs:TimeSpecification, rhs:TimeSpecification) -> Bool {
return false return false
} }
/* IntegerLiteralConvertible */ /* ExpressibleByIntegerLiteral */
extension TimeSpecification { extension TimeSpecification {
public typealias IntegerLiteralType = Int64 public typealias IntegerLiteralType = Int64
public init(integerLiteral value:Int64) { public init(integerLiteral value:Int64) {
@ -66,7 +66,7 @@ extension TimeSpecification {
} }
} }
/* FloatLiteralConvertible */ /* ExpressibleByFloatLiteral */
extension TimeSpecification { extension TimeSpecification {
public typealias FloatLiteralType = Double public typealias FloatLiteralType = Double
public init(floatLiteral value:Double) { public init(floatLiteral value:Double) {

6
Tests/LinuxMain.swift Normal file
View File

@ -0,0 +1,6 @@
import XCTest
@testable import TimeSpecificationTests
XCTMain([
testCase(TimeSpecificationTests.allTests),
])

View File

@ -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)
]
}

View File

@ -2,138 +2,154 @@
=begin =begin
build-install.rb build-install.rb
© 2016 YOCKOW. © 2016-2017 YOCKOW.
Licensed under MIT License. Licensed under MIT License.
See "LICENSE.txt" for more information. See "LICENSE.txt" for more information.
=end =end
# Requirements #####################################################################################
require 'fileutils' require 'fileutils'
require 'json' require 'json'
require 'optparse' require 'optparse'
require 'pathname' require 'pathname'
require 'open3'
require 'shellwords' require 'shellwords'
# Constants # Constants ########################################################################################
OS = case RbConfig::CONFIG['host_os'] OS = case RbConfig::CONFIG['host_os']
when /darwin/i then :OS_X when /darwin/i then :macOS
when /linux/i then :Linux when /linux/i then :Linux
else :Other else :Other
end end
RequiredSwiftVersion = '3.0'
SharedLibraryPrefix = 'lib' SharedLibraryPrefix = 'lib'
SharedLibrarySuffix = (OS == :OS_X) ? '.dylib' : '.so' SharedLibrarySuffix = (OS == :macOS) ? '.dylib' : '.so'
SourcesDirectory = (Pathname(__FILE__).dirname + 'Sources').expand_path
LibrarySources = [
'Library'
].map{|fn| Pathname(fn)}
TestSources = [
'Test'
].map{|fn| Pathname(fn)}
ModuleName = 'TimeSpecification' ModuleName = 'TimeSpecification'
ModuleLinkName = 'SwiftTimeSpecification' ModuleLinkName = 'Swift' + ModuleName
RootDirectory = Pathname(__FILE__).dirname.expand_path
## Default Values ## Default Values ##
Defaults = { Defaults = {
:swiftc => Pathname('swiftc'), :swift => Pathname('swift'),
:build_directory => Pathname('build'), :build_directory => Pathname('./build'),
:install => false, :install => false,
:prefix => Pathname('/usr/local'), :prefix => Pathname('/usr/local'),
:debug => false,
:skip_build => false, :skip_build => false,
:skip_test => false, :skip_test => false,
:sdk => 'macosx',
:clean => false :clean => false
} }
## Options ##
## Options
Options = { Options = {
:swiftc => nil, :swift => nil,
:build_directory => nil, :build_directory => nil,
:install => nil, :install => nil,
:prefix => nil, :prefix => nil,
:debug => nil,
:skip_build => nil, :skip_build => nil,
:skip_test => nil, :skip_test => nil,
:sdk => nil, :sdk => nil,
:clean => 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) def failed(message)
$stderr.puts(message) $stderr.puts("!!ERROR!! #{message}")
exit exit(false)
end end
def all_files_in_dir(dirname, basedir) ## Run Shell Script
files = [] def try_exec(command, indent = 0)
directory = basedir + dirname puts(' ' * indent + "Execute: #{command}")
Dir.foreach(directory.to_s) {|filename| Open3.popen3(command) {|stdin, stdout, stderr, wait_thread|
next if filename =~ /\A\.\.?\Z/ stdin.close
path = directory + filename stdout.each {|line| $stdout.puts(' ' * indent * 2 + line) }
if path.directory? stderr.each {|line| $stderr.puts(' ' * indent * 2 + line) }
files.concat(all_files_in_dir(filename, directory))
else status = wait_thread.value.exitstatus
files.push(path) failed("Command exited with status #{status}") if status != 0
end
} }
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 end
# Extends class # Extends Class(es) ################################################################################
## Pathname ##
class Pathname class Pathname
def escaped def escaped(type = :shell)
return Shellwords.shellescape(self.to_s) 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
end end
# Parsing Options # Parse Options ####################################################################################
OptionParser.new(__FILE__){|parser| OptionParser.new(__FILE__){|parser|
parser.on('--swiftc=PATH', parser.on('--swift=PATH',
'Path to the Swift compiler which you want to use.') {|path| 'Path to `swift` which you want to use.') {|path|
Options[:swiftc] = Pathname(path) path = Pathname(path)
path += 'swift' if path.directory?
Options[:swift] = Pathname(path)
} }
parser.on('--build-dir=PATH', '--build-directory=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) Options[:build_directory] = Pathname(path)
} }
parser.on('--install', parser.on('--install',
'Whether to install products or not.') {|value| 'Whether to install products or not.') {|value|
Options[:install] = value Options[:install] = value
} }
parser.on('--prefix=PATH', '--install-prefix=PATH', 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] = Pathname(path)
Options[:prefix]= Options[:prefix].expand_path if path =~ /\A~\// Options[:prefix]= Options[:prefix].expand_path if path =~ /\A~\//
if !Options[:prefix].absolute? if !Options[:prefix].absolute?
failed(%Q[The installation prefix must be absolute path.]) failed(%Q[The installation prefix must be absolute path.])
elsif Options[:prefix].exist? && !Options[:prefix].directory?
failed(%Q["#{path}" is not directory.])
end 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', parser.on('--skip-build',
'Whether to skip building or not.') {|value| 'Whether to skip building or not.') {|value|
Options[:skip_build] = value Options[:skip_build] = value
} }
parser.on('--do-build',
'Cancel skipping building') {|value|
Canceled[:skip_build] = value
}
parser.on('--skip-test', parser.on('--skip-test',
'Whether to skip testing or not.') {|value| 'Whether to skip testing or not.') {|value|
Options[:skip_test] = value Options[:skip_test] = value
} }
parser.on('--sdk=VALUE', parser.on('--do-test',
'(OS X Only) SDK name to be passed to `xcrun`.'){|value| 'Cancel skipping testing') {|value|
Options[:sdk] = value Canceled[:skip_test] = value
failed("Invalid SDK Name: #{sdk}") if sdk !~ /\A[\.0-9A-Z_a-z]+\Z/
} }
parser.on('--clean', parser.on('--clean',
'Whether to clean up or not.') {|value| 'Whether to clean up or not.') {|value|
Options[:clean] = value Options[:clean] = value
@ -146,43 +162,27 @@ OptionParser.new(__FILE__){|parser|
end end
} }
# Set SDK Name if it's nil # Determine Options ################################################################################
Options[:sdk] = Defaults[:sdk] if OS == :OS_X && Options[:sdk].nil? UnsavableOptions.each{|key| Options[key] = Defaults[key] if Options[key].nil?}
# Check SDK Name ## Build Directory
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
Options[:build_directory] = Defaults[:build_directory] if Options[:build_directory].nil? Options[:build_directory] = Defaults[:build_directory] if Options[:build_directory].nil?
Options[:build_directory] = Options[:build_directory].expand_path if Options[:build_directory].relative? Options[:build_directory] = RootDirectory + Options[:build_directory] if Options[:build_directory].relative?
exit_if_path_is_not_directory(Options[:build_directory]) Options[:build_directory].exit_if_i_am_file
Options[:build_directory] += Options[:sdk] if !Options[:sdk].nil?
exit_if_path_is_not_directory(Options[:build_directory])
FileUtils.rm_r(Options[:build_directory].to_s) if Options[:clean] FileUtils.rm_r(Options[:build_directory].to_s) if Options[:clean]
FileUtils.mkdir_p(Options[:build_directory].to_s) FileUtils.mkdir_p(Options[:build_directory].to_s)
# Set Options from defaults ## Save/Read Cache
cache_txt = Options[:build_directory] + 'build_options-cache.txt' cache_json = Options[:build_directory] + 'build_options-cache.json'
saved_defaults = nil saved_defaults = nil
if cache_txt.exist? if cache_json.exist?
saved_defaults = JSON.parse(File.read(cache_txt.to_s), {:symbolize_names => true}) saved_defaults = JSON.parse(File.read(cache_json.to_s), {:symbolize_names => true})
saved_defaults.each_key{|key| saved_defaults.each_key{|key|
if key == :build_directory || key == :swiftc || key == :prefix saved_defaults[key] = Pathname(saved_defaults[key]) if PathnameOptions.include?(key)
saved_defaults[key] = Pathname(saved_defaults[key])
end
} }
end end
Defaults.each_pair{|key, value| Defaults.each_pair{|key, value|
next if key == :sdk || key == :build_directory || key == :clean next if UnsavableOptions.include?(key)
if Options[key].nil? if Options[key].nil?
if !saved_defaults.nil? && !saved_defaults[key].nil? if !saved_defaults.nil? && !saved_defaults[key].nil?
Options[key] = saved_defaults[key] Options[key] = saved_defaults[key]
@ -191,126 +191,59 @@ Defaults.each_pair{|key, value|
end end
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 # Swift? ###########################################################################################
if !system(%Q[which #{Options[:swiftc].escaped} >/dev/null]) failed("#{Options[:swift]} is not found.") if !system(%Q[which #{Options[:swift].escaped} >/dev/null])
failed("`swiftc` is not found.")
end
swiftc_command =
(OS == :OS_X) ? "xcrun --sdk #{Options[:sdk]} #{Options[:swiftc].escaped}"
: Options[:swiftc].escaped
# Detect Swift Version # Build! ###########################################################################################
swift_version = %x[#{swiftc_command} --version] configuration = Options[:debug] ? 'debug' : 'release'
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
# Print Options libFilename = Pathname(SharedLibraryPrefix + ModuleLinkName + SharedLibrarySuffix)
puts("Options:") libPath = Options[:build_directory] + Pathname(configuration) + libFilename
puts(%Q[ Swift Compiler: #{Options[:swiftc].to_s}]) moduleFilename = Pathname(ModuleName + '.swiftmodule')
puts(%Q[ Swift Version: #{swift_version}]) modulePath = Options[:build_directory] + Pathname(configuration) + moduleFilename
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"}])
# Create directories # Build
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
if !Options[:skip_build] if !Options[:skip_build]
puts("===== BUILD =====") puts("[Start Building...]")
try_exec(["swift build --build-path #{Options[:build_directory].escaped()}",
library_sources = all_files(LibrarySources, SourcesDirectory) "--configuration #{configuration}",
"-Xswiftc -emit-library -Xswiftc -o#{libPath.escaped()}",
create_module = "-Xswiftc -module-link-name -Xswiftc #{ModuleLinkName}",
"#{swiftc_command} " + "-Xswiftc -module-name -Xswiftc #{ModuleName}",
library_sources.map{|file| file.escaped}.join(' ') + ' ' + "-Xswiftc -emit-module-path -Xswiftc #{modulePath.escaped()}"].join(" "),
"-module-name #{ModuleName} " + 2)
"-module-link-name #{ModuleLinkName} " + puts()
"-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")
end end
## TEST # Test
if !Options[:skip_test] if !Options[:skip_test]
puts("===== TEST =====") if !Options[:debug]
$stderr.puts("** WARNING ** No tests will be executed when `release` mode is specified.\n")
test_executable = Options[:build_directory] + 'test' else
test_sources = all_files(TestSources, SourcesDirectory) puts("[Start Testing...]")
try_exec(["swift test --build-path #{Options[:build_directory].escaped()}",
if !test_executable.exist? "--configuration #{configuration}"].join(" "), 2)
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)
end end
puts("Start tests")
if !system(%Q[LD_LIBRARY_PATH=#{build_lib_directory.escaped} #{test_executable.escaped}])
failed("TEST FAILED.")
end
puts("..SUCCESSFUL")
end end
## INSTALL # Install
if Options[:install] if !Options[:install]
puts("===== INSTALL =====") puts("[Installing...]")
include_directory = Options[:prefix] + 'include' if Options[:debug]
lib_directory = Options[:prefix] + 'lib' $stderr.puts("** WARNING ** DEBUG MODE. Products to be installed may not be optimized.\n")
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.")
end 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 end