diff --git a/.gitignore b/.gitignore index 5e5d5ce..329e500 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,5 @@ Carthage/Build fastlane/report.xml fastlane/screenshots +.idea +.DS_Store diff --git a/Package.swift b/Package.swift index e5912dc..f3098cd 100644 --- a/Package.swift +++ b/Package.swift @@ -19,23 +19,28 @@ import PackageDescription -#if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +#if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Android) let package = Package( - name: "Signals", - products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. - .library( - name: "Signals", - targets: ["Signals"] - ) - ], - targets: [ - .target( - name: "Signals", - exclude: ["Signals.xcodeproj", "README.md", "Sources/Info.plist", "Sources/Signals.h", "Tests"] - ), - ] + name: "Signals", + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "Signals", + type: .dynamic, + targets: ["Signals"] + ) + ], + targets: [ + .target( + name: "Signals", + exclude: ["Signals.xcodeproj", "README.md", "Sources/Info.plist", "Sources/Signals.h", "Tests"] + ), + .target( + name: "Example", + dependencies: ["Signals"] + ), + ] ) #else diff --git a/Package@swift-4.swift b/Package@swift-4.swift index 63f2167..6b06845 100644 --- a/Package@swift-4.swift +++ b/Package@swift-4.swift @@ -22,20 +22,25 @@ import PackageDescription #if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) let package = Package( - name: "Signals", - products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. - .library( - name: "Signals", - targets: ["Signals"] - ) - ], - targets: [ - .target( - name: "Signals", - exclude: ["Signals.xcodeproj", "README.md", "Sources/Info.plist", "Sources/Signals.h", "Tests"] - ), - ] + name: "Signals", + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "Signals", + type: .dynamic, + targets: ["Signals"] + ) + ], + targets: [ + .target( + name: "Signals", + exclude: ["Signals.xcodeproj", "README.md", "Sources/Info.plist", "Sources/Signals.h", "Tests"] + ), + .target( + name: "Example", + dependencies: ["Signals"] + ), + ] ) #else diff --git a/README.md b/README.md index 1835f9b..8d0d785 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ Build Status - Master - macOS iOS - Linux + Android + macOS + Linux Apache 2 Slack Status @@ -45,16 +46,64 @@ BlueSignals version 2.0 and above supports Swift 5.1+. See older versions of Bl * Ubuntu 16.04 (or 16.10 but only tested on 16.04) and 18.04. * One of the Swift Open Source toolchain listed above. +### Android + +* macOS 10.15.7 (*Catalina*) or higher. +* Xcode Version 12.4 (12D4e) or higher using the included toolchain (*Recommended*). +* [Swift-android-toolchain-5.4.2-RELEASE](https://github.com/Guang1234567/swift_android_all_in_one/tree/swift_android_5.4.2_release) (**Recommended**) +* Android sdk(latest) and ndk(21.4.7075529) ## Build To build Signals from the command line: +- other platform ``` % cd % swift build ``` +- android + +``` +#!/usr/bin/env bash + +export ANDROID_HOME=$HOME/dev_kit/sdk/android_sdk +export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/21.4.7075529 + +# clone from https://github.com/Guang1234567/swift_android_all_in_one/tree/swift_android_5.4.2_release +export SWIFT_ANDROID_HOME=$HOME/dev_kit/sdk/swift-android-5.4.2-release-ndk21 + +export SWIFT_ANDROID_ARCH=aarch64 +#export SWIFT_ANDROID_ARCH=armv7 +#export SWIFT_ANDROID_ARCH=x86_64 +#export SWIFT_ANDROID_ARCH=x86 +export SWIFT_ANDROID_API=23 + +cd Swift_Signals + +echo -e "Running on macOS:\n=======================================\n" +swift run Example + +echo -e "Running on androidOS:\n=======================================\n" + +${SWIFT_ANDROID_HOME}/build-tools/1.9.7-swift5.4/swift-build --configuration debug -Xswiftc -DDEBUG -Xswiftc -g + +echo -e "Copy ELF to real android device :\n" + +adb push .build/aarch64-unknown-linux-android/debug/Example /data/local/tmp + +echo -e "Copy swift runtime SO to real android device :\n" + +adb push ${SWIFT_ANDROID_HOME}/toolchain/usr/lib/swift/android/${SWIFT_ANDROID_ARCH}/*.so /data/local/tmp + +echo -e "Running on real android device :\n" + +adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/Example + +cd .. +``` + ## Using Signals ### Including in your project diff --git a/Sources/Example/main.swift b/Sources/Example/main.swift new file mode 100644 index 0000000..31556f9 --- /dev/null +++ b/Sources/Example/main.swift @@ -0,0 +1,62 @@ +import Foundation +import Signals + +let reason = CommandLine.arguments.count == 2 ? CommandLine.arguments[1] : "unknown" + +func A1() -> Void { + A2() +} + +func A2() -> Void { + A3() +} + +func A3() -> Void { + B1() +} + +func B1() -> Void { + B2() +} + +func B2() -> Void { + B3() +} + +func B3() -> Void { + Van().drive() +} + +class Van { + func drive() -> Void { + + print("\n\n\n") + print("👇👇👇") + fatalError(reason) // raise SIGILL here !!! + print("👆👆👆") + print("\n\n\n") + } + + func oilEmpty() throws -> Void { + throw CarError.oilEmpty(message: "!!! oil empty !!!") + } +} + +enum CarError: Error { + case oilEmpty(message: String) +} + +Signals.trap(signal: Signals.Signal.ill) { signal in + + print("----> \(signal)") + + if (signal == SIGILL) { + #if os(macOS) + exit(EXIT_FAILURE) + #endif + } +} + +A1() + + diff --git a/Sources/Signals/Signals.swift b/Sources/Signals/Signals.swift index 5ba30e5..f0f9366 100644 --- a/Sources/Signals/Signals.swift +++ b/Sources/Signals/Signals.swift @@ -19,9 +19,9 @@ // #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - import Darwin -#elseif os(Linux) - import Glibc +import Darwin +#elseif os(Linux) || os(Android) +import Glibc #endif import Foundation @@ -29,172 +29,191 @@ import Foundation // MARK: Signals public class Signals { - - // MARK: Enums - - /// - /// Common OS Signals - /// - public enum Signal { - case hup - case int - case quit - case abrt - case kill - case alrm - case term - case pipe - case user(Int) - - /// - /// Obtain the OS dependent value of a Signal - /// - public var valueOf: Int32 { - - switch self { - case .hup: - return Int32(SIGHUP) - case .int: - return Int32(SIGINT) - case .quit: - return Int32(SIGQUIT) - case .abrt: - return Int32(SIGABRT) - case .kill: - return Int32(SIGKILL) - case .alrm: - return Int32(SIGALRM) - case .term: - return Int32(SIGTERM) - case .pipe: - return Int32(SIGPIPE) - case .user(let sig): - return Int32(sig) - - } - } - } - - - // MARK: Typealiases - - /// - /// Action handler signature. - /// - public typealias SigActionHandler = @convention(c)(Int32) -> Void - - - // MARK: Class Methods - - /// - /// Trap an operating system signal. - /// - /// - Parameters: - /// - signal: The signal to catch. - /// - action: The action handler. - /// - public class func trap(signal: Signal, action: @escaping SigActionHandler) { - - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - - var signalAction = sigaction(__sigaction_u: unsafeBitCast(action, to: __sigaction_u.self), sa_mask: 0, sa_flags: 0) - - _ = withUnsafePointer(to: &signalAction) { actionPointer in - - sigaction(signal.valueOf, actionPointer, nil) - } - - #elseif os(Linux) - - var sigAction = sigaction() - - sigAction.__sigaction_handler = unsafeBitCast(action, to: sigaction.__Unnamed_union___sigaction_handler.self) - - _ = sigaction(signal.valueOf, &sigAction, nil) - - #endif - } - - /// - /// Trap multiple signals to individual handlers - /// - /// - Parameter signals: An array of tuples each containing a signal and signal handler. - /// - public class func trap(signals: [(signal: Signal, action: SigActionHandler)]) { - - for sighandler in signals { - - Signals.trap(signal: sighandler.signal, action: sighandler.action) - } - } - - /// - /// Trap multiple signals to a single handler - /// - /// - Parameters: - /// - signals: An array of signals to catch. - /// - action: The action handler that will handle these signals. - /// - public class func trap(signals: [Signal], action: @escaping SigActionHandler) { - - for signal in signals { - - Signals.trap(signal: signal, action: action) - } - } - - /// - /// Raise an operating system signal - /// - /// - Parameter signal: The signal to raise. - /// - public class func raise(signal: Signal) { - - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - - _ = Darwin.raise(signal.valueOf) - - #elseif os(Linux) - - _ = Glibc.raise(signal.valueOf) - - #endif - } - - /// - /// Ignore a signal - /// - /// - Parameter signal: The signal to ignore. - /// - public class func ignore(signal: Signal) { - - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - - _ = Darwin.signal(signal.valueOf, SIG_IGN) - - #elseif os(Linux) - - _ = Glibc.signal(signal.valueOf, SIG_IGN) - - #endif - } - - /// - /// Restore default signal handling - /// - /// - Parameter signal: The signal to restore. - /// - public class func restore(signal: Signal) { - - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - - _ = Darwin.signal(signal.valueOf, SIG_DFL) - - #elseif os(Linux) - - _ = Glibc.signal(signal.valueOf, SIG_DFL) - - #endif - } - + + // MARK: Enums + + /// + /// Common OS Signals + /// + public enum Signal { + case hup + case int + case quit + case ill + case trap + case abrt + case kill + case pipe + case alrm + case term + case user(Int) + + /// + /// Obtain the OS dependent value of a Signal + /// + public var valueOf: CInt { + + switch self { + case .hup: + return CInt(SIGHUP) + case .int: + return CInt(SIGINT) + case .quit: + return CInt(SIGQUIT) + case .ill: + return CInt(SIGILL) + case .trap: + return CInt(SIGTRAP) + case .abrt: + return CInt(SIGABRT) + case .kill: + return CInt(SIGKILL) + case .pipe: + return CInt(SIGPIPE) + case .alrm: + return CInt(SIGALRM) + case .term: + return CInt(SIGTERM) + case .user(let sig): + return CInt(sig) + + } + } + } + + + // MARK: Typealiases + + /// + /// Action handler signature. + /// + public typealias SigActionHandler = @convention(c) (CInt) -> Void + + + // MARK: Class Methods + + /// + /// Trap an operating system signal. + /// + /// - Parameters: + /// - signal: The signal to catch. + /// - action: The action handler. + /// + public class func trap(signal: Signal, mask: sigset_t = sigset_t(), flags: CInt = 0, action: @escaping SigActionHandler) { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + var signalAction = sigaction( + __sigaction_u: __sigaction_u(__sa_handler: action), + sa_mask: mask, + sa_flags: flags + ) + + #elseif os(Linux) + + var signalAction = sigaction( + __sigaction_handler: unsafeBitCast(action, to: sigaction.__Unnamed_union___sigaction_handler.self), + sa_flags: flags, + sa_mask: mask, + sa_restorer: nil + ) + + #elseif os(Android) + + var signalAction = sigaction( + sa_flags: flags, + .init(sa_handler: action), + sa_mask: mask, + sa_restorer: nil) + + #endif + + _ = withUnsafePointer(to: &signalAction) { actionPointer in + + sigaction(signal.valueOf, actionPointer, nil) + } + } + + /// + /// Trap multiple signals to individual handlers + /// + /// - Parameter signals: An array of tuples each containing a signal and signal handler. + /// + public class func trap(signals: [(signal: Signal, action: SigActionHandler, mask: sigset_t, flags: CInt)]) { + + for item in signals { + + Signals.trap(signal: item.signal, mask: item.mask, flags: item.flags, action: item.action) + } + } + + /// + /// Trap multiple signals to a single handler + /// + /// - Parameters: + /// - signals: An array of signals to catch. + /// - action: The action handler that will handle these signals. + /// + public class func trap(signals: [Signal], mask: sigset_t = sigset_t(), flags: CInt = 0, action: @escaping SigActionHandler) { + + for signal in signals { + + Signals.trap(signal: signal, mask: mask, flags: flags, action: action) + } + } + + /// + /// Raise an operating system signal + /// + /// - Parameter signal: The signal to raise. + /// + public class func raise(signal: Signal) { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + _ = Darwin.raise(signal.valueOf) + + #elseif os(Linux) || os(Android) + + _ = Glibc.raise(signal.valueOf) + + #endif + } + + /// + /// Ignore a signal + /// + /// - Parameter signal: The signal to ignore. + /// + public class func ignore(signal: Signal) { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + _ = Darwin.signal(signal.valueOf, SIG_IGN) + + #elseif os(Linux) || os(Android) + + _ = Glibc.signal(signal.valueOf, SIG_IGN) + + #endif + } + + /// + /// Restore default signal handling + /// + /// - Parameter signal: The signal to restore. + /// + public class func restore(signal: Signal) { + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + + _ = Darwin.signal(signal.valueOf, SIG_DFL) + + #elseif os(Linux) || os(Android) + + _ = Glibc.signal(signal.valueOf, SIG_DFL) + + #endif + } + }