Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit 605581b

Browse files
author
James Thomas
committed
Adding support for pre-compiled Swift binary handlers.
1 parent f8a64be commit 605581b

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ In the `serverless.yaml` file, the `handler` property is used to denote the sour
264264
```yaml
265265
functions:
266266
my_function:
267-
handler: index.greeting
267+
handler: index.main
268268
runtime: swift
269269
```
270270

@@ -291,6 +291,42 @@ func main(args: [String:Any]) -> [String:Any] {
291291

292292
If you want to return an error message, return an object with an `error` property with the message.
293293

294+
## Writing Functions - Pre-Compiled Swift Binaries
295+
296+
OpenWhisk supports creating Swift actions from a pre-compiled binary. This reduces startup time for Swift actions by removing the need for a dynamic compilation step.
297+
298+
In the `serverless.yaml` file, the `handler` property can refer to the compiled binary file produced by the build.
299+
300+
```yaml
301+
functions:
302+
hello:
303+
handler: .build/release/Hello
304+
```
305+
306+
This configuration will generate the deployment package for that function containing only this binary file. It will not include other local files, e.g. Swift source files.
307+
308+
Pre-compiled Swift actions must be compatible with the platform runtime and architecture. There is an [open-source Swift package](https://packagecatalog.com/package/jthomas/OpenWhiskAction) (`OpenWhiskAction`) that handles wrapping functions within a shim to support runtime execution.
309+
310+
```
311+
import OpenWhiskAction
312+
313+
func hello(args: [String:Any]) -> [String:Any] {
314+
if let name = args["name"] as? String {
315+
return [ "greeting" : "Hello \(name)!" ]
316+
} else {
317+
return [ "greeting" : "Hello stranger!" ]
318+
}
319+
}
320+
321+
OpenWhiskAction(main: hello)
322+
```
323+
324+
Binaries produced by the Swift build process must be generated for the correct platform architecture. This Docker command will compile Swift sources files using the relevant Swift environment.
325+
326+
```
327+
docker run --rm -it -v $(pwd):/swift-package openwhisk/swift3action bash -e -c "cd /swift-package && swift build -v -c release"
328+
```
329+
294330
## Writing Functions - Binary
295331
296332
OpenWhisk supports executing a compiled binary for the function handler. Using a Python wrapper, the file will be invoked within the `openwhisk/dockerskeleton` Docker container.

compile/functions/runtimes/swift.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const BaseRuntime = require('./base')
4+
const JSZip = require("jszip")
45

56
class Swift extends BaseRuntime {
67
constructor (serverless) {
@@ -9,8 +10,27 @@ class Swift extends BaseRuntime {
910
this.extension = '.swift'
1011
}
1112

13+
convertHandlerToPath (functionHandler) {
14+
if (this.isBuildPath(functionHandler)) {
15+
return functionHandler
16+
}
17+
18+
return super.convertHandlerToPath(functionHandler)
19+
}
20+
21+
isBuildPath (path) {
22+
return path.startsWith('.build/')
23+
}
24+
25+
// Ensure zip package used to deploy action has the correct artifacts for the runtime.
26+
// Swift source actions must have the function code in `main.swift`.
27+
// Swift binary actions must have the binary as `./build/release/Action`.
1228
processActionPackage (handlerFile, zip) {
1329
return zip.file(handlerFile).async('nodebuffer').then(data => {
30+
if (this.isBuildPath(handlerFile)) {
31+
zip = new JSZip()
32+
return zip.file('.build/release/Action', data)
33+
}
1434
zip.remove(handlerFile)
1535
return zip.file('main.swift', data)
1636
})

compile/functions/runtimes/tests/swift.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ describe('Swift', () => {
8383
})
8484
});
8585

86+
describe('#convertHandlerToPath()', () => {
87+
it('should return file path for swift function handlers', () => {
88+
expect(node.convertHandlerToPath('file.func')).to.be.equal('file.swift')
89+
})
90+
91+
it('should return file path for build binaries', () => {
92+
expect(node.convertHandlerToPath('.build/release/Action')).to.be.equal('.build/release/Action')
93+
})
94+
})
95+
8696
describe('#generateActionPackage()', () => {
8797
it('should throw error for missing handler file', () => {
8898
expect(() => node.generateActionPackage({handler: 'does_not_exist.main'}))
@@ -134,5 +144,28 @@ describe('Swift', () => {
134144
})
135145
});
136146
})
147+
148+
it('should create zip file with binary action', () => {
149+
node.serverless.service.package = {artifact: '/path/to/zip_file.zip'};
150+
node.isValidFile = () => true
151+
const zip = new JSZip();
152+
const source = 'binary file contents'
153+
zip.file(".build/release/foo/bar", source);
154+
return zip.generateAsync({type:"nodebuffer"}).then(zipped => {
155+
sandbox.stub(fs, 'readFile', (path, cb) => {
156+
expect(path).to.equal('/path/to/zip_file.zip');
157+
cb(null, zipped);
158+
});
159+
return node.generateActionPackage({handler: '.build/release/foo/bar'}).then(data => {
160+
return JSZip.loadAsync(new Buffer(data, 'base64')).then(zip => {
161+
return zip.file(".build/release/Action").async("string").then(contents => {
162+
expect(contents).to.be.equal(source)
163+
})
164+
})
165+
})
166+
});
167+
})
168+
169+
137170
})
138171
});

0 commit comments

Comments
 (0)