Author SHA1 Message Date
NanoTech c8959b0cf2 Change some titles 2017-02-15 22:35:53 -06:00
NanoTech 16bb42eedd Update screenshots
- Crop the first two project creation screenshots
- Update and use the New Copy Files Phase screenshot
- Remove unused screenshots
2017-02-15 22:20:47 -06:00
NanoTech d185c5f0c2 Update Run Script input files 2017-02-15 22:10:50 -06:00
NanoTech 6f870aea9c Fix linking with -whole-module-optimization enabled 2017-02-15 22:10:50 -06:00
NanoTech d244d0293d xcodebuild postBuild hook example 2017-02-15 21:47:06 -06:00
NanoTech 898c58a23e Add FFI tutorial and examples 2017-02-15 20:32:22 -06:00
NanoTech 4fea5657c2 Complete the linking tutorial 2017-02-12 03:06:39 -06:00
NanoTech 47f6693417 Alternate version that links the Swift code as a framework 2017-02-09 23:21:59 -06:00
31 changed files with 1277 additions and 294 deletions
+678 -126
View File
@@ -1,204 +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 # Integrating Haskell with Swift Mac Apps
To start, let's create a new Xcode project: To start, create a new Cocoa Application Xcode project
![Select the Cocoa app template](tutorial/xcode-cocoa-template.png) ![Select the Cocoa app template](tutorial/xcode-cocoa-template-icon.png)
![Create a new project](tutorial/xcode-create-project.png) with Swift as the default language.
![Select Swift as the default language](tutorial/xcode-create-project-language-swift.png)
Then `cd` into the directory with the `.xcodeproj` and create a Then `cd` into the directory with the `.xcodeproj` and create a
new stack project: new stack project:
$ cd SwiftHaskell ```sh
$ stack new SwiftHaskellLibrary simple-library $ cd SwiftHaskell
$ stack new SwiftHaskellLibrary simple
```
Let's move these files up to the top directory, so we can Move these files up to the top directory, so we can run all of
run both `stack` and `xcodebuild` from the same directory: our commands from the same directory:
$ mv -vn SwiftHaskellLibrary/* . ```sh
$ rmdir SwiftHaskellLibrary $ 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 ```haskell
square x = x * x square x = x * x
```
If we `stack build` now, in addition to building the library, Haskell functions exported via the FFI can only contain
GHC will generate a C header file for us to include. Because certain types in their signatures that are compatible with C:
it's a build artifact, it's buried somewhat deep in the file primitive integers, floats and doubles, and pointer types.
hierarchy, but we can ask `stack` where it is: 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 Since we'll only be using `square` to demonstrate the FFI, let's
.stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/Lib_stub.h 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 ```haskell
to find the current compiler's version of that header. 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 $ 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 /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 Since we'll be importing these headers into a Swift framework,
location so we don't need to change the Xcode project when the we won't be able to use `#include` as we would in C. Instead,
compiler or Cabal version changes: 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 module SwiftHaskell {
set -eux header "Main_stub.h"
DIST_DIR="$(stack path --dist-dir)" export *
ln -sf ../"$DIST_DIR"/build/Lib_stub.h build/ }
ln -sf "$GHC_LIB_DIR"/include build/
Run it, then add `$(PROJECT_DIR)/build` and [swift-bridging-headers]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID156
`$(PROJECT_DIR)/build/include` to the target's *User Header [so-non-modular-header]: https://stackoverflow.com/questions/24103169/swift-compiler-error-non-modular-header-inside-framework-module/37072619#37072619
Search Paths* in Xcode: [clang-modules]: http://clang.llvm.org/docs/Modules.html
![The User Header Search Paths](tutorial/xcode-header-search-paths.png) 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 ```bash
need to import them into Swift. Create a new header file in #!/usr/bin/env bash
Xcode named `SwiftHaskell-Bridging-Header.h` and save it in the set -eu
same directory as `AppDelegate.swift`. Then set it as the
*Objective-C Bridging Header* in Xcode:
![Set the target's Objective-C Bridging Header](tutorial/xcode-bridging-header.png) 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 # Symlink to the current GHC's header directory from a more
#define SwiftHaskell_Bridging_Header_h # 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 Create a new Cocoa Framework target in the Xcode project
as a dynamic library and add it to our Xcode project. Building a named SwiftAppLibrary, then change the Target Membership of
static library is also possible, but currently requires `AppDelegate.swift` and `MainMenu.xib` to only SwiftAppLibrary
[rebuilding the GHC standard libraries][pic-ghc]. in Xcode's File Inspector in the right sidebar:
[pic-ghc]: https://github.com/lyokha/nginx-haskell-module#static-linkage-against-basic-haskell-libraries ![Target Membership](tutorial/xcode-target-membership.png)
Add a `ghc-options` line to the `library` section in the In the new framework's build settings, set **Always Embed Swift
`.cabal` file: Standard Libraries** to **Yes**.
library Drag the `SwiftHaskell` executable we built previously with
# ... other options Stack into Xcode from the `build/` directory that we symlinked
ghc-options: -threaded -dynamic -shared -fPIC -o build/libswifthaskell.dylib it into, but do not add it to any targets when prompted:
![The SwiftHaskell executable in Xcode](tutorial/xcode-files-swifthaskell-executable.png)
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:
![New Copy Files Phase](tutorial/xcode-new-copy-files-phase.png)
![Copy into Executables](tutorial/xcode-copy-files-swifthaskell-executable.png)
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 - `-threaded` enables the multithreaded GHC runtime, which is
usually what you want. usually what you want.
- `-dynamic` tells GHC to link to the dynamic versions of - `-framework-path build` tells GHC to look for frameworks where we
Haskell libraries. This is required when using `-shared`. symlinked our framework to.
- `-shared` builds a shared library. - `-rpath @executable_path/../Frameworks` embeds into the
- `-fPIC` enables position-independent code, which is needed for executable where the dynamic linker should look for shared
shared libraries. libraries.
Run `stack build`, then drag `build/libswifthaskell.dylib` into ## Starting Cocoa
the Xcode project and add it to the SwiftHaskell target.
We'll also need to link to the RTS so we can initialize it Because Haskell has control over the program's entry point
from Swift, and copy all shared library dependencies into the (`main`), we'll need to have it call out to Cocoa to start its
app bundle so it's self-contained. `otool -L` will show what main thread. In `SwiftAppLibrary.h`, declare a new function
libraries `libswifthaskell.dylib` depends on: named `runNSApplication` and mark it as `FOUNDATION_EXPORT` to
indicate that it should be exported from the framework:
$ otool -L build/libswifthaskell.dylib ```c
build/libswifthaskell.dylib: FOUNDATION_EXPORT void runNSApplication(void);
@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)
Let's update our previous script to locate them and symlink them Implement the function by adding a new Objective-C `.m` file to
into the build directory: the framework target containing
#!/bin/sh ```objective-c
set -eux #import "SwiftAppLibrary.h"
DIST_DIR="$(stack path --dist-dir)" @interface AClassInThisFramework : NSObject @end
GHC_VERSION="$(stack exec -- ghc --numeric-version)" @implementation AClassInThisFramework @end
GHC_LIB_DIR="$(stack path --compiler-bin)/../lib/ghc-$GHC_VERSION"
ln -sf ../"$DIST_DIR"/build/Lib_stub.h build/ void runNSApplication(void) {
ln -sf "$GHC_LIB_DIR"/include build/ NSApplication *app = [NSApplication sharedApplication];
ln -sf "$GHC_LIB_DIR"/rts/libHSrts_thr-ghc"$GHC_VERSION".dylib build/ NSBundle *bundle = [NSBundle bundleForClass:[AClassInThisFramework class]];
# FIXME: Link the other Haskell libraries NSArray *topObjects;
ln -sf "$GHC_LIB_DIR"/rts/libffi.dylib build/ [[[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 In `Main.hs`, import the foreign function and call it from
`cabal-macosx`. the end of `main`:
Drag these libraries into Xcode too, ```haskell
module Main where
![Xcode files](tutorial/xcode-files.png) import Foreign.C
Then go to the target's Build Phases panel and add a new phase foreign export ccall square :: CInt -> CInt
to copy the libraries into the app bundle:
![Add a new Copy Files phase](tutorial/xcode-new-copy-files-phase.png) square :: CInt -> CInt
square x = x * x
![Add the libraries to the phase](tutorial/xcode-copy-frameworks.png) 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 `runNSApplication` will not return, being busy with Cocoa's
runtime. `hs_init` takes pointers to C's argc and argv. In main run loop. Use `Control.Concurrent.forkIO` before calling
Swift, these are available as `CommandLine.argc` and `runNSApplication` to run other tasks as needed.
`CommandLine.unsafeArgv`.
Add this to your app delegate's `applicationDidFinishLaunching` Run `stack build`, and build and run the `SwiftHaskell` app
method: target in Xcode to launch the app and see the default window
from `MainMenu.xib`:
func applicationDidFinishLaunching(_ aNotification: Notification) { ![A blank window](tutorial/empty-app.png)
var argc = CommandLine.argc
var argv = Optional.some(CommandLine.unsafeArgv)
hs_init(&argc, &argv)
}
And the corresponding `hs_exit` to `applicationWillTerminate`: ## Linking to the Executable
func applicationWillTerminate(_ aNotification: Notification) { Add `$(PROJECT_DIR)/SwiftHaskell/include` to the framework
hs_exit() target's **Swift Compiler - Search Paths, Import Paths** setting
} in Xcode,
![The Swift module import paths](tutorial/xcode-swift-module-search-paths.png)
and `$(PROJECT_DIR)/build/ghc/include` to the framework's **User
Header Search Paths** setting:
![The User Header Search Paths](tutorial/xcode-header-search-paths.png)
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 ## 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 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 the result of our Haskell function `square` into, and add it as
an `@IBOutlet` to the `AppDelegate`: 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 We already have our Haskell library's header imported, so we
can just call the exported `square` function. Add this to can just call the exported `square` function. Add this to
`applicationDidFinishLaunching`: `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,
![5 squared](tutorial/squared.png) ![5 squared](tutorial/squared.png)
## 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)
}
}
```
+3
View File
@@ -0,0 +1,3 @@
module SwHaLib {
export *
}
+115
View File
@@ -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>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <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> <connections>
<outlet property="label" destination="drV-ep-pYM" id="xdv-Y5-tR0"/> <outlet property="label" destination="drV-ep-pYM" id="xdv-Y5-tR0"/>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/> <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"/> <rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="drV-ep-pYM"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="drV-ep-pYM">
<rect key="frame" x="18" y="323" width="37" height="17"/> <rect key="frame" x="18" y="323" width="444" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="Wba-z2-8m1"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="Wba-z2-8m1">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -700,6 +699,11 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
</subviews> </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> </view>
</window> </window>
</objects> </objects>
+26
View File
@@ -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>
+19
View File
@@ -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>
+13
View File
@@ -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];
}
+245 -103
View File
@@ -7,136 +7,187 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
BFABC4321E4BE794006036C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFABC4311E4BE794006036C6 /* AppDelegate.swift */; }; BFABC4601E4C1DD1006036C6 /* SwiftAppLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; };
BFABC4341E4BE794006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4331E4BE794006036C6 /* Assets.xcassets */; }; BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */; };
BFABC4371E4BE794006036C6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4351E4BE794006036C6 /* MainMenu.xib */; }; BFABC4D21E4D664D006036C6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4D01E4D664D006036C6 /* MainMenu.xib */; };
BFABC4C31E4C26C8006036C6 /* libswifthaskell.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */; }; BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFABC4DF1E4D781D006036C6 /* Assets.xcassets */; };
BFABC4C61E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC4C51E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib */; }; BFABC4ED1E4D78E5006036C6 /* SwiftHaskell in Copy Files */ = {isa = PBXBuildFile; fileRef = BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
BFABC4C81E4C26E2006036C6 /* libffi.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC4C71E4C26E2006036C6 /* libffi.dylib */; }; BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BFABC4C91E4C2746006036C6 /* libswifthaskell.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */ = {isa = PBXBuildFile; fileRef = BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */; };
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, ); }; };
/* End PBXBuildFile section */ /* 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 */ /* 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; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
BFABC4C91E4C2746006036C6 /* libswifthaskell.dylib in CopyFiles */, BFABC4F01E4D7951006036C6 /* SwiftAppLibrary.framework in Embed Frameworks */,
BFABC4CA1E4C2746006036C6 /* libffi.dylib in CopyFiles */,
BFABC4CB1E4C2746006036C6 /* libHSrts_thr-ghc8.0.1.dylib in CopyFiles */,
); );
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
BFABC42E1E4BE794006036C6 /* SwiftHaskell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftHaskell.app; sourceTree = BUILT_PRODUCTS_DIR; }; BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftAppLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BFABC4311E4BE794006036C6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftAppLibrary.h; sourceTree = "<group>"; };
BFABC4331E4BE794006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; BFABC45F1E4C1DD1006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFABC4361E4BE794006036C6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; }; BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
BFABC4381E4BE794006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; BFABC4D11E4D664D006036C6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = MainMenu.xib; sourceTree = "<group>"; };
BFABC4461E4C011B006036C6 /* SwiftHaskell-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftHaskell-Bridging-Header.h"; sourceTree = "<group>"; }; BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftHaskell.app; sourceTree = BUILT_PRODUCTS_DIR; };
BFABC4C21E4C26C8006036C6 /* libswifthaskell.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libswifthaskell.dylib; path = build/libswifthaskell.dylib; sourceTree = "<group>"; }; BFABC4DF1E4D781D006036C6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; 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>"; }; BFABC4E41E4D781D006036C6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFABC4C71E4C26E2006036C6 /* libffi.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libffi.dylib; path = build/libffi.dylib; 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 */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
BFABC42B1E4BE794006036C6 /* Frameworks */ = { BFABC4571E4C1DD1006036C6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
BFABC4C31E4C26C8006036C6 /* libswifthaskell.dylib in Frameworks */,
BFABC4C81E4C26E2006036C6 /* libffi.dylib in Frameworks */,
BFABC4C61E4C26DF006036C6 /* libHSrts_thr-ghc8.0.1.dylib in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
BFABC4251E4BE794006036C6 = { BFABC4511E4C1DD1006036C6 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BFABC4301E4BE794006036C6 /* SwiftHaskell */, BFABC45D1E4C1DD1006036C6 /* SwiftAppLibrary */,
BFABC4C41E4C26CA006036C6 /* Libraries */, BFABC4D81E4D781D006036C6 /* SwiftHaskell */,
BFABC42F1E4BE794006036C6 /* Products */, BFABC45C1E4C1DD1006036C6 /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
BFABC42F1E4BE794006036C6 /* Products */ = { BFABC45C1E4C1DD1006036C6 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BFABC42E1E4BE794006036C6 /* SwiftHaskell.app */, BFABC45B1E4C1DD1006036C6 /* SwiftAppLibrary.framework */,
BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
BFABC4301E4BE794006036C6 /* SwiftHaskell */ = { BFABC45D1E4C1DD1006036C6 /* SwiftAppLibrary */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BFABC4311E4BE794006036C6 /* AppDelegate.swift */, BFABC45E1E4C1DD1006036C6 /* SwiftAppLibrary.h */,
BFABC4461E4C011B006036C6 /* SwiftHaskell-Bridging-Header.h */, BFE4CC411E55553000F232D6 /* SwiftAppLibrary.m */,
BFABC4331E4BE794006036C6 /* Assets.xcassets */, BFABC4CC1E4D627E006036C6 /* AppDelegate.swift */,
BFABC4351E4BE794006036C6 /* MainMenu.xib */, BFABC4D01E4D664D006036C6 /* MainMenu.xib */,
BFABC4381E4BE794006036C6 /* Info.plist */, BFABC45F1E4C1DD1006036C6 /* Info.plist */,
);
path = SwiftAppLibrary;
sourceTree = "<group>";
};
BFABC4D81E4D781D006036C6 /* SwiftHaskell */ = {
isa = PBXGroup;
children = (
BFABC4DF1E4D781D006036C6 /* Assets.xcassets */,
BFABC4EA1E4D78D9006036C6 /* SwiftHaskell */,
BFABC4E41E4D781D006036C6 /* Info.plist */,
); );
path = SwiftHaskell; path = SwiftHaskell;
sourceTree = "<group>"; 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 */ /* 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 */ /* Begin PBXNativeTarget section */
BFABC42D1E4BE794006036C6 /* SwiftHaskell */ = { BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = BFABC43B1E4BE794006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */; buildConfigurationList = BFABC4631E4C1DD1006036C6 /* Build configuration list for PBXNativeTarget "SwiftAppLibrary" */;
buildPhases = ( buildPhases = (
BFABC42A1E4BE794006036C6 /* Sources */, BF2EA5CA1E5030E100651018 /* stack build and link-deps */,
BFABC42B1E4BE794006036C6 /* Frameworks */, BFABC4561E4C1DD1006036C6 /* Sources */,
BFABC42C1E4BE794006036C6 /* Resources */, BFABC4571E4C1DD1006036C6 /* Frameworks */,
BFABC44B1E4C1BFA006036C6 /* CopyFiles */, BFABC4581E4C1DD1006036C6 /* Headers */,
BFABC4441E4BFEA3006036C6 /* ShellScript */, BFABC4591E4C1DD1006036C6 /* Resources */,
BFABC4CE1E4D6344006036C6 /* Link framework to build */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( 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; name = SwiftHaskell;
productName = SwiftHaskell; productName = SwiftHaskell;
productReference = BFABC42E1E4BE794006036C6 /* SwiftHaskell.app */; productReference = BFABC4D71E4D781D006036C6 /* SwiftHaskell.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
BFABC4261E4BE794006036C6 /* Project object */ = { BFABC4521E4C1DD1006036C6 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0820;
LastUpgradeCheck = 0820; LastUpgradeCheck = 0820;
ORGANIZATIONNAME = nanotech; ORGANIZATIONNAME = nanotech;
TargetAttributes = { TargetAttributes = {
BFABC42D1E4BE794006036C6 = { BFABC45A1E4C1DD1006036C6 = {
CreatedOnToolsVersion = 8.2;
LastSwiftMigration = 0820;
ProvisioningStyle = Automatic;
};
BFABC4D61E4D781D006036C6 = {
CreatedOnToolsVersion = 8.2; CreatedOnToolsVersion = 8.2;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
}; };
}; };
}; };
buildConfigurationList = BFABC4291E4BE794006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */; buildConfigurationList = BFABC4551E4C1DD1006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
@@ -144,80 +195,115 @@
en, en,
Base, Base,
); );
mainGroup = BFABC4251E4BE794006036C6; mainGroup = BFABC4511E4C1DD1006036C6;
productRefGroup = BFABC42F1E4BE794006036C6 /* Products */; productRefGroup = BFABC45C1E4C1DD1006036C6 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
BFABC42D1E4BE794006036C6 /* SwiftHaskell */, BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */,
BFABC4D61E4D781D006036C6 /* SwiftHaskell */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
BFABC42C1E4BE794006036C6 /* Resources */ = { BFABC4591E4C1DD1006036C6 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
BFABC4341E4BE794006036C6 /* Assets.xcassets in Resources */, BFABC4D21E4D664D006036C6 /* MainMenu.xib in Resources */,
BFABC4371E4BE794006036C6 /* MainMenu.xib in Resources */, );
runOnlyForDeploymentPostprocessing = 0;
};
BFABC4D51E4D781D006036C6 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFABC4E01E4D781D006036C6 /* Assets.xcassets in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
BFABC4441E4BFEA3006036C6 /* ShellScript */ = { BF2EA5CA1E5030E100651018 /* stack build and link-deps */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
"$(PROJECT_DIR)/src/Lib.hs", "$(PROJECT_DIR)/src/Main.hs",
"$(PROJECT_DIR)/SwiftHaskellLibrary.cabal", "$(PROJECT_DIR)/SwiftHaskellLibrary.cabal",
"$(PROJECT_DIR)/stack.yaml", "$(PROJECT_DIR)/stack.yaml",
); );
name = "stack build and link-deps";
outputPaths = ( outputPaths = (
"$(PROJECT_DIR)/build/libswifthaskell.dylib", "$(PROJECT_DIR)/build/SwiftHaskell",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; 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; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
BFABC42A1E4BE794006036C6 /* Sources */ = { BFABC4561E4C1DD1006036C6 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
BFABC4321E4BE794006036C6 /* AppDelegate.swift in Sources */, BFABC4CD1E4D627E006036C6 /* AppDelegate.swift in Sources */,
BFE4CC421E55553000F232D6 /* SwiftAppLibrary.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
BFABC4E91E4D7842006036C6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BFABC45A1E4C1DD1006036C6 /* SwiftAppLibrary */;
targetProxy = BFABC4E81E4D7842006036C6 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
BFABC4351E4BE794006036C6 /* MainMenu.xib */ = { BFABC4D01E4D664D006036C6 /* MainMenu.xib */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
BFABC4361E4BE794006036C6 /* Base */, BFABC4D11E4D664D006036C6 /* Base */,
); );
name = MainMenu.xib; name = MainMenu.xib;
path = Base.lproj;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
BFABC4391E4BE794006036C6 /* Debug */ = { BFABC4611E4C1DD1006036C6 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
@@ -233,6 +319,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
@@ -256,17 +343,19 @@
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
}; };
name = Debug; name = Debug;
}; };
BFABC43A1E4BE794006036C6 /* Release */ = { BFABC4621E4C1DD1006036C6 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
@@ -282,6 +371,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -297,69 +387,121 @@
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
}; };
name = Release; name = Release;
}; };
BFABC43C1E4BE794006036C6 /* Debug */ = { BFABC4641E4C1DD1006036C6 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = SwiftHaskell/Info.plist; DEFINES_MODULE = YES;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; DYLIB_COMPATIBILITY_VERSION = 1;
LIBRARY_SEARCH_PATHS = ( DYLIB_CURRENT_VERSION = 1;
"$(inherited)", DYLIB_INSTALL_NAME_BASE = "@rpath";
"$(PROJECT_DIR)/build", 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)"; 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; 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; 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; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = SwiftHaskell/Info.plist; INFOPLIST_FILE = SwiftHaskell/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/build",
);
PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell; PRODUCT_BUNDLE_IDENTIFIER = net.nanotechcorp.SwiftHaskell;
PRODUCT_NAME = "$(TARGET_NAME)"; 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; name = Release;
}; };
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
BFABC4291E4BE794006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */ = { BFABC4551E4C1DD1006036C6 /* Build configuration list for PBXProject "SwiftHaskell" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
BFABC4391E4BE794006036C6 /* Debug */, BFABC4611E4C1DD1006036C6 /* Debug */,
BFABC43A1E4BE794006036C6 /* Release */, BFABC4621E4C1DD1006036C6 /* Release */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
BFABC43B1E4BE794006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */ = { BFABC4631E4C1DD1006036C6 /* Build configuration list for PBXNativeTarget "SwiftAppLibrary" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
BFABC43C1E4BE794006036C6 /* Debug */, BFABC4641E4C1DD1006036C6 /* Debug */,
BFABC43D1E4BE794006036C6 /* Release */, BFABC4651E4C1DD1006036C6 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BFABC4E51E4D781D006036C6 /* Build configuration list for PBXNativeTarget "SwiftHaskell" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BFABC4E61E4D781D006036C6 /* Debug */,
BFABC4E71E4D781D006036C6 /* Release */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = BFABC4261E4BE794006036C6 /* Project object */; rootObject = BFABC4521E4C1DD1006036C6 /* Project object */;
} }
-28
View File
@@ -1,28 +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 argc = CommandLine.argc
var argv = Optional.some(CommandLine.unsafeArgv)
hs_init(&argc, &argv)
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 */
+16
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
module SwiftHaskell {
header "Main_stub.h"
export *
}
+11 -7
View File
@@ -13,13 +13,17 @@ build-type: Simple
extra-source-files: extra-source-files:
cabal-version: >=1.10 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 source-repository head
type: git type: git
location: https://github.com/nanotech/swift-haskell-tutorial 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
Executable
+33
View File
@@ -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/
+106
View File
@@ -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
-12
View File
@@ -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/
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB