Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8959b0cf2 | ||
|
|
16bb42eedd | ||
|
|
d185c5f0c2 | ||
|
|
6f870aea9c | ||
|
|
d244d0293d | ||
|
|
898c58a23e | ||
|
|
4fea5657c2 | ||
|
|
47f6693417 |
@@ -1,212 +1,756 @@
|
||||
> Write or rewrite a tutorial that covers building a project
|
||||
> that interfaces haskell backend code with Swift GUI code in an
|
||||
> XCode 8.2 project. Bonus points if the entire thing can be
|
||||
> built from the command line `stack build` rather than inside
|
||||
> XCode.
|
||||
|
||||
# Integrating Haskell with Swift Mac Apps
|
||||
|
||||
To start, let's create a new Xcode project:
|
||||
To start, create a new Cocoa Application Xcode project
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
with Swift as the default language.
|
||||
|
||||

|
||||
|
||||
Then `cd` into the directory with the `.xcodeproj` and create a
|
||||
new stack project:
|
||||
|
||||
$ cd SwiftHaskell
|
||||
$ stack new SwiftHaskellLibrary simple-library
|
||||
```sh
|
||||
$ cd SwiftHaskell
|
||||
$ stack new SwiftHaskellLibrary simple
|
||||
```
|
||||
|
||||
Let's move these files up to the top directory, so we can
|
||||
run both `stack` and `xcodebuild` from the same directory:
|
||||
Move these files up to the top directory, so we can run all of
|
||||
our commands from the same directory:
|
||||
|
||||
$ mv -vn SwiftHaskellLibrary/* .
|
||||
$ rmdir SwiftHaskellLibrary
|
||||
```sh
|
||||
$ mv -vn SwiftHaskellLibrary/* .
|
||||
$ rmdir SwiftHaskellLibrary
|
||||
```
|
||||
|
||||
## Exporting Haskell Functions to Swift
|
||||
In `SwiftHaskellLibrary.cabal`, rename the executable to
|
||||
match the Xcode app's name of SwiftHaskell:
|
||||
|
||||
Add a `foreign export` function to the Haskell library.
|
||||
```cabal
|
||||
executable SwiftHaskell
|
||||
```
|
||||
|
||||
TODO: Explain exporting using Haskell's C FFI
|
||||
To combine our Haskell library with our Swift UI, we'll build
|
||||
the Swift app as a framework and link to it from the Haskell
|
||||
executable. Xcode will then package both up into an app bundle.
|
||||
|
||||
module Lib where
|
||||
The reason for doing the linking in this direction is that
|
||||
building a self-contained dynamic library is currently simpler
|
||||
with Swift and Xcode than it is with Cabal.
|
||||
|
||||
import Foreign.C
|
||||
## Exporting Haskell Functions
|
||||
|
||||
foreign export ccall square :: CInt -> CInt
|
||||
Here's the trivial function `square` that we'll export as a
|
||||
simple first example:
|
||||
|
||||
square :: CInt -> CInt
|
||||
square x = x * x
|
||||
```haskell
|
||||
square x = x * x
|
||||
```
|
||||
|
||||
If we `stack build` now, in addition to building the library,
|
||||
GHC will generate a C header file for us to include. Because
|
||||
it's a build artifact, it's buried somewhat deep in the file
|
||||
hierarchy, but we can ask `stack` where it is:
|
||||
Haskell functions exported via the FFI can only contain
|
||||
certain types in their signatures that are compatible with C:
|
||||
primitive integers, floats and doubles, and pointer types.
|
||||
The full list is in [section 8.7 of the Haskell
|
||||
Report][haskell-report-8.7].
|
||||
|
||||
$ find "$(stack path --dist-dir)" -name Lib_stub.h
|
||||
.stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/Lib_stub.h
|
||||
Since we'll only be using `square` to demonstrate the FFI, let's
|
||||
assign it a FFI-compatible type directly. For more complex
|
||||
functions, wrap them in a new function and convert their inputs
|
||||
and outputs as needed.
|
||||
|
||||
The stub header includes `HsFFI.h` from GHC, so we'll also need
|
||||
to find the current compiler's version of that header.
|
||||
```haskell
|
||||
import Foreign.C
|
||||
|
||||
square :: CInt -> CInt
|
||||
square x = x * x
|
||||
```
|
||||
|
||||
To export `square`, add a `foreign export` definition with a
|
||||
calling convention of `ccall`:
|
||||
|
||||
```haskell
|
||||
foreign export ccall square :: CInt -> CInt
|
||||
```
|
||||
|
||||
For the full syntax of `foreign export`, see [section 8.3 of the
|
||||
Haskell Report][haskell-report-8.3].
|
||||
|
||||
[haskell-report-8.7]: https://www.haskell.org/onlinereport/haskell2010/haskellch8.html#x15-1700008.7
|
||||
[haskell-report-8.3]: https://www.haskell.org/onlinereport/haskell2010/haskellch8.html#x15-1530008.3
|
||||
|
||||
Together, `src/Main.hs` is
|
||||
|
||||
```haskell
|
||||
module Main where
|
||||
|
||||
import Foreign.C
|
||||
|
||||
foreign export ccall square :: CInt -> CInt
|
||||
|
||||
square :: CInt -> CInt
|
||||
square x = x * x
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "hello world"
|
||||
```
|
||||
|
||||
## Importing Haskell's Generated FFI Headers into Swift
|
||||
|
||||
If we now `stack build`, in addition to building the library,
|
||||
GHC will generate C header files for each module with foreign
|
||||
exports. Because these are build artifacts, they're buried
|
||||
somewhat deep in the file hierarchy, but we can ask `stack`
|
||||
and `find` where they are:
|
||||
|
||||
$ find "$(stack path --dist-dir)" -name Main_stub.h
|
||||
.stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/SwiftHaskellLibrary/SwiftHaskellLibrary-tmp/Main_stub.h
|
||||
|
||||
These stub headers `#include "HsFFI.h"` from GHC, so we'll also
|
||||
need to find the current compiler's version of that header.
|
||||
|
||||
$ find "$(stack path --compiler-bin)/.." -name HsFFI.h
|
||||
/Users/nanotech/.stack/programs/x86_64-osx/ghc-8.0.1/bin/../lib/ghc-8.0.1/include/HsFFI.h
|
||||
|
||||
Let's make a script to symlink to these from a more convenient
|
||||
location so we don't need to change the Xcode project when the
|
||||
compiler or Cabal version changes:
|
||||
Since we'll be importing these headers into a Swift framework,
|
||||
we won't be able to use `#include` as we would in C. Instead,
|
||||
Swift uses [Clang's module format][clang-modules]. (Swift
|
||||
applications can use [bridging headers][swift-bridging-headers],
|
||||
but frameworks [must use modules][so-non-modular-header].) A
|
||||
`module.modulemap` file to import `Main_stub.h` looks like
|
||||
|
||||
#!/bin/sh
|
||||
set -eux
|
||||
DIST_DIR="$(stack path --dist-dir)"
|
||||
ln -sf ../"$DIST_DIR"/build/Lib_stub.h build/
|
||||
ln -sf "$GHC_LIB_DIR"/include build/
|
||||
module SwiftHaskell {
|
||||
header "Main_stub.h"
|
||||
export *
|
||||
}
|
||||
|
||||
Run it, then add `$(PROJECT_DIR)/build` and
|
||||
`$(PROJECT_DIR)/build/include` to the target's *User Header
|
||||
Search Paths* in Xcode:
|
||||
[swift-bridging-headers]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID156
|
||||
[so-non-modular-header]: https://stackoverflow.com/questions/24103169/swift-compiler-error-non-modular-header-inside-framework-module/37072619#37072619
|
||||
[clang-modules]: http://clang.llvm.org/docs/Modules.html
|
||||
|
||||

|
||||
As the paths to these headers vary, let's use a script to
|
||||
automatically copy them out and build a module map. We'll also
|
||||
create a symlink to the built executable's location for later.
|
||||
|
||||
Now that we have the exported definitions in a header file, we
|
||||
need to import them into Swift. Create a new header file in
|
||||
Xcode named `SwiftHaskell-Bridging-Header.h` and save it in the
|
||||
same directory as `AppDelegate.swift`. Then set it as the
|
||||
*Objective-C Bridging Header* in Xcode:
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||

|
||||
EXECUTABLE_NAME=SwiftHaskell
|
||||
DIST_DIR="$(stack path --dist-dir)"
|
||||
GHC_VERSION="$(stack exec -- ghc --numeric-version)"
|
||||
GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION"
|
||||
STUB_BUILD_DIR="${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}-tmp"
|
||||
STUB_MODULE_DIR="${EXECUTABLE_NAME}/include"
|
||||
STUB_MODULE_MAP="${STUB_MODULE_DIR}/module.modulemap"
|
||||
|
||||
And include the Haskell stub header:
|
||||
# Create a module map from the generated Haskell
|
||||
# FFI export headers for importing into Swift.
|
||||
mkdir -p "${STUB_MODULE_DIR}"
|
||||
NL="
|
||||
"
|
||||
module_map="module ${EXECUTABLE_NAME} {${NL}"
|
||||
for h in $(find "${STUB_BUILD_DIR}" -name '*.h'); do
|
||||
h_filename="${h/$STUB_BUILD_DIR\//}"
|
||||
cp "$h" "${STUB_MODULE_DIR}/"
|
||||
module_map="${module_map} header \"${h_filename}\"${NL}"
|
||||
done
|
||||
module_map="${module_map} export *${NL}"
|
||||
module_map="${module_map}}"
|
||||
echo "${module_map}" > "${STUB_MODULE_MAP}"
|
||||
|
||||
#ifndef SwiftHaskell_Bridging_Header_h
|
||||
#define SwiftHaskell_Bridging_Header_h
|
||||
# Symlink to the current GHC's header directory from a more
|
||||
# convenient place for Xcode to find.
|
||||
mkdir -p build/ghc
|
||||
ln -sf "${GHC_LIB_DIR}/include" build/ghc/
|
||||
|
||||
#include "Lib_stub.h"
|
||||
# Symlink to the Haskell executable for Xcode.
|
||||
ln -sf "../${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}" build/
|
||||
```
|
||||
|
||||
#endif /* SwiftHaskell_Bridging_Header_h */
|
||||
Save the script as `link-deps.sh`, run `stack build`, and then
|
||||
run `bash link-deps.sh` to prepare for the next section.
|
||||
|
||||
## Linking
|
||||
## Converting the Swift App to a Framework
|
||||
|
||||
To link our Haskell library into our Swift app, we'll build it
|
||||
as a dynamic library and add it to our Xcode project. Building
|
||||
a static library is also possible, but currently requires
|
||||
[building your own GHC][pic-ghc].
|
||||
Create a new Cocoa Framework target in the Xcode project
|
||||
named SwiftAppLibrary, then change the Target Membership of
|
||||
`AppDelegate.swift` and `MainMenu.xib` to only SwiftAppLibrary
|
||||
in Xcode's File Inspector in the right sidebar:
|
||||
|
||||
[pic-ghc]: https://github.com/lyokha/nginx-haskell-module#static-linkage-against-basic-haskell-libraries
|
||||

|
||||
|
||||
Add a `ghc-options` line to the `library` section in the
|
||||
`.cabal` file:
|
||||
In the new framework's build settings, set **Always Embed Swift
|
||||
Standard Libraries** to **Yes**.
|
||||
|
||||
library
|
||||
# ... other options
|
||||
ghc-options: -threaded -dynamic -shared -fPIC -o build/libswifthaskell.dylib
|
||||
Drag the `SwiftHaskell` executable we built previously with
|
||||
Stack into Xcode from the `build/` directory that we symlinked
|
||||
it into, but do not add it to any targets when prompted:
|
||||
|
||||

|
||||
|
||||
In the SwiftHaskell app target's Build Phases, remove the
|
||||
**Compile Sources** and **Link Binary With Libraries**
|
||||
phases, and add a new **Copy Files** phase that copies the
|
||||
`SwiftHaskell` executable into the app bundle's Executables
|
||||
directory:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Finally, in the SwiftAppLibrary framework target's Build Phases,
|
||||
add a new **Run Script** phase to create a symlink to the built
|
||||
framework for us to link to from Cabal:
|
||||
|
||||
```sh
|
||||
set -u
|
||||
ln -sf "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "${PROJECT_DIR}/build/"
|
||||
```
|
||||
|
||||
## Linking to the Framework
|
||||
|
||||
Add these options to the executable's section in the `.cabal`
|
||||
file:
|
||||
|
||||
```cabal
|
||||
executable SwiftHaskell
|
||||
ghc-options: -threaded -framework-path build
|
||||
ld-options: -rpath @executable_path/../Frameworks
|
||||
frameworks: SwiftAppLibrary
|
||||
```
|
||||
|
||||
- `-threaded` enables the multithreaded GHC runtime, which is
|
||||
usually what you want.
|
||||
- `-dynamic` tells GHC to link to the dynamic versions of
|
||||
Haskell libraries. This is required when using `-shared`.
|
||||
- `-shared` builds a shared library.
|
||||
- `-fPIC` enables position-independent code, which is needed for
|
||||
shared libraries.
|
||||
- `-framework-path build` tells GHC to look for frameworks where we
|
||||
symlinked our framework to.
|
||||
- `-rpath @executable_path/../Frameworks` embeds into the
|
||||
executable where the dynamic linker should look for shared
|
||||
libraries.
|
||||
|
||||
Run `stack build`, then drag `build/libswifthaskell.dylib` into
|
||||
the Xcode project and add it to the SwiftHaskell target.
|
||||
## Starting Cocoa
|
||||
|
||||
We'll also need to link to the RTS so we can initialize it
|
||||
from Swift, and copy all shared library dependencies into the
|
||||
app bundle so it's self-contained. `otool -L` will show what
|
||||
libraries `libswifthaskell.dylib` depends on:
|
||||
Because Haskell has control over the program's entry point
|
||||
(`main`), we'll need to have it call out to Cocoa to start its
|
||||
main thread. In `SwiftAppLibrary.h`, declare a new function
|
||||
named `runNSApplication` and mark it as `FOUNDATION_EXPORT` to
|
||||
indicate that it should be exported from the framework:
|
||||
|
||||
$ otool -L build/libswifthaskell.dylib
|
||||
build/libswifthaskell.dylib:
|
||||
@rpath/libswifthaskell.dylib (compatibility version 0.0.0, current version 0.0.0)
|
||||
@rpath/libHSbase-4.9.0.0-ghc8.0.1.dylib (compatibility version 0.0.0, current version 0.0.0)
|
||||
@rpath/libHSinteger-gmp-1.0.0.1-ghc8.0.1.dylib (compatibility version 0.0.0, current version 0.0.0)
|
||||
@rpath/libHSghc-prim-0.5.0.0-ghc8.0.1.dylib (compatibility version 0.0.0, current version 0.0.0)
|
||||
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
|
||||
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
|
||||
```c
|
||||
FOUNDATION_EXPORT void runNSApplication(void);
|
||||
```
|
||||
|
||||
Let's update our previous script to locate them and symlink them
|
||||
into the build directory:
|
||||
Implement the function by adding a new Objective-C `.m` file to
|
||||
the framework target containing
|
||||
|
||||
#!/bin/sh
|
||||
set -eux
|
||||
```objective-c
|
||||
#import "SwiftAppLibrary.h"
|
||||
|
||||
DIST_DIR="$(stack path --dist-dir)"
|
||||
GHC_VERSION="$(stack exec -- ghc --numeric-version)"
|
||||
GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION"
|
||||
@interface AClassInThisFramework : NSObject @end
|
||||
@implementation AClassInThisFramework @end
|
||||
|
||||
ln -sf ../"$DIST_DIR"/build/Lib_stub.h build/
|
||||
ln -sf "$GHC_LIB_DIR"/include build/
|
||||
ln -sf "$GHC_LIB_DIR"/rts/libHSrts_thr-ghc"$GHC_VERSION".dylib build/
|
||||
# FIXME: Link the other Haskell libraries
|
||||
ln -sf "$GHC_LIB_DIR"/rts/libffi.dylib build/
|
||||
void runNSApplication(void) {
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[AClassInThisFramework class]];
|
||||
NSArray *topObjects;
|
||||
[[[NSNib alloc] initWithNibNamed:@"MainMenu" bundle:bundle]
|
||||
instantiateWithOwner:app topLevelObjects:&topObjects];
|
||||
[app run];
|
||||
}
|
||||
```
|
||||
|
||||
This is the `symlink_deps.sh` script in the repository.
|
||||
This *is* possible to write in Swift, however as of Swift 3.0.2,
|
||||
the annotation to export unmangled C symbols (`@_cdecl`) is not
|
||||
documented as stable. Additionally, whole module optimization
|
||||
will assume that `@_cdecl` symbols are unused and remove them.
|
||||
|
||||
TODO: Find shared library dependencies automatically, like
|
||||
`cabal-macosx`.
|
||||
In `Main.hs`, import the foreign function and call it from
|
||||
the end of `main`:
|
||||
|
||||
Drag these libraries into Xcode too,
|
||||
```haskell
|
||||
module Main where
|
||||
|
||||

|
||||
import Foreign.C
|
||||
|
||||
Then go to the target's Build Phases panel and add a new phase
|
||||
to copy the libraries into the app bundle:
|
||||
foreign export ccall square :: CInt -> CInt
|
||||
|
||||

|
||||
square :: CInt -> CInt
|
||||
square x = x * x
|
||||
|
||||

|
||||
foreign import ccall "runNSApplication" runNSApplication :: IO ()
|
||||
|
||||
## Runtime Setup
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "hello world"
|
||||
runNSApplication
|
||||
```
|
||||
|
||||
Before using functions from Haskell, we'll need to start its
|
||||
runtime. `hs_init` takes pointers to C's argc and argv, however
|
||||
since we aren't using command line arguments, we can just pass a
|
||||
constant array.
|
||||
`runNSApplication` will not return, being busy with Cocoa's
|
||||
main run loop. Use `Control.Concurrent.forkIO` before calling
|
||||
`runNSApplication` to run other tasks as needed.
|
||||
|
||||
Add this to your app delegate's `applicationDidFinishLaunching`
|
||||
method:
|
||||
Run `stack build`, and build and run the `SwiftHaskell` app
|
||||
target in Xcode to launch the app and see the default window
|
||||
from `MainMenu.xib`:
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
var argv0 = Array("SwiftHaskell".utf8CString)
|
||||
argv0.withUnsafeMutableBufferPointer { argv0bp in
|
||||
var argv = [argv0bp.baseAddress];
|
||||
var argc = CInt(argv.count)
|
||||
argv.withUnsafeMutableBufferPointer { argvbp in
|
||||
var argvp = argvbp.baseAddress
|
||||
hs_init(&argc, &argvp)
|
||||
}
|
||||
}
|
||||
}
|
||||

|
||||
|
||||
And the corresponding `hs_exit` to `applicationWillTerminate`:
|
||||
## Linking to the Executable
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
hs_exit()
|
||||
}
|
||||
Add `$(PROJECT_DIR)/SwiftHaskell/include` to the framework
|
||||
target's **Swift Compiler - Search Paths, Import Paths** setting
|
||||
in Xcode,
|
||||
|
||||
TODO: Explain swift pointer bits.
|
||||

|
||||
|
||||
and `$(PROJECT_DIR)/build/ghc/include` to the framework's **User
|
||||
Header Search Paths** setting:
|
||||
|
||||

|
||||
|
||||
In order for the framework to be able to link to symbols in the
|
||||
Haskell executable, we need to tell the linker to leave symbols
|
||||
undefined and have them be resolved at runtime.
|
||||
|
||||
Add `-undefined dynamic_lookup` to the framework's **Other
|
||||
Linker Flags** setting.
|
||||
|
||||
Be aware that this means that link errors will occur at runtime
|
||||
instead of at link time. Also note that the framework linking
|
||||
to symbols in the executable (and depending on the generated
|
||||
headers), and the executable linking to the framework, creates
|
||||
a circular dependency. When initially building the project, you
|
||||
will need to build the components in this order:
|
||||
|
||||
- `stack build` to generate the Haskell FFI export headers.
|
||||
Linking will fail, as the Swift framework is not built yet.
|
||||
- Build the Swift framework.
|
||||
- `stack build`
|
||||
- Build the app bundle.
|
||||
|
||||
The first step can be skipped subsequently by committing the
|
||||
generated headers to source control.
|
||||
|
||||
To help automate this, add a new **Run Script** build phase to
|
||||
the beginning of the framework's build phases with the contents
|
||||
|
||||
```sh
|
||||
stack build
|
||||
bash link-deps.sh
|
||||
```
|
||||
|
||||
Add the Haskell sources as input files:
|
||||
|
||||
```
|
||||
$(PROJECT_DIR)/src/Main.hs
|
||||
$(PROJECT_DIR)/SwiftHaskellLibrary.cabal
|
||||
$(PROJECT_DIR)/stack.yaml
|
||||
```
|
||||
|
||||
And the executable as an output file:
|
||||
|
||||
```
|
||||
$(PROJECT_DIR)/build/SwiftHaskell
|
||||
```
|
||||
|
||||
Or, if you prefer building primarily with `stack build`, set the
|
||||
`build-type` in your `.cabal` to `Custom` and add a `postBuild`
|
||||
hook to `Setup.hs`:
|
||||
|
||||
```haskell
|
||||
import Distribution.Simple
|
||||
import System.Process
|
||||
|
||||
main = defaultMainWithHooks $ simpleUserHooks
|
||||
{ postBuild = \args buildFlags pkgDesc localBuildInfo -> do
|
||||
callProcess "bash" ["link-deps.sh"]
|
||||
callProcess "xcodebuild" ["-target", "SwiftHaskell"]
|
||||
}
|
||||
```
|
||||
|
||||
## Calling Haskell from Swift
|
||||
|
||||
We're now ready to use exported Haskell functions from Swift.
|
||||
Import `SwiftHaskell` at the top of `AppDelegate.swift`
|
||||
|
||||
```swift
|
||||
import SwiftHaskell
|
||||
```
|
||||
|
||||
Add a new label to the window in `MainMenu.xib` for us to write
|
||||
the result of our Haskell function `square` into, and add it as
|
||||
an `@IBOutlet` to the `AppDelegate`:
|
||||
|
||||
@IBOutlet weak var label: NSTextField!
|
||||
```swift
|
||||
@IBOutlet weak var label: NSTextField!
|
||||
```
|
||||
|
||||
We already have our Haskell library's header imported, so we
|
||||
can just call the exported `square` function. Add this to
|
||||
`applicationDidFinishLaunching`:
|
||||
|
||||
label.stringValue = "\(square(5))"
|
||||
```swift
|
||||
label.stringValue = "\(square(5))"
|
||||
```
|
||||
|
||||
And run the app:
|
||||
The final contents of `AppDelegate.swift` are:
|
||||
|
||||
```swift
|
||||
import Cocoa
|
||||
import SwiftHaskell
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
@IBOutlet weak var label: NSTextField!
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
label.stringValue = "\(square(5))"
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("swiftAppMain")
|
||||
func swiftAppMain() {
|
||||
let app = NSApplication.shared()
|
||||
var topObjects: NSArray = []
|
||||
NSNib.init(nibNamed: "MainMenu", bundle: Bundle(for: AppDelegate.self))!
|
||||
.instantiate(withOwner: app, topLevelObjects: &topObjects)
|
||||
app.run()
|
||||
}
|
||||
```
|
||||
|
||||
Running the app,
|
||||
|
||||

|
||||
|
||||
## Calling Swift from Haskell
|
||||
## Passing Complex Data Types
|
||||
|
||||
TODO: FunPtr callbacks
|
||||
### Bytes
|
||||
|
||||
#### `[UInt8]` to `ByteString`
|
||||
|
||||
Call `withUnsafeBufferPointer` on a Swift `Array` to get
|
||||
an `UnsafeBufferPointer`, and then read its `.baseAddress`
|
||||
property to get an `UnsafePointer` pass into the exported
|
||||
Haskell function. The corresponding mutable variants are
|
||||
`withUnsafeMutableBufferPointer`, `UnsafeMutableBufferPointer`,
|
||||
and `UnsafeMutablePointer`.
|
||||
|
||||
The generated Haskell headers use a single pointer type for all
|
||||
pointers, `HsPtr` (`void *`), which is mutable (not `const`). If
|
||||
you know that a function does not mutate through a pointer, you
|
||||
can use the `HsPtr(mutating:)` constructor to cast a non-mutable
|
||||
pointer to a mutable pointer.
|
||||
|
||||
```swift
|
||||
bytes.withUnsafeBufferPointer { bytesBufPtr in
|
||||
someHaskellFunction(HsPtr(mutating: bytesBufPtr.baseAddress), bytesBufPtr.count)
|
||||
}
|
||||
```
|
||||
|
||||
If the function mutates the pointer's data, you must use
|
||||
`withUnsafeMutableBytes`:
|
||||
|
||||
```swift
|
||||
bytes.withUnsafeMutableBufferPointer { bytesBufPtr in
|
||||
someHaskellFunction(bytesBufPtr.baseAddress, bytesBufPtr.count)
|
||||
}
|
||||
```
|
||||
|
||||
To bring an array of bytes into a Haskell `ByteString`, use
|
||||
`Data.ByteString.packCStringLen`:
|
||||
|
||||
```haskell
|
||||
type CString = Ptr CChar
|
||||
packCStringLen :: (CString, Int) -> IO ByteString
|
||||
```
|
||||
|
||||
For example,
|
||||
|
||||
```haskell
|
||||
import Foreign.C
|
||||
import Foreign.Ptr
|
||||
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Word
|
||||
|
||||
foreign export ccall countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize
|
||||
|
||||
countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize
|
||||
countBytes needle haystack haystackLen = do
|
||||
s <- B.packCStringLen (haystack, fromIntegral haystackLen)
|
||||
pure (B.foldl (\count b -> count + if b == needle then 1 else 0) 0 s)
|
||||
```
|
||||
|
||||
With a Swift wrapping function of
|
||||
|
||||
```swift
|
||||
func count(byte: UInt8, in bytes: [UInt8]) -> Int {
|
||||
var r = 0
|
||||
bytes.withUnsafeBytes { bytesPtr in
|
||||
r = Int(SwiftHaskell.countBytes(byte, HsPtr(mutating: bytesPtr.baseAddress)))
|
||||
}
|
||||
return r
|
||||
}
|
||||
```
|
||||
|
||||
#### `ByteString` to `[UInt8]`
|
||||
|
||||
To pass a `ByteString` to an exported Swift function that
|
||||
accepts a pointer and a length, use `useAsCStringLen`:
|
||||
|
||||
```haskell
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString as B
|
||||
|
||||
foreign import ccall "someSwiftFunction" someSwiftFunction :: Ptr CChar -> CSize -> IO ()
|
||||
|
||||
passByteString :: ByteString -> IO ()
|
||||
passByteString s =
|
||||
B.useAsCStringLen s $ \(p, n) ->
|
||||
someSwiftFunction p (fromIntegral n)
|
||||
```
|
||||
|
||||
To return the contents of a `ByteString`, call `mallocArray` to
|
||||
allocate a new array with C's `malloc` allocator and copy the
|
||||
`ByteString` data into it. The Swift caller is then responsible
|
||||
for calling `free` on the pointer. Use `Foreign.Storable.poke`
|
||||
to also return the size by writing into a passed pointer.
|
||||
|
||||
```haskell
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Unsafe as BU
|
||||
import Foreign.Storable (poke)
|
||||
|
||||
mallocCopyByteString :: ByteString -> IO (Ptr CChar, Int)
|
||||
mallocCopyByteString s =
|
||||
BU.unsafeUseAsCStringLen s $ \(p, n) -> do
|
||||
a <- mallocArray n
|
||||
copyArray a p n
|
||||
pure (a, n)
|
||||
|
||||
foreign export ccall getSequence :: Ptr CSize -> IO (Ptr CChar)
|
||||
|
||||
getSequence :: Ptr CSize -> IO (Ptr CChar)
|
||||
getSequence sizePtr = do
|
||||
(p, n) <- mallocCopyByteString (B.pack [1..10])
|
||||
poke sizePtr (fromIntegral n)
|
||||
pure p
|
||||
```
|
||||
|
||||
The imported `getSequence` function returns a
|
||||
`UnsafeMutableRawPointer` in Swift. To copy the elements into
|
||||
a Swift array, first assign a type to the memory using the
|
||||
`.assumingMemoryBound(to:)` method. Then wrap the pointer
|
||||
and length in an `UnsafeBufferPointer` and pass it to the
|
||||
array constructor, which copies the elements into a new array
|
||||
using the `Collection` protocol that `UnsafeBufferPointer`
|
||||
implements.
|
||||
|
||||
```swift
|
||||
func getSequence() -> [UInt8] {
|
||||
var n = 0
|
||||
let p = SwiftHaskell.getSequence(&n).assumingMemoryBound(to: UInt8.self)
|
||||
let a = [UInt8](UnsafeBufferPointer(start: p, count: n))
|
||||
free(p)
|
||||
return a
|
||||
}
|
||||
```
|
||||
|
||||
### Functions and Closures
|
||||
|
||||
#### Passing Swift Functions to Haskell
|
||||
|
||||
C function pointers have a type constructor of `FunPtr` in
|
||||
Haskell. For example, `FunPtr (CInt -> CSize -> IO ())`
|
||||
corresponds to `void (*)(int, size_t)`.
|
||||
|
||||
To convert `FunPtr`s into callable Haskell functions, use a
|
||||
`foreign import ccall "dynamic"` declaration to ask the compiler
|
||||
to generate a conversion function for that function type:
|
||||
|
||||
```haskell
|
||||
foreign export ccall callbackExample :: FunPtr (CInt -> IO ()) -> IO ()
|
||||
foreign import ccall "dynamic" unwrapCallback :: FunPtr (CInt -> IO ()) -> (CInt -> IO ())
|
||||
|
||||
callbackExample :: FunPtr (CInt -> IO ()) -> IO ()
|
||||
callbackExample f = (unwrapCallback f) 3
|
||||
```
|
||||
|
||||
If there is no context that needs to be captured, Swift
|
||||
functions can be passed in almost directly. However, like
|
||||
`HsPtr`, the generated headers only use a single function
|
||||
pointer type, `HsFunPtr` (`void (*)(void)`), so a little casting
|
||||
is usually necessary:
|
||||
|
||||
```swift
|
||||
func callbackExample(f: (@convention(c) (CInt) -> Void)) {
|
||||
let hsf: HsFunPtr = unsafeBitCast(f, to: HsFunPtr.self)
|
||||
SwiftHaskell.callbackExample(hsf)
|
||||
}
|
||||
```
|
||||
|
||||
To pass Swift closures with context, we can use the traditional
|
||||
`void *` context pointer solution. Passing context however
|
||||
means that we need to keep it alive while the callback is held,
|
||||
and release it when we're done with it. For that, we can use
|
||||
`Foreign.ForeignPtr`.
|
||||
|
||||
We'll wrap the context with
|
||||
|
||||
```haskell
|
||||
type FinalizerPtr a = FunPtr (Ptr a -> IO ())
|
||||
newForeignPtr :: FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
|
||||
```
|
||||
|
||||
and then apply it to the function with
|
||||
|
||||
```haskell
|
||||
withForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b
|
||||
```
|
||||
|
||||
Together we have:
|
||||
|
||||
```haskell
|
||||
import Control.Concurrent
|
||||
import Foreign.C
|
||||
import Foreign.ForeignPtr
|
||||
import Foreign.Ptr
|
||||
|
||||
foreign export ccall contextCallbackExample
|
||||
:: Ptr ()
|
||||
-> FunPtr (Ptr () -> IO ())
|
||||
-> FunPtr (Ptr () -> CInt -> IO ())
|
||||
-> IO ()
|
||||
foreign import ccall "dynamic" unwrapContextCallback
|
||||
:: FunPtr (Ptr () -> CInt -> IO ())
|
||||
-> (Ptr () -> CInt -> IO ())
|
||||
|
||||
contextCallbackExample
|
||||
:: Ptr () -- ^ Context pointer
|
||||
-> FunPtr (Ptr () -> IO ()) -- ^ Context release function
|
||||
-> FunPtr (Ptr () -> CInt -> IO ()) -- ^ Callback function
|
||||
-> IO ()
|
||||
contextCallbackExample ctxp releaseCtx callbackPtr = do
|
||||
ctxfp <- newForeignPtr releaseCtx ctxp
|
||||
let callback :: CInt -> IO ()
|
||||
callback result = withForeignPtr ctxfp $ \ctxp' ->
|
||||
(unwrapContextCallback callbackPtr) ctxp' result
|
||||
_ <- forkIO $ do
|
||||
let result = 3 -- perform your complex computation here
|
||||
callback result
|
||||
pure ()
|
||||
```
|
||||
|
||||
The context pointer that we pass from the Swift side will be an
|
||||
object containing the closure itself. The function passed as
|
||||
the function pointer will merely cast the object to the known
|
||||
closure type and call it.
|
||||
|
||||
To convert our Swift closure into a raw pointer, we'll use
|
||||
Swift's `Unmanaged` wrapper type. These are the methods we'll
|
||||
use from it:
|
||||
|
||||
```swift
|
||||
public struct Unmanaged<Instance : AnyObject> {
|
||||
public static func passRetained(_ value: Instance) -> Unmanaged<Instance>
|
||||
public func toOpaque() -> UnsafeMutableRawPointer
|
||||
public static func fromOpaque(_ value: UnsafeRawPointer) -> Unmanaged<Instance>
|
||||
public func takeUnretainedValue() -> Instance
|
||||
public func takeRetainedValue() -> Instance
|
||||
}
|
||||
```
|
||||
|
||||
Since Swift functions do not implement the `AnyObject` protocol
|
||||
(they are not class types), we'll need to wrap them in a object
|
||||
first.
|
||||
|
||||
Additionally, referring directly to a Swift function name will
|
||||
give a Swift function type, which is not bit-compatible with a C
|
||||
function type. Before casting to `HsFunPtr`, we'll need to use a
|
||||
safe `as` cast to a `@convention(c)` type.
|
||||
|
||||
```swift
|
||||
func contextCallbackExample(f: ((CInt) -> Void)) {
|
||||
class Wrap<T> {
|
||||
var inner: T
|
||||
|
||||
init(_ inner: T) {
|
||||
self.inner = inner
|
||||
}
|
||||
}
|
||||
func release(context: HsPtr) {
|
||||
let _: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeRetainedValue()
|
||||
}
|
||||
func call(context: HsPtr, value: CInt) {
|
||||
let wf: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeUnretainedValue()
|
||||
let f = wf.inner
|
||||
f(value)
|
||||
}
|
||||
let release_hs = unsafeBitCast(
|
||||
release as @convention(c) (HsPtr) -> Void, to: HsFunPtr.self)
|
||||
let call_hs = unsafeBitCast(
|
||||
call as @convention(c) (HsPtr, CInt) -> Void, to: HsFunPtr.self)
|
||||
let ctx = Unmanaged.passRetained(Wrap(f)).toOpaque()
|
||||
SwiftHaskell.contextCallbackExample(ctx, release_hs, call_hs)
|
||||
}
|
||||
```
|
||||
|
||||
#### Passing Haskell Functions to Swift
|
||||
|
||||
In addition to the static `foreign export`, we can export
|
||||
dynamically created Haskell functions with `foreign export
|
||||
"wrapper"`. Unlike when passing Swift closures, a separate
|
||||
context pointer is not needed as the Haskell runtime supplies a
|
||||
distinct function pointer address for each wrapped function.
|
||||
|
||||
```haskell
|
||||
import Foreign.C
|
||||
import Foreign.Ptr
|
||||
|
||||
foreign export ccall makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt))
|
||||
foreign import ccall "wrapper" wrapMultiplier
|
||||
:: (CInt -> CInt)
|
||||
-> IO (FunPtr (CInt -> CInt))
|
||||
|
||||
makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt))
|
||||
makeMultiplier x = wrapMultiplier (x *)
|
||||
```
|
||||
|
||||
To free the `FunPtr`, export `Foreign.Ptr.freeHaskellFunPtr` and
|
||||
call it from Swift when you're done with the function.
|
||||
|
||||
```haskell
|
||||
foreign export ccall freeMultiplier :: FunPtr (CInt -> CInt) -> IO ()
|
||||
|
||||
freeMultiplier :: FunPtr (CInt -> CInt) -> IO ()
|
||||
freeMultiplier = freeHaskellFunPtr
|
||||
```
|
||||
|
||||
Wrap the Haskell function in a Swift class to manage its
|
||||
lifetime:
|
||||
|
||||
```swift
|
||||
class Multiplier {
|
||||
let funPtr: HsFunPtr
|
||||
|
||||
init(_ x: CInt) {
|
||||
self.funPtr = SwiftHaskell.makeMultiplier(x)
|
||||
}
|
||||
|
||||
func multiply(_ y: CInt) -> CInt {
|
||||
typealias F = @convention(c) (CInt) -> CInt
|
||||
let f = unsafeBitCast(self.funPtr, to: F.self)
|
||||
return f(y)
|
||||
}
|
||||
|
||||
deinit {
|
||||
SwiftHaskell.freeMultiplier(self.funPtr)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module SwHaLib {
|
||||
export *
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// SwiftAppLibrary
|
||||
//
|
||||
// Created by NanoTech on 2017-02-09.
|
||||
// Copyright © 2017 nanotech. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftHaskell
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
@IBOutlet weak var label: NSTextField!
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
let bytes: [UInt8] = [1, 2, 3, 2, 1, 2]
|
||||
callbackExample { n in
|
||||
print(n)
|
||||
}
|
||||
var callbackResult: CInt? = nil
|
||||
contextCallbackExample { n in
|
||||
callbackResult = n
|
||||
}
|
||||
let m = Multiplier(5)
|
||||
label.stringValue = [
|
||||
"5^2 = \(square(5))",
|
||||
"2 occurs \(count(byte: 2, in: bytes)) times in \(bytes)",
|
||||
"getSequence returned \(getSequence())",
|
||||
"Context callback returned \(callbackResult)",
|
||||
"5 * 3 = \(m.multiply(3))",
|
||||
].joined(separator: "\n")
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Swift to Haskell ByteString Example
|
||||
|
||||
func count(byte: UInt8, in bytes: [UInt8]) -> Int {
|
||||
var r = 0
|
||||
bytes.withUnsafeBufferPointer { bytesBufPtr in
|
||||
r = Int(SwiftHaskell.countBytes(byte,
|
||||
HsPtr(mutating: bytesBufPtr.baseAddress),
|
||||
HsWord64(bytesBufPtr.count)))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Haskell to Swift ByteString Example
|
||||
|
||||
func getSequence() -> [UInt8] {
|
||||
var n = 0
|
||||
let p = SwiftHaskell.getSequence(&n).assumingMemoryBound(to: UInt8.self)
|
||||
let a = [UInt8](UnsafeBufferPointer(start: p, count: n))
|
||||
free(p)
|
||||
return a
|
||||
}
|
||||
|
||||
// Swift to Haskell Callback Example
|
||||
|
||||
func callbackExample(f: (@convention(c) (CInt) -> Void)) {
|
||||
let hsf = unsafeBitCast(f, to: HsFunPtr.self)
|
||||
SwiftHaskell.callbackExample(hsf)
|
||||
}
|
||||
|
||||
// Swift to Haskell Callback with Context Example
|
||||
|
||||
func contextCallbackExample(f: ((CInt) -> Void)) {
|
||||
class Wrap<T> {
|
||||
var inner: T
|
||||
|
||||
init(_ inner: T) {
|
||||
self.inner = inner
|
||||
}
|
||||
}
|
||||
let x = 3
|
||||
func release(context: HsPtr) {
|
||||
let _: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeRetainedValue()
|
||||
}
|
||||
func call(context: HsPtr, value: CInt) {
|
||||
let wf: Wrap<(CInt) -> Void> = Unmanaged.fromOpaque(context).takeUnretainedValue()
|
||||
let f = wf.inner
|
||||
f(value)
|
||||
}
|
||||
let release_hs = unsafeBitCast(
|
||||
release as @convention(c) (HsPtr) -> Void, to: HsFunPtr.self)
|
||||
let call_hs = unsafeBitCast(
|
||||
call as @convention(c) (HsPtr, CInt) -> Void, to: HsFunPtr.self)
|
||||
let ctx = Unmanaged.passRetained(Wrap(f)).toOpaque()
|
||||
SwiftHaskell.contextCallbackExample(ctx, release_hs, call_hs)
|
||||
}
|
||||
|
||||
// Swift to Haskell Function Example
|
||||
|
||||
class Multiplier {
|
||||
let funPtr: HsFunPtr
|
||||
|
||||
init(_ x: CInt) {
|
||||
self.funPtr = SwiftHaskell.makeMultiplier(x)
|
||||
}
|
||||
|
||||
func multiply(_ y: CInt) -> CInt {
|
||||
typealias F = @convention(c) (CInt) -> CInt
|
||||
let f = unsafeBitCast(self.funPtr, to: F.self)
|
||||
return f(y)
|
||||
}
|
||||
|
||||
deinit {
|
||||
SwiftHaskell.freeMultiplier(self.funPtr)
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="SwiftHaskell" customModuleProvider="target">
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="SwiftAppLibrary" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="label" destination="drV-ep-pYM" id="xdv-Y5-tR0"/>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
|
||||
@@ -690,9 +690,8 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="drV-ep-pYM">
|
||||
<rect key="frame" x="18" y="323" width="37" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="drV-ep-pYM">
|
||||
<rect key="frame" x="18" y="323" width="444" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="Wba-z2-8m1">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -700,6 +699,11 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="drV-ep-pYM" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="5eo-Sd-hEH"/>
|
||||
<constraint firstItem="drV-ep-pYM" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" id="OlT-mM-yos"/>
|
||||
<constraint firstAttribute="trailing" secondItem="drV-ep-pYM" secondAttribute="trailing" constant="20" id="kzO-Mq-rea"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</window>
|
||||
</objects>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2017 nanotech. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// SwiftAppLibrary.h
|
||||
// SwiftAppLibrary
|
||||
//
|
||||
// Created by NanoTech on 2017-02-08.
|
||||
// Copyright © 2017 nanotech. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//! Project version number for SwiftAppLibrary.
|
||||
FOUNDATION_EXPORT double SwiftAppLibraryVersionNumber;
|
||||
|
||||
//! Project version string for SwiftAppLibrary.
|
||||
FOUNDATION_EXPORT const unsigned char SwiftAppLibraryVersionString[];
|
||||
|
||||
FOUNDATION_EXPORT void runNSApplication(void);
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <SwiftAppLibrary/PublicHeader.h>
|
||||
@@ -0,0 +1,13 @@
|
||||
#import "SwiftAppLibrary.h"
|
||||
|
||||
@interface AClassInThisFramework : NSObject @end
|
||||
@implementation AClassInThisFramework @end
|
||||
|
||||
void runNSApplication(void) {
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[AClassInThisFramework class]];
|
||||
NSArray *topObjects;
|
||||
[[[NSNib alloc] initWithNibNamed:@"MainMenu" bundle:bundle]
|
||||
instantiateWithOwner:app topLevelObjects:&topObjects];
|
||||
[app run];
|
||||
}
|
||||
@@ -7,136 +7,187 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
BFABC4321E4BE794006036C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFABC4311E4BE794006036C6 /* AppDelegate.swift */; };
|
||||
BFABC4341E4BE794006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4331E4BE794006036C6 /* Assets.xcassets */; };
|
||||
BFABC4371E4BE794006036C6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4351E4BE794006036C6 /* MainMenu.xib */; };
|
||||
BFABC4C31E4C26C8006036C6 /* libswifthaskell.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */; };
|
||||
BFABC4C61E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC4C51E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib */; };
|
||||
BFABC4C81E4C26E2006036C6 /* libffi.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC4C71E4C26E2006036C6 /* libffi.dylib */; };
|
||||
BFABC4C91E4C2746006036C6 /* libswifthaskell.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
BFABC4CA1E4C2746006036C6 /* libffi.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = BFABC4C71E4C26E2006036C6 /* libffi.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
BFABC4CB1E4C2746006036C6 /* libHSrts_thr-ghc8.0.1.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = BFABC4C51E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
BFABC4601E4C1DD1006036C6 /* SwiftAppLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */; };
|
||||
BFABC4D21E4D664D006036C6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4D01E4D664D006036C6 /* MainMenu.xib */; };
|
||||
BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4DF1E4D781D006036C6 /* Assets.xcassets */; };
|
||||
BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */ = {isa = PBXBuildFile; fileRef = BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
BFABC4E81E4D7842006036C6 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = BFABC4521E4C1DD1006036C6 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = BFABC45A1E4C1DD1006036C6;
|
||||
remoteInfo = SwiftAppLibrary;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
BFABC44B1E4C1BFA006036C6 /* CopyFiles */ = {
|
||||
BFABC4EC1E4D78DF006036C6 /* Copy Files */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 6;
|
||||
files = (
|
||||
BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */,
|
||||
);
|
||||
name = "Copy Files";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
BFABC4EF1E4D7948006036C6 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
BFABC4C91E4C2746006036C6 /* libswifthaskell.dylib in CopyFiles */,
|
||||
BFABC4CA1E4C2746006036C6 /* libffi.dylib in CopyFiles */,
|
||||
BFABC4CB1E4C2746006036C6 /* libHSrts_thr-ghc8.0.1.dylib in CopyFiles */,
|
||||
BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
BFABC42E1E4BE794006036C6 /* SwiftHaskell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftHaskell.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFABC4311E4BE794006036C6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
BFABC4331E4BE794006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
BFABC4361E4BE794006036C6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
BFABC4381E4BE794006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
BFABC4461E4C011B006036C6 /* SwiftHaskell-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftHaskell-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libswifthaskell.dylib; path = build/libswifthaskell.dylib; sourceTree = "<group>"; };
|
||||
BFABC4C51E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libHSrts_thr-ghc8.0.1.dylib"; path = "build/libHSrts_thr-ghc8.0.1.dylib"; sourceTree = "<group>"; };
|
||||
BFABC4C71E4C26E2006036C6 /* libffi.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libffi.dylib; path = build/libffi.dylib; sourceTree = "<group>"; };
|
||||
BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftAppLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftAppLibrary.h; sourceTree = "<group>"; };
|
||||
BFABC45F1E4C1DD1006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
BFABC4D11E4D664D006036C6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||
BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftHaskell.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFABC4DF1E4D781D006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
BFABC4E41E4D781D006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = SwiftHaskell; path = build/SwiftHaskell; sourceTree = SOURCE_ROOT; };
|
||||
BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwiftAppLibrary.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
BFABC42B1E4BE794006036C6 /* Frameworks */ = {
|
||||
BFABC4571E4C1DD1006036C6 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFABC4C31E4C26C8006036C6 /* libswifthaskell.dylib in Frameworks */,
|
||||
BFABC4C81E4C26E2006036C6 /* libffi.dylib in Frameworks */,
|
||||
BFABC4C61E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
BFABC4251E4BE794006036C6 = {
|
||||
BFABC4511E4C1DD1006036C6 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFABC4301E4BE794006036C6 /* SwiftHaskell */,
|
||||
BFABC4C41E4C26CA006036C6 /* Libraries */,
|
||||
BFABC42F1E4BE794006036C6 /* Products */,
|
||||
BFABC45D1E4C1DD1006036C6 /* SwiftAppLibrary */,
|
||||
BFABC4D81E4D781D006036C6 /* SwiftHaskell */,
|
||||
BFABC45C1E4C1DD1006036C6 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFABC42F1E4BE794006036C6 /* Products */ = {
|
||||
BFABC45C1E4C1DD1006036C6 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFABC42E1E4BE794006036C6 /* SwiftHaskell.app */,
|
||||
BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */,
|
||||
BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFABC4301E4BE794006036C6 /* SwiftHaskell */ = {
|
||||
BFABC45D1E4C1DD1006036C6 /* SwiftAppLibrary */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFABC4311E4BE794006036C6 /* AppDelegate.swift */,
|
||||
BFABC4461E4C011B006036C6 /* SwiftHaskell-Bridging-Header.h */,
|
||||
BFABC4331E4BE794006036C6 /* Assets.xcassets */,
|
||||
BFABC4351E4BE794006036C6 /* MainMenu.xib */,
|
||||
BFABC4381E4BE794006036C6 /* Info.plist */,
|
||||
BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */,
|
||||
BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */,
|
||||
BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */,
|
||||
BFABC4D01E4D664D006036C6 /* MainMenu.xib */,
|
||||
BFABC45F1E4C1DD1006036C6 /* Info.plist */,
|
||||
);
|
||||
path = SwiftAppLibrary;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFABC4D81E4D781D006036C6 /* SwiftHaskell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFABC4DF1E4D781D006036C6 /* Assets.xcassets */,
|
||||
BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */,
|
||||
BFABC4E41E4D781D006036C6 /* Info.plist */,
|
||||
);
|
||||
path = SwiftHaskell;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFABC4C41E4C26CA006036C6 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */,
|
||||
BFABC4C71E4C26E2006036C6 /* libffi.dylib */,
|
||||
BFABC4C51E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
BFABC4581E4C1DD1006036C6 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFABC4601E4C1DD1006036C6 /* SwiftAppLibrary.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
BFABC42D1E4BE794006036C6 /* SwiftHaskell */ = {
|
||||
BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = BFABC43B1E4BE794006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */;
|
||||
buildConfigurationList = BFABC4631E4C1DD1006036C6 /* Build configuration list for PBXNativeTarget "SwiftAppLibrary" */;
|
||||
buildPhases = (
|
||||
BFABC42A1E4BE794006036C6 /* Sources */,
|
||||
BFABC42B1E4BE794006036C6 /* Frameworks */,
|
||||
BFABC42C1E4BE794006036C6 /* Resources */,
|
||||
BFABC44B1E4C1BFA006036C6 /* CopyFiles */,
|
||||
BFABC4441E4BFEA3006036C6 /* ShellScript */,
|
||||
BF2EA5CA1E5030E100651018 /* stack build and link-deps */,
|
||||
BFABC4561E4C1DD1006036C6 /* Sources */,
|
||||
BFABC4571E4C1DD1006036C6 /* Frameworks */,
|
||||
BFABC4581E4C1DD1006036C6 /* Headers */,
|
||||
BFABC4591E4C1DD1006036C6 /* Resources */,
|
||||
BFABC4CE1E4D6344006036C6 /* Link framework to build */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SwiftAppLibrary;
|
||||
productName = SwiftAppLibrary;
|
||||
productReference = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
BFABC4D61E4D781D006036C6 /* SwiftHaskell */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = BFABC4E51E4D781D006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */;
|
||||
buildPhases = (
|
||||
BFABC4D51E4D781D006036C6 /* Resources */,
|
||||
BFABC4EF1E4D7948006036C6 /* Embed Frameworks */,
|
||||
BFABC4EC1E4D78DF006036C6 /* Copy Files */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
BFABC4E91E4D7842006036C6 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SwiftHaskell;
|
||||
productName = SwiftHaskell;
|
||||
productReference = BFABC42E1E4BE794006036C6 /* SwiftHaskell.app */;
|
||||
productReference = BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
BFABC4261E4BE794006036C6 /* Project object */ = {
|
||||
BFABC4521E4C1DD1006036C6 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0820;
|
||||
LastUpgradeCheck = 0820;
|
||||
ORGANIZATIONNAME = nanotech;
|
||||
TargetAttributes = {
|
||||
BFABC42D1E4BE794006036C6 = {
|
||||
BFABC45A1E4C1DD1006036C6 = {
|
||||
CreatedOnToolsVersion = 8.2;
|
||||
LastSwiftMigration = 0820;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
BFABC4D61E4D781D006036C6 = {
|
||||
CreatedOnToolsVersion = 8.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = BFABC4291E4BE794006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */;
|
||||
buildConfigurationList = BFABC4551E4C1DD1006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
@@ -144,80 +195,115 @@
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = BFABC4251E4BE794006036C6;
|
||||
productRefGroup = BFABC42F1E4BE794006036C6 /* Products */;
|
||||
mainGroup = BFABC4511E4C1DD1006036C6;
|
||||
productRefGroup = BFABC45C1E4C1DD1006036C6 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
BFABC42D1E4BE794006036C6 /* SwiftHaskell */,
|
||||
BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */,
|
||||
BFABC4D61E4D781D006036C6 /* SwiftHaskell */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
BFABC42C1E4BE794006036C6 /* Resources */ = {
|
||||
BFABC4591E4C1DD1006036C6 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFABC4341E4BE794006036C6 /* Assets.xcassets in Resources */,
|
||||
BFABC4371E4BE794006036C6 /* MainMenu.xib in Resources */,
|
||||
BFABC4D21E4D664D006036C6 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
BFABC4D51E4D781D006036C6 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
BFABC4441E4BFEA3006036C6 /* ShellScript */ = {
|
||||
BF2EA5CA1E5030E100651018 /* stack build and link-deps */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(PROJECT_DIR)/src/Lib.hs",
|
||||
"$(PROJECT_DIR)/src/Main.hs",
|
||||
"$(PROJECT_DIR)/SwiftHaskellLibrary.cabal",
|
||||
"$(PROJECT_DIR)/stack.yaml",
|
||||
);
|
||||
name = "stack build and link-deps";
|
||||
outputPaths = (
|
||||
"$(PROJECT_DIR)/build/libswifthaskell.dylib",
|
||||
"$(PROJECT_DIR)/build/SwiftHaskell",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "stack build";
|
||||
shellScript = "stack build\nbash link-deps.sh";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
BFABC4CE1E4D6344006036C6 /* Link framework to build */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(BUILT_PRODUCTS_DIR)/$(FULL_PRODUCT_NAME)",
|
||||
);
|
||||
name = "Link framework to build";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -u\nln -sf \"${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}\" \"${PROJECT_DIR}/build/\"";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
BFABC42A1E4BE794006036C6 /* Sources */ = {
|
||||
BFABC4561E4C1DD1006036C6 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFABC4321E4BE794006036C6 /* AppDelegate.swift in Sources */,
|
||||
BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */,
|
||||
BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
BFABC4E91E4D7842006036C6 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */;
|
||||
targetProxy = BFABC4E81E4D7842006036C6 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
BFABC4351E4BE794006036C6 /* MainMenu.xib */ = {
|
||||
BFABC4D01E4D664D006036C6 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
BFABC4361E4BE794006036C6 /* Base */,
|
||||
BFABC4D11E4D664D006036C6 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
path = Base.lproj;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
BFABC4391E4BE794006036C6 /* Debug */ = {
|
||||
BFABC4611E4C1DD1006036C6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
@@ -233,6 +319,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
@@ -256,17 +343,19 @@
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
BFABC43A1E4BE794006036C6 /* Release */ = {
|
||||
BFABC4621E4C1DD1006036C6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
@@ -282,6 +371,7 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -297,69 +387,121 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
BFABC43C1E4BE794006036C6 /* Debug */ = {
|
||||
BFABC4641E4C1DD1006036C6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = SwiftHaskell/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/build",
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = SwiftAppLibrary/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"-undefined",
|
||||
dynamic_lookup,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftAppLibrary;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "SwiftHaskell/SwiftHaskell-Bridging-Header.h";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/SwiftHaskell/include";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/build $(PROJECT_DIR)/build/include";
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/build/ghc/include";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
BFABC43D1E4BE794006036C6 /* Release */ = {
|
||||
BFABC4651E4C1DD1006036C6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = SwiftAppLibrary/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
"-undefined",
|
||||
dynamic_lookup,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftAppLibrary;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/SwiftHaskell/include";
|
||||
SWIFT_VERSION = 3.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/build/ghc/include";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
BFABC4E61E4D781D006036C6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = SwiftHaskell/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
BFABC4E71E4D781D006036C6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = SwiftHaskell/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/build",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "SwiftHaskell/SwiftHaskell-Bridging-Header.h";
|
||||
SWIFT_VERSION = 3.0;
|
||||
USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/build $(PROJECT_DIR)/build/include";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
BFABC4291E4BE794006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */ = {
|
||||
BFABC4551E4C1DD1006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
BFABC4391E4BE794006036C6 /* Debug */,
|
||||
BFABC43A1E4BE794006036C6 /* Release */,
|
||||
BFABC4611E4C1DD1006036C6 /* Debug */,
|
||||
BFABC4621E4C1DD1006036C6 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
BFABC43B1E4BE794006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */ = {
|
||||
BFABC4631E4C1DD1006036C6 /* Build configuration list for PBXNativeTarget "SwiftAppLibrary" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
BFABC43C1E4BE794006036C6 /* Debug */,
|
||||
BFABC43D1E4BE794006036C6 /* Release */,
|
||||
BFABC4641E4C1DD1006036C6 /* Debug */,
|
||||
BFABC4651E4C1DD1006036C6 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
BFABC4E51E4D781D006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
BFABC4E61E4D781D006036C6 /* Debug */,
|
||||
BFABC4E71E4D781D006036C6 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = BFABC4261E4BE794006036C6 /* Project object */;
|
||||
rootObject = BFABC4521E4C1DD1006036C6 /* Project object */;
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// SwiftHaskell
|
||||
//
|
||||
// Created by NanoTech on 2017-02-08.
|
||||
// Copyright © 2017 nanotech. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
@IBOutlet weak var label: NSTextField!
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
var argv0 = Array("SwiftHaskell".utf8CString)
|
||||
argv0.withUnsafeMutableBufferPointer { argv0bp in
|
||||
var argv = [argv0bp.baseAddress];
|
||||
var argc = CInt(argv.count)
|
||||
argv.withUnsafeMutableBufferPointer { argvbp in
|
||||
var argvp = argvbp.baseAddress
|
||||
hs_init(&argc, &argvp)
|
||||
}
|
||||
}
|
||||
|
||||
label.stringValue = "\(square(5))"
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
hs_exit()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
//
|
||||
// SwiftHaskell-Bridging-Header.h
|
||||
// SwiftHaskell
|
||||
//
|
||||
// Created by NanoTech on 2017-02-08.
|
||||
// Copyright © 2017 nanotech. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SwiftHaskell_Bridging_Header_h
|
||||
#define SwiftHaskell_Bridging_Header_h
|
||||
|
||||
#include "Lib_stub.h"
|
||||
|
||||
#endif /* SwiftHaskell_Bridging_Header_h */
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "HsFFI.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern HsInt32 square(HsInt32 a1);
|
||||
extern HsWord64 countBytes(HsWord8 a1, HsPtr a2, HsWord64 a3);
|
||||
extern HsPtr getSequence(HsPtr a1);
|
||||
extern void callbackExample(HsFunPtr a1);
|
||||
extern void contextCallbackExample(HsPtr a1, HsFunPtr a2, HsFunPtr a3);
|
||||
extern HsFunPtr makeMultiplier(HsInt32 a1);
|
||||
extern void freeMultiplier(HsFunPtr a1);
|
||||
extern HsInt32 Main_d9Dt(StgStablePtr the_stableptr, HsInt32 a1);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module SwiftHaskell {
|
||||
header "Main_stub.h"
|
||||
export *
|
||||
}
|
||||
@@ -13,13 +13,17 @@ build-type: Simple
|
||||
extra-source-files:
|
||||
cabal-version: >=1.10
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
exposed-modules: Lib
|
||||
ghc-options: -threaded -dynamic -shared -fPIC -o build/libswifthaskell.dylib
|
||||
build-depends: base >= 4.7 && < 5
|
||||
default-language: Haskell2010
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/nanotech/swift-haskell-tutorial
|
||||
|
||||
executable SwiftHaskell
|
||||
default-language: Haskell2010
|
||||
hs-source-dirs: src
|
||||
main-is: Main.hs
|
||||
ghc-options: -threaded -framework-path build
|
||||
ld-options: -rpath @executable_path/../Frameworks
|
||||
frameworks: SwiftAppLibrary
|
||||
build-depends: base >= 4.7 && < 5
|
||||
, bytestring >= 0.10 && < 0.11
|
||||
, text >= 1.2 && < 1.3
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
EXECUTABLE_NAME=SwiftHaskell
|
||||
DIST_DIR="$(stack path --dist-dir)"
|
||||
GHC_VERSION="$(stack exec -- ghc --numeric-version)"
|
||||
GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION"
|
||||
STUB_BUILD_DIR="${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}-tmp"
|
||||
STUB_MODULE_DIR="${EXECUTABLE_NAME}/include"
|
||||
STUB_MODULE_MAP="${STUB_MODULE_DIR}/module.modulemap"
|
||||
|
||||
# Create a module map from the generated Haskell
|
||||
# FFI export headers for importing into Swift.
|
||||
mkdir -p "${STUB_MODULE_DIR}"
|
||||
NL="
|
||||
"
|
||||
module_map="module ${EXECUTABLE_NAME} {${NL}"
|
||||
for h in $(find "${STUB_BUILD_DIR}" -name '*.h'); do
|
||||
h_filename="${h/$STUB_BUILD_DIR\//}"
|
||||
cp "$h" "${STUB_MODULE_DIR}/"
|
||||
module_map="${module_map} header \"${h_filename}\"${NL}"
|
||||
done
|
||||
module_map="${module_map} export *${NL}"
|
||||
module_map="${module_map}}"
|
||||
echo "${module_map}" > "${STUB_MODULE_MAP}"
|
||||
|
||||
# Symlink to the current GHC's header directory from a more
|
||||
# convenient place for Xcode to find.
|
||||
mkdir -p build/ghc
|
||||
ln -sf "${GHC_LIB_DIR}/include" build/ghc/
|
||||
|
||||
# Symlink to the Haskell executable for Xcode.
|
||||
ln -sf "../${DIST_DIR}/build/${EXECUTABLE_NAME}/${EXECUTABLE_NAME}" build/
|
||||
@@ -0,0 +1,106 @@
|
||||
module Main where
|
||||
|
||||
import Control.Concurrent (forkIO)
|
||||
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Char8 as B8
|
||||
import qualified Data.ByteString.Unsafe as BU
|
||||
import qualified Data.Text as T
|
||||
import Data.Word (Word8)
|
||||
|
||||
import Foreign.C (CChar (CChar), CInt (CInt), CSize (CSize))
|
||||
import Foreign.ForeignPtr (newForeignPtr, withForeignPtr)
|
||||
import Foreign.Marshal.Array (copyArray, mallocArray)
|
||||
import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr)
|
||||
import Foreign.Storable (poke)
|
||||
|
||||
foreign export ccall square :: CInt -> CInt
|
||||
|
||||
square :: CInt -> CInt
|
||||
square x = x * x
|
||||
|
||||
foreign import ccall "runNSApplication" runNSApplication :: IO ()
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "hello world"
|
||||
runNSApplication
|
||||
|
||||
|
||||
-- Examples
|
||||
|
||||
-- Swift to Haskell ByteString Example
|
||||
|
||||
foreign export ccall countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize
|
||||
|
||||
countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize
|
||||
countBytes needle haystack haystackLen = do
|
||||
s <- B.packCStringLen (haystack, fromIntegral haystackLen)
|
||||
pure (B.foldl (\count b -> count + if b == needle then 1 else 0) 0 s)
|
||||
|
||||
-- Haskell to Swift ByteString Example
|
||||
|
||||
mallocCopyByteString :: ByteString -> IO (Ptr CChar, Int)
|
||||
mallocCopyByteString s =
|
||||
BU.unsafeUseAsCStringLen s $ \(p, n) -> do
|
||||
a <- mallocArray n
|
||||
copyArray a p n
|
||||
pure (a, n)
|
||||
|
||||
foreign export ccall getSequence :: Ptr CSize -> IO (Ptr CChar)
|
||||
|
||||
getSequence :: Ptr CSize -> IO (Ptr CChar)
|
||||
getSequence sizePtr = do
|
||||
(p, n) <- mallocCopyByteString (B.pack [1..10])
|
||||
poke sizePtr (fromIntegral n)
|
||||
pure p
|
||||
|
||||
-- Swift to Haskell Callback Example
|
||||
|
||||
foreign export ccall callbackExample :: FunPtr (CInt -> IO ()) -> IO ()
|
||||
foreign import ccall "dynamic" unwrapCallback :: FunPtr (CInt -> IO ()) -> (CInt -> IO ())
|
||||
|
||||
callbackExample :: FunPtr (CInt -> IO ()) -> IO ()
|
||||
callbackExample f = (unwrapCallback f) 3
|
||||
|
||||
-- Swift to Haskell Callback with Context Example
|
||||
|
||||
foreign export ccall contextCallbackExample
|
||||
:: Ptr ()
|
||||
-> FunPtr (Ptr () -> IO ())
|
||||
-> FunPtr (Ptr () -> CInt -> IO ())
|
||||
-> IO ()
|
||||
foreign import ccall "dynamic" unwrapContextCallback
|
||||
:: FunPtr (Ptr () -> CInt -> IO ())
|
||||
-> (Ptr () -> CInt -> IO ())
|
||||
|
||||
contextCallbackExample
|
||||
:: Ptr () -- ^ Context pointer
|
||||
-> FunPtr (Ptr () -> IO ()) -- ^ Context release function
|
||||
-> FunPtr (Ptr () -> CInt -> IO ()) -- ^ Callback function
|
||||
-> IO ()
|
||||
contextCallbackExample ctxp releaseCtx callbackPtr = do
|
||||
ctxfp <- newForeignPtr releaseCtx ctxp
|
||||
let callback :: CInt -> IO ()
|
||||
callback result = withForeignPtr ctxfp $ \ctxp' ->
|
||||
(unwrapContextCallback callbackPtr) ctxp' result
|
||||
_ <- forkIO $ do
|
||||
let result = 3 -- perform your complex computation here
|
||||
callback result
|
||||
pure ()
|
||||
|
||||
-- Swift to Haskell Function Example
|
||||
|
||||
foreign export ccall makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt))
|
||||
foreign import ccall "wrapper" wrapMultiplier
|
||||
:: (CInt -> CInt)
|
||||
-> IO (FunPtr (CInt -> CInt))
|
||||
|
||||
makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt))
|
||||
makeMultiplier x = wrapMultiplier (x *)
|
||||
|
||||
foreign export ccall freeMultiplier :: FunPtr (CInt -> CInt) -> IO ()
|
||||
|
||||
freeMultiplier :: FunPtr (CInt -> CInt) -> IO ()
|
||||
freeMultiplier = freeHaskellFunPtr
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
DIST_DIR="$(stack path --dist-dir)"
|
||||
GHC_VERSION="$(stack exec -- ghc --numeric-version)"
|
||||
GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION"
|
||||
|
||||
ln -sf ../"$DIST_DIR"/build/Lib_stub.h build/
|
||||
ln -sf "$GHC_LIB_DIR"/include build/
|
||||
ln -sf "$GHC_LIB_DIR"/rts/libHSrts_thr-ghc"$GHC_VERSION".dylib build/
|
||||
ln -sf "$GHC_LIB_DIR"/rts/libffi.dylib build/
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.0 KiB |