You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by dg...@apache.org on 2021/07/03 15:16:38 UTC

[openwhisk-runtime-swift] branch master updated: Add support for Swift 5.4 (#140)

This is an automated email from the ASF dual-hosted git repository.

dgrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-swift.git


The following commit(s) were added to refs/heads/master by this push:
     new 5dc532c  Add support for Swift 5.4 (#140)
5dc532c is described below

commit 5dc532c4c13a835fd39b9a20070b45234d1f135c
Author: Luke-Roy-IBM <83...@users.noreply.github.com>
AuthorDate: Sat Jul 3 17:16:30 2021 +0200

    Add support for Swift 5.4 (#140)
    
    * Add support for Swift 5.4
    - Added suport for Swift 5.4 by copying and adapting core of Swift 5.3. Files Modified during this Process (version set to 5.4 from 5.3):
    -- core/swift54Action/build.gradle
    -- core/swift54Action/CHANGELOG.md
    -- core/swift54Action/Dockerfile
    - Added Swift 5.4 entries to:
    -- settings.gradle
    -- .travis.yml
    -- README.md
    -- ansible/files/runtimes.json
    
    * Fix build by adapting akka versions (#139)
    * Add fixes from #139
    
    Co-authored-by: Martin Henke <ma...@web.de>
---
 .travis.yml                                        |   2 +-
 README.md                                          |   1 +
 ansible/files/runtimes.json                        |  14 ++
 core/swift54Action/CHANGELOG.md                    |  23 +++
 core/swift54Action/Dockerfile                      |  67 +++++++++
 .../swift54Action/Package.swift                    |  48 +++---
 core/swift54Action/_Whisk.swift                    | 167 +++++++++++++++++++++
 settings.gradle => core/swift54Action/build.gradle |  30 +---
 settings.gradle => core/swift54Action/main.swift   |  35 +----
 core/swift54Action/swiftbuild.py                   | 119 +++++++++++++++
 core/swift54Action/swiftbuild.py.launcher.swift    | 133 ++++++++++++++++
 core/swift54Action/swiftbuildandlink.sh            |  18 +++
 settings.gradle                                    |   5 +-
 tests/dat/build.sh                                 |   5 +
 tools/travis/build.sh                              |   1 +
 tools/travis/publish.sh                            |   2 +
 16 files changed, 584 insertions(+), 86 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 6d780d2..d3eb103 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,7 +47,7 @@ deploy:
       branch: master
       repo: apache/openwhisk-runtime-swift
   - provider: script
-    script: "./tools/travis/publish.sh openwhisk 5.1 nightly && ./tools/travis/publish.sh openwhisk 5.3 nightly"
+    script: "./tools/travis/publish.sh openwhisk 5.1 nightly && ./tools/travis/publish.sh openwhisk 5.3 nightly && ./tools/travis/publish.sh openwhisk 5.4 nightly"
     on:
       branch: master
       repo: apache/openwhisk-runtime-swift
diff --git a/README.md b/README.md
index 9838634..c1d76c6 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
 - [Swift 4.2   CHANGELOG.md](core/swift42Action/CHANGELOG.md)
 - [Swift 5.1   CHANGELOG.md](core/swift51Action/CHANGELOG.md)
 - [Swift 5.3   CHANGELOG.md](core/swift53Action/CHANGELOG.md)
+- [Swift 5.4   CHANGELOG.md](core/swift54Action/CHANGELOG.md)
 
 ## Quick Swift Action
 ### Simple swift action hello.swift
diff --git a/ansible/files/runtimes.json b/ansible/files/runtimes.json
index dbc4f3b..4239c7f 100644
--- a/ansible/files/runtimes.json
+++ b/ansible/files/runtimes.json
@@ -71,6 +71,20 @@
           "attachmentName": "codefile",
           "attachmentType": "text/plain"
         }
+      },
+      {
+        "kind": "swift:5.4",
+        "default": false,
+        "image": {
+          "prefix": "testing",
+          "name": "action-swift-v5.4",
+          "tag": "latest"
+        },
+        "deprecated": false,
+        "attached": {
+          "attachmentName": "codefile",
+          "attachmentType": "text/plain"
+        }
       }
     ]
   },
diff --git a/core/swift54Action/CHANGELOG.md b/core/swift54Action/CHANGELOG.md
new file mode 100644
index 0000000..395d500
--- /dev/null
+++ b/core/swift54Action/CHANGELOG.md
@@ -0,0 +1,23 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+# Apache OpenWhisk Swift 5.4 Runtime Container
+
+## 1.16.0
+ - Initial Release
diff --git a/core/swift54Action/Dockerfile b/core/swift54Action/Dockerfile
new file mode 100644
index 0000000..d6156d6
--- /dev/null
+++ b/core/swift54Action/Dockerfile
@@ -0,0 +1,67 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# build go proxy from source
+FROM golang:1.15 AS builder_source
+ARG GO_PROXY_GITHUB_USER=apache
+ARG GO_PROXY_GITHUB_BRANCH=master
+RUN git clone --branch ${GO_PROXY_GITHUB_BRANCH} \
+   https://github.com/${GO_PROXY_GITHUB_USER}/openwhisk-runtime-go /src ;\
+   cd /src ; env GO111MODULE=on CGO_ENABLED=0 go build main/proxy.go && \
+   mv proxy /bin/proxy
+
+# or build it from a release
+FROM golang:1.15 AS builder_release
+ARG GO_PROXY_RELEASE_VERSION=1.15@1.17.0
+RUN curl -sL \
+  https://github.com/apache/openwhisk-runtime-go/archive/{$GO_PROXY_RELEASE_VERSION}.tar.gz\
+  | tar xzf -\
+  && cd openwhisk-runtime-go-*/main\
+  && GO111MODULE=on go build -o /bin/proxy
+
+FROM swift:5.4
+
+# select the builder to use
+ARG GO_PROXY_BUILD_FROM=release
+
+RUN rm -rf /var/lib/apt/lists/* && apt-get clean && apt-get -qq update \
+	&& apt-get install -y --no-install-recommends locales python3 vim libssl-dev libicu-dev \
+	&& rm -rf /var/lib/apt/lists/* \
+	&& locale-gen en_US.UTF-8
+
+ENV LANG="en_US.UTF-8" \
+	LANGUAGE="en_US:en" \
+	LC_ALL="en_US.UTF-8"
+
+RUN mkdir -p /swiftAction
+WORKDIR /swiftAction
+
+COPY --from=builder_source /bin/proxy /bin/proxy_source
+COPY --from=builder_release /bin/proxy /bin/proxy_release
+RUN mv /bin/proxy_${GO_PROXY_BUILD_FROM} /bin/proxy
+ADD swiftbuild.py /bin/compile
+ADD swiftbuild.py.launcher.swift /bin/compile.launcher.swift
+COPY _Whisk.swift /swiftAction/Sources/
+COPY Package.swift /swiftAction/
+COPY swiftbuildandlink.sh /swiftAction/
+COPY main.swift /swiftAction/Sources/
+RUN swift build -c release; \
+	touch /swiftAction/Sources/main.swift; \
+	rm /swiftAction/.build/release/Action
+
+ENV OW_COMPILER=/bin/compile
+ENTRYPOINT [ "/bin/proxy" ]
diff --git a/settings.gradle b/core/swift54Action/Package.swift
similarity index 58%
copy from settings.gradle
copy to core/swift54Action/Package.swift
index ab6a605..181e352 100644
--- a/settings.gradle
+++ b/core/swift54Action/Package.swift
@@ -1,3 +1,6 @@
+// swift-tools-version:4.2
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,31 +18,20 @@
  * limitations under the License.
  */
 
-include 'tests'
-
-include 'core:swift42Action'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
-        version: '1.0.0-SNAPSHOT'
-]
-
-gradle.ext.scala = [
-    version: '2.12.7',
-    depVersion  : '2.12',
-    compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
-    version: '1.5.0',
-    config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
+import PackageDescription
+
+let package = Package(
+    name: "Action",
+    products: [
+      .executable(
+        name: "Action",
+        targets:  ["Action"]
+      )
+    ],
+    targets: [
+      .target(
+        name: "Action",
+        path: "."
+      )
+    ]
+)
diff --git a/core/swift54Action/_Whisk.swift b/core/swift54Action/_Whisk.swift
new file mode 100644
index 0000000..3f18e9d
--- /dev/null
+++ b/core/swift54Action/_Whisk.swift
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Foundation
+import Dispatch
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
+class Whisk {
+
+    static var baseUrl = ProcessInfo.processInfo.environment["__OW_API_HOST"]
+    static var apiKey = ProcessInfo.processInfo.environment["__OW_API_KEY"]
+    // This will allow user to modify the default JSONDecoder and JSONEncoder used by epilogue
+    static var jsonDecoder = JSONDecoder()
+    static var jsonEncoder = JSONEncoder()
+
+    class func invoke(actionNamed action : String, withParameters params : [String:Any], blocking: Bool = true) -> [String:Any] {
+        let parsedAction = parseQualifiedName(name: action)
+        let strBlocking = blocking ? "true" : "false"
+        let path = "/api/v1/namespaces/\(parsedAction.namespace)/actions/\(parsedAction.name)?blocking=\(strBlocking)"
+
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "POST")
+    }
+
+    class func trigger(eventNamed event : String, withParameters params : [String:Any]) -> [String:Any] {
+        let parsedEvent = parseQualifiedName(name: event)
+        let path = "/api/v1/namespaces/\(parsedEvent.namespace)/triggers/\(parsedEvent.name)?blocking=true"
+
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "POST")
+    }
+
+    class func createTrigger(triggerNamed trigger: String, withParameters params : [String:Any]) -> [String:Any] {
+        let parsedTrigger = parseQualifiedName(name: trigger)
+        let path = "/api/v1/namespaces/\(parsedTrigger.namespace)/triggers/\(parsedTrigger.name)"
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "PUT")
+    }
+
+    class func createRule(ruleNamed ruleName: String, withTrigger triggerName: String, andAction actionName: String) -> [String:Any] {
+        let parsedRule = parseQualifiedName(name: ruleName)
+        let path = "/api/v1/namespaces/\(parsedRule.namespace)/rules/\(parsedRule.name)"
+        let params = ["trigger":triggerName, "action":actionName]
+        return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "PUT")
+    }
+
+    // handle the GCD dance to make the post async, but then obtain/return
+    // the result from this function sync
+    private class func sendWhiskRequestSyncronish(uriPath path: String, params : [String:Any], method: String) -> [String:Any] {
+        var response : [String:Any]!
+
+        let queue = DispatchQueue.global()
+        let invokeGroup = DispatchGroup()
+
+        invokeGroup.enter()
+        queue.async {
+            postUrlSession(uriPath: path, params: params, method: method, group: invokeGroup) { result in
+                response = result
+            }
+        }
+
+        // On one hand, FOREVER seems like an awfully long time...
+        // But on the other hand, I think we can rely on the system to kill this
+        // if it exceeds a reasonable execution time.
+        switch invokeGroup.wait(timeout: DispatchTime.distantFuture) {
+        case DispatchTimeoutResult.success:
+            break
+        case DispatchTimeoutResult.timedOut:
+            break
+        }
+
+        return response
+    }
+
+
+    /**
+     * Using new UrlSession
+     */
+    private class func postUrlSession(uriPath: String, params : [String:Any], method: String,group: DispatchGroup, callback : @escaping([String:Any]) -> Void) {
+
+        guard let encodedPath = uriPath.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else {
+            callback(["error": "Error encoding uri path to make openwhisk REST call."])
+            return
+        }
+
+        let urlStr = "\(baseUrl!)\(encodedPath)"
+        if let url = URL(string: urlStr) {
+            var request = URLRequest(url: url)
+            request.httpMethod = method
+
+            do {
+                request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+                request.httpBody = try JSONSerialization.data(withJSONObject: params)
+
+                let loginData: Data = apiKey!.data(using: String.Encoding.utf8, allowLossyConversion: false)!
+                let base64EncodedAuthKey  = loginData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
+                request.addValue("Basic \(base64EncodedAuthKey)", forHTTPHeaderField: "Authorization")
+                let session = URLSession(configuration: URLSessionConfiguration.default)
+
+                let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
+
+                    // exit group after we are done
+                    defer {
+                        group.leave()
+                    }
+
+                    if let error = error {
+                        callback(["error":error.localizedDescription])
+                    } else {
+
+                        if let data = data {
+                            do {
+                                //let outputStr  = String(data: data, encoding: String.Encoding.utf8) as String!
+                                //print(outputStr)
+                                let respJson = try JSONSerialization.jsonObject(with: data)
+                                if respJson is [String:Any] {
+                                    callback(respJson as! [String:Any])
+                                } else {
+                                    callback(["error":" response from server is not a dictionary"])
+                                }
+                            } catch {
+                                callback(["error":"Error creating json from response: \(error)"])
+                            }
+                        }
+                    }
+                })
+
+                task.resume()
+            } catch {
+                callback(["error":"Got error creating params body: \(error)"])
+            }
+        }
+    }
+
+    // separate an OpenWhisk qualified name (e.g. "/whisk.system/samples/date")
+    // into namespace and name components
+    private class func parseQualifiedName(name qualifiedName : String) -> (namespace : String, name : String) {
+        let defaultNamespace = "_"
+        let delimiter = "/"
+
+        let segments :[String] = qualifiedName.components(separatedBy: delimiter)
+
+        if segments.count > 2 {
+            return (segments[1], Array(segments[2..<segments.count]).joined(separator: delimiter))
+        } else if segments.count == 2 {
+            // case "/action" or "package/action"
+            let name = qualifiedName.hasPrefix(delimiter) ? segments[1] : segments.joined(separator: delimiter)
+            return (defaultNamespace, name)
+        } else {
+            return (defaultNamespace, segments[0])
+        }
+    }
+
+}
diff --git a/settings.gradle b/core/swift54Action/build.gradle
similarity index 58%
copy from settings.gradle
copy to core/swift54Action/build.gradle
index ab6a605..b666103 100644
--- a/settings.gradle
+++ b/core/swift54Action/build.gradle
@@ -15,31 +15,5 @@
  * limitations under the License.
  */
 
-include 'tests'
-
-include 'core:swift42Action'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
-        version: '1.0.0-SNAPSHOT'
-]
-
-gradle.ext.scala = [
-    version: '2.12.7',
-    depVersion  : '2.12',
-    compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
-    version: '1.5.0',
-    config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
+ext.dockerImageName = 'action-swift-v5.4'
+apply from: '../../gradle/docker.gradle'
diff --git a/settings.gradle b/core/swift54Action/main.swift
similarity index 58%
copy from settings.gradle
copy to core/swift54Action/main.swift
index ab6a605..75071a3 100644
--- a/settings.gradle
+++ b/core/swift54Action/main.swift
@@ -15,31 +15,10 @@
  * limitations under the License.
  */
 
-include 'tests'
-
-include 'core:swift42Action'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
-        version: '1.0.0-SNAPSHOT'
-]
-
-gradle.ext.scala = [
-    version: '2.12.7',
-    depVersion  : '2.12',
-    compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
-    version: '1.5.0',
-    config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
+func main(args: [String:Any]) -> [String:Any] {
+    if let name = args["name"] as? String {
+        return [ "greeting" : "Hello \(name)!" ]
+    } else {
+        return [ "greeting" : "Hello stranger!" ]
+    }
+}
diff --git a/core/swift54Action/swiftbuild.py b/core/swift54Action/swiftbuild.py
new file mode 100755
index 0000000..9b40903
--- /dev/null
+++ b/core/swift54Action/swiftbuild.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+"""Swift Action Compiler
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""
+
+from __future__ import print_function
+import os
+import re
+import sys
+import codecs
+import subprocess
+from io import StringIO
+
+def eprint(*args, **kwargs):
+    print(*args, file=sys.stderr, **kwargs)
+
+def sources(launcher, source_dir, main):
+    actiondir = "%s/Sources" % source_dir
+    # copy the launcher fixing the main
+    dst = "%s/main.swift" % actiondir
+    with codecs.open(dst, 'a', 'utf-8') as d:
+        with codecs.open(launcher, 'r', 'utf-8') as e:
+            code = e.read()
+            code += "while let inputStr: String = readLine() {\n"
+            code += "  let json = inputStr.data(using: .utf8, allowLossyConversion: true)!\n"
+            code += "  let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any]\n"
+            code += "  for (key, value) in parsed {\n"
+            code += "    if key != \"value\" {\n"
+            code += "      setenv(\"__OW_\\(key.uppercased())\",value as! String,1)\n"
+            code += "    }\n"
+            code += "  }\n"
+            code += "  let jsonData = try JSONSerialization.data(withJSONObject: parsed[\"value\"] as Any, options: [])\n"
+            code += "  _run_main(mainFunction: %s, json: jsonData)\n" % main
+            code += "} \n"
+            d.write(code)
+
+def swift_build(dir, buildcmd):
+    # compile...
+    env = {
+      "PATH": os.environ["PATH"]
+    }
+    p = subprocess.Popen(buildcmd,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        cwd=dir,
+        env=env)
+    (o, e) = p.communicate()
+    # stdout/stderr may be either text or bytes, depending on Python
+    # version, so if bytes, decode to text. Note that in Python 2
+    # a string will match both types; so also skip decoding in that case
+    if isinstance(o, bytes) and not isinstance(o, str):
+        o = o.decode('utf-8')
+    if isinstance(e, bytes) and not isinstance(e, str):
+        e = e.decode('utf-8')
+    return p.returncode, o, e
+
+def build(source_dir, target_file, buildcmd):
+    r, o, e = swift_build(source_dir, buildcmd)
+    #if e: print(e)
+    #if o: print(o)
+    if r != 0:
+        print(e)
+        print(o)
+        print(r)
+        return
+
+    bin_file = "%s/.build/release/Action" % source_dir
+    os.rename(bin_file, target_file)
+    if not os.path.isfile(target_file):
+        print("failed %s -> %s" % (bin_file, target_file))
+        return
+
+
+def main(argv):
+    if len(argv) < 4:
+        print("usage: <main-function> <source-dir> <target-dir>")
+        sys.exit(1)
+
+    main = argv[1]
+    source_dir = os.path.abspath(argv[2])
+    target = os.path.abspath("%s/exec" % argv[3])
+    launch = os.path.abspath(argv[0]+".launcher.swift")
+
+    src = "%s/exec" % source_dir
+
+    #check if single source
+    if os.path.isfile(src):
+        actiondir = os.path.abspath("Sources")
+        if not os.path.isdir(actiondir):
+            os.makedirs(actiondir, mode=0o755)
+        dst = "%s/main.swift" % actiondir
+        os.rename(src, dst)
+        sources(launch, os.path.abspath("."), main)
+        build(os.path.abspath("."), target, ["./swiftbuildandlink.sh"])
+    else:
+        actiondir = "%s/Sources" % source_dir
+        if not os.path.isdir(actiondir):
+            os.makedirs(actiondir, mode=0o755)
+        os.rename(os.path.abspath("Sources/_Whisk.swift"),"%s/Sources/_Whisk.swift" % source_dir)
+        sources(launch, source_dir, main)
+        build(source_dir, target, ["swift", "build", "-c", "release"])
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/core/swift54Action/swiftbuild.py.launcher.swift b/core/swift54Action/swiftbuild.py.launcher.swift
new file mode 100644
index 0000000..3405777
--- /dev/null
+++ b/core/swift54Action/swiftbuild.py.launcher.swift
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Imports
+import Foundation
+#if os(Linux)
+    import Glibc
+#else
+    import Darwin
+#endif
+
+func _whisk_print_error(message: String, error: Error?){
+    var errStr =  "{\"error\":\"\(message)\"}\n"
+    if let error = error {
+        errStr = "{\"error\":\"\(message) \(error.localizedDescription)\"\n}"
+    }
+    _whisk_print_buffer(jsonString: errStr)
+}
+func _whisk_print_result(jsonData: Data){
+    let jsonString = String(data: jsonData, encoding: .utf8)!
+    _whisk_print_buffer(jsonString: jsonString)
+}
+func _whisk_print_buffer(jsonString: String){
+    var buf : [UInt8] = Array(jsonString.utf8)
+    buf.append(10)
+    fflush(stdout)
+    fflush(stderr)
+    write(3, buf, buf.count)
+}
+
+// snippet of code "injected" (wrapper code for invoking traditional main)
+func _run_main(mainFunction: ([String: Any]) -> [String: Any], json: Data) -> Void {
+    do {
+        let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any]
+        let result = mainFunction(parsed)
+        if JSONSerialization.isValidJSONObject(result) {
+            do {
+                let jsonData = try JSONSerialization.data(withJSONObject: result, options: [])
+                 _whisk_print_result(jsonData: jsonData)
+            } catch {
+                _whisk_print_error(message: "Failed to encode Dictionary type to JSON string:", error: error)
+            }
+        } else {
+            _whisk_print_error(message: "Error serializing JSON, data does not appear to be valid JSON", error: nil)
+        }
+    } catch {
+        _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+        return
+    }
+}
+
+// Codable main signature input Codable
+func _run_main<In: Decodable, Out: Encodable>(mainFunction: (In, @escaping (Out?, Error?) -> Void) -> Void, json: Data) {
+    do {
+        let input = try Whisk.jsonDecoder.decode(In.self, from: json)
+        let resultHandler = { (out: Out?, error: Error?) in
+            if let error = error {
+                _whisk_print_error(message: "Action handler callback returned an error:", error: error)
+                return
+            }
+            guard let out = out else {
+                _whisk_print_error(message: "Action handler callback did not return response or error.", error: nil)
+                return
+            }
+            do {
+                let jsonData = try Whisk.jsonEncoder.encode(out)
+                _whisk_print_result(jsonData: jsonData)
+            } catch let error as EncodingError {
+                _whisk_print_error(message: "JSONEncoder failed to encode Codable type to JSON string:", error: error)
+                return
+            } catch {
+                _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+                return
+            }
+        }
+        let _ = mainFunction(input, resultHandler)
+    } catch let error as DecodingError {
+        _whisk_print_error(message: "JSONDecoder failed to decode JSON string \(String(data: json, encoding: .utf8)!.replacingOccurrences(of: "\"", with: "\\\"")) to Codable type:", error: error)
+        return
+    } catch {
+        _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+        return
+    }
+}
+
+// Codable main signature no input
+func _run_main<Out: Encodable>(mainFunction: ( @escaping (Out?, Error?) -> Void) -> Void, json: Data) {
+    let resultHandler = { (out: Out?, error: Error?) in
+        if let error = error {
+            _whisk_print_error(message: "Action handler callback returned an error:", error: error)
+            return
+        }
+        guard let out = out else {
+            _whisk_print_error(message: "Action handler callback did not return response or error.", error: nil)
+            return
+        }
+        do {
+            let jsonData = try Whisk.jsonEncoder.encode(out)
+            _whisk_print_result(jsonData: jsonData)
+        } catch let error as EncodingError {
+            _whisk_print_error(message: "JSONEncoder failed to encode Codable type to JSON string:", error: error)
+            return
+        } catch {
+            _whisk_print_error(message: "Failed to execute action handler with error:", error: error)
+            return
+        }
+    }
+    let _ = mainFunction(resultHandler)
+}
+
+// snippets of code "injected", depending on the type of function the developer
+// wants to use traditional vs codable
+
+
+
+
+
+
+
diff --git a/core/swift54Action/swiftbuildandlink.sh b/core/swift54Action/swiftbuildandlink.sh
new file mode 100755
index 0000000..c35a1ba
--- /dev/null
+++ b/core/swift54Action/swiftbuildandlink.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+swift build -c release
diff --git a/settings.gradle b/settings.gradle
index ab6a605..7ae6f5e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,6 +23,9 @@ include 'core:swift51Action'
 
 include 'core:swift53Action'
 
+include 'core:swift54Action'
+
+
 rootProject.name = 'runtime-swift'
 
 gradle.ext.openwhisk = [
@@ -40,6 +43,6 @@ gradle.ext.scalafmt = [
     config: new File(rootProject.projectDir, '.scalafmt.conf')
 ]
 
-
 gradle.ext.akka = [version : '2.6.12']
 gradle.ext.akka_http = [version : '10.2.4']
+
diff --git a/tests/dat/build.sh b/tests/dat/build.sh
index 29315e0..3abba0c 100755
--- a/tests/dat/build.sh
+++ b/tests/dat/build.sh
@@ -32,3 +32,8 @@ set -e
 ../../tools/build/compile5.sh  action-swift-v5.3 HelloSwift5Codable swift5.3 "-v"
 ../../tools/build/compile5.sh  action-swift-v5.3 SwiftyRequest5 swift5.3 "-v"
 ../../tools/build/compile5.sh  action-swift-v5.3 SwiftyRequestCodable5 swift5.3 "-v"
+
+../../tools/build/compile5.sh  action-swift-v5.4 HelloSwift5 swift5.4 "-v"
+../../tools/build/compile5.sh  action-swift-v5.4 HelloSwift5Codable swift5.4 "-v"
+../../tools/build/compile5.sh  action-swift-v5.4 SwiftyRequest5 swift5.4 "-v"
+../../tools/build/compile5.sh  action-swift-v5.4 SwiftyRequestCodable5 swift5.4 "-v"
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index 3b9e7dc..8104212 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -49,6 +49,7 @@ TERM=dumb ./gradlew \
 :core:swift42Action:distDocker \
 :core:swift51Action:distDocker \
 :core:swift53Action:distDocker \
+:core:swift54Action:distDocker \
 -PdockerImagePrefix=${IMAGE_PREFIX}
 
 # Compile test files
diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh
index 2e46b8f..8a0ece3 100755
--- a/tools/travis/publish.sh
+++ b/tools/travis/publish.sh
@@ -36,6 +36,8 @@ elif [ ${RUNTIME_VERSION} == "5.1" ]; then
   RUNTIME="swift51Action"
 elif [ ${RUNTIME_VERSION} == "5.3" ]; then
   RUNTIME="swift53Action"
+elif [ ${RUNTIME_VERSION} == "5.4" ]; then
+  RUNTIME="swift54Action"
 fi
 
 if [[ ! -z ${DOCKER_USER} ]] && [[ ! -z ${DOCKER_PASSWORD} ]]; then