Skip to content

Commit

Permalink
Documentation updates.
Browse files Browse the repository at this point in the history
  • Loading branch information
RockfordWei committed Mar 14, 2017
1 parent 53f4145 commit c462125
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 26 deletions.
120 changes: 94 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
# Swift调用C语言自建函数库的方法
# C Module for Swift, Swift Script and Dynamic Library Call [简体中文](README.zh_CN.md)

Rockford Wei,2017-01-17

本程序示范了如何用Swift调用自定义C语言模块的方法。您可以直接下载本程序,或者按照以下教程逐步完成。
This project demonstrates how to call a customized C library in your swift project.
You can clone it from github, or follow the instruction below to generate it step by step.

## 简介
Furthermore, this demo also shows how to use swift as a script, with examples of calling C api as dynamic libraries.
This practice makes it possible to patch hot fixes for server side swift without stopping the server, theoretically.

示范程序中有一个C语言的源程序CSwift.C和一个头文件CSwift.h,我们的目标是构造一个CSwift的函数库,能够让swift源程序执行CSwift程序中的函数。
## Introduction

## 快速上手
There are two C files: CSwift.c and CSwift.h. The objective is to build a CSwift library and export it to Swift.

本程序需要Swift 3.0以上版本。[:clipboard:](https://github.com/RockfordWei/)
## Quick Start

### 下载、编译和测试
Please compile this project with Swift 3.0 toolchain.

### Build & Test

```
$ git clone https://github.com/RockfordWei/CSwift.git
$ cd CSwift
$ swift build
$ swift test
```
源程序采用C语言写成,测试程序则是Swift语言编写。因此如果通过测试,则恭喜您,已经成功实现了Swift语言调用C语言的整个过程。

## 详细步骤
The sources are written in C while the test program is written in Swift. So if all tests passed, then congratulations! You've already mastered the process to call C api in a Swift source.

## Walk Through

您可以完全不依赖所有上述内容,而一步一步从零开始制作C函数库和调用C库的Swift代码:
However, even without the sources above, you can still start everything from blank:

### 构造空白的函数库
### Start From An Empty Folder:

仍然假定函数库名称为CSwift。首先找一个空白目录,然后执行:
Assume the objective library is still CSwift, then find an empty folder and try these commands in a terminal:

```
$ mkdir CSwift
Expand All @@ -47,11 +52,15 @@ $ echo > include/CSwift.h
$ cd ..
```

细心的读者会发现,上面的bash 命令行在CSwift 文件夹下面建立了第二个CSwift文件夹,但是使用了不同的`swift package`了命令。第一个命令是“创建swift空白项目,而且项目类型是系统模块”;而第二个命令是“创建swift 空白项目,项目类型是函数库”。这种做法主要是为了能够在同一个项目中用Swift去测试C语言的模块。其次,在第二个CSwift 子目录下,还建立了一个include 文件夹,并分别建立了两个空白源程序文件 CSwift.c 和 CSwift.h
You may notice that the above commands make a second CSwift folder inside the first one, but with different `swift package` commmand line.
The first `swift package` is to create a blank swift project and will build as an system module; The second one, however, is just to create a common swift library.
The purpose of such a trick is to create testing scripts for system module.

Besides, it also creates an `include` folder under the second CSwift directory, with two blank source file place holder `CSwift.c` and `CSwift.h`.

### Module Map

下一步是修理一下目标的模块映射表。请把module.modulemap修改为如下程序:
Now it is time to modify the module map file, check this out:

``` swift
module CSwift [system] {
Expand All @@ -61,9 +70,9 @@ module CSwift [system] {
}
```

### C模块编程
### C Code

好了,现在请编辑刚才在第二个CSwift文件夹下面的建立两个C语言文件:CSwift.c和CSwift.h,内容如下:
Then you can customize the C code as demo below:

#### CSwift/CSwift/include/CSwift.h

Expand All @@ -79,11 +88,11 @@ extern int c_add(int, int);
int c_add(int a, int b) { return a + b ; }
```

到此为止,C语言函数库就应该准备好了。
Up to now, C api is ready to call. So far so good.

### Swift 程序调用
### Call C API in Swift

请修改Tests/CSwiftTests/CSwiftTests.swift文件,内容如下:
The following code is a good example to show how to call your C api statically, as `Tests/CSwiftTests/CSwiftTests.swift`:

``` swift

Expand All @@ -92,10 +101,11 @@ import XCTest

class CSwiftTests: XCTestCase {
func testExample() {
// 测试调用 C 函数
// Test call "x + y" from your C library
let three = c_add(1, 2)
XCTAssertEqual(three, 3)
// 测试调用 C 语言的符号

// Test macro exports
XCTAssertEqual(C_TEN, 10)
}

Expand All @@ -109,17 +119,75 @@ class CSwiftTests: XCTestCase {

```

### 测试
### Test

最后一步最简单,直接执行:
The final step is the easiest one - build & test:

```
$ swift build
$ swift test
```

如果没有问题,那就一切OK了!
If success, then perfect!

## Swift as Script and Call C lib dynamically

Beside the above classic static build & run, Swift also provide an interpreter to execute swift source as scripts, just like a playground in a terminal.
This project also makes an example for swift script, and even more, introduces how to call the same C api dynamically in such a script.

### Dynamic Link Library

Please check a swift script `dll.swift.script`, actually it is a common swift with no difference to any other swift sources:

``` swift
// First thing first, make sure your dll path is an dynamic library in an ABSOLUTE path.
// on Mac, the suffix is ".dylib"; on Linux, it is ".so"
guard let lib = dlopen(dllpath, RTLD_LAZY) else {
exit(0)
}

// declare the api prototype to call
typealias AddFunc = @convention(c) (CInt, CInt) -> CInt

// look up the function in the library
guard let c_add = dlsym(lib, "c_add") else {
dlclose(lib)
exit(0)
}

// attache the function to the real API address
let add = unsafeBitCast(c_add, to: AddFunc.self)

// call the C method, dynamically
let x = add(1, 2)
print(x)

// release resources
dlclose(lib)
```

### Run the Swift Script

This project also provides a bash script `dll.sh` to run the swift script above.
```
# step one, build the C library
swift build
# then test what OS it is: .dylib for apple and .so for linux
if swift --version|grep apple
then
SUFFIX=dylib
else
SUFFIX=so
fi
# generate the full path of new library.
DLL=$PWD/.build/debug/libCSwift.$SUFFIX
# run the swift script and call the libray.
swift dll.swift.script $DLL
```

## 其他
## More Info

如果您在使用Xcode,则需要使用`swift package generate-xcodeproj`
If Xcode is preferred, then try command `swift package generate-xcodeproj` before building.
184 changes: 184 additions & 0 deletions README.zh_CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Swift 调用 C 语言自建函数库 及 Swift 脚本和动态链接库的使用方法 [English](README.md)

Rockford Wei,2017-01-17

本程序示范了如何用Swift调用自定义C语言模块的方法。您可以直接下载本程序,或者按照以下教程逐步完成。
此外,示范程序还展示了如何使用 Swift 作为脚本运行,并且在脚本中调用C语言的动态链接库,使得服务器端Swift 开发实现热补丁。

## 简介

示范程序中有一个C语言的源程序CSwift.c和一个头文件CSwift.h,我们的目标是构造一个CSwift的函数库,能够让swift源程序执行CSwift程序中的函数。

## 快速上手

本程序需要Swift 3.0以上版本。

### 下载、编译和测试

```
$ git clone https://github.com/RockfordWei/CSwift.git
$ cd CSwift
$ swift build
$ swift test
```
源程序采用C语言写成,测试程序则是Swift语言编写。因此如果通过测试,则恭喜您,已经成功实现了Swift语言调用C语言的整个过程。

## 详细步骤

您可以完全不依赖所有上述内容,而一步一步从零开始制作C函数库和调用C库的Swift代码:

### 构造空白的函数库

仍然假定函数库名称为CSwift。首先找一个空白目录,然后执行:

```
$ mkdir CSwift
$ cd CSwift
$ swift package init --type=system-module
$ mkdir CSwift
$ cd CSwift
$ swift package init
$ mv Tests ..
$ mkdir include
$ mv ../module.modulemap inlcude/
$ rm Package.swift
$ rm -rf Sources
$ echo > CSwift.c
$ echo > include/CSwift.h
$ cd ..
```

细心的读者会发现,上面的bash 命令行在CSwift 文件夹下面建立了第二个CSwift文件夹,但是使用了不同的`swift package`了命令。第一个命令是“创建swift空白项目,而且项目类型是系统模块”;而第二个命令是“创建swift 空白项目,项目类型是函数库”。这种做法主要是为了能够在同一个项目中用Swift去测试C语言的模块。其次,在第二个CSwift 子目录下,还建立了一个include 文件夹,并分别建立了两个空白源程序文件 CSwift.c 和 CSwift.h

### Module Map

下一步是修理一下目标的模块映射表。请把module.modulemap修改为如下程序:

``` swift
module CSwift [system] {
header "CSwift.h"
link "CSwift"
export *
}
```

### C模块编程

好了,现在请编辑刚才在第二个CSwift文件夹下面的建立两个C语言文件:CSwift.c和CSwift.h,内容如下:

#### CSwift/CSwift/include/CSwift.h

``` c
extern int c_add(int, int);
#define C_TEN 10
```
#### CSwift/CSwift/CSwift.c
``` c
#include "include/CSwift.h"
int c_add(int a, int b) { return a + b ; }
```

到此为止,C语言函数库就应该准备好了。

### Swift 程序调用

请修改Tests/CSwiftTests/CSwiftTests.swift文件,内容如下:

``` swift

import XCTest
@testable import CSwift

class CSwiftTests: XCTestCase {
func testExample() {
// 测试调用 C 函数
let three = c_add(1, 2)
XCTAssertEqual(three, 3)
// 测试调用 C 语言的符号
XCTAssertEqual(C_TEN, 10)
}


static var allTests : [(String, (CSwiftTests) -> () throws -> Void)] {
return [
("testExample", testExample),
]
}
}

```

### 测试

最后一步最简单,直接执行:

```
$ swift build
$ swift test
```

如果没有问题,那就一切OK了!

## Swift 脚本解释器的使用以及动态链接库的调用

除了以上正文之外,Swift 还提供了直接以解释器执行 Swift脚本的方法,相当于在linux或mac命令行直接使用playground。
本文顺便利用这个脚本,同时说明一下动态链接库的使用方法。

### 动态链接库

本项目附带了一个特殊脚本dll.swift.script,内容为一个普通Swift 源程序:

``` swift
// 首先dllpath 一定是编译完成的C语言动态链接库,在mac上是.dylib文件,在linux上是.so文件
// 请注意dllpath 一定是绝对路径
guard let lib = dlopen(dllpath, RTLD_LAZY) else {
exit(0)
}

// 然后声明需要引用的函数类型
typealias AddFunc = @convention(c) (CInt, CInt) -> CInt

// 用dlsym方法真正获取目标函数的地址
guard let c_add = dlsym(lib, "c_add") else {
dlclose(lib)
exit(0)
}

// 将动态函数库函数地址绑定到刚才声明的函数类型上
let add = unsafeBitCast(c_add, to: AddFunc.self)

// 调用动态链接库
let x = add(1, 2)
print(x)

// 释放资源
dlclose(lib)
```

### 用Swift脚本调用动态链接库

附件中还包括了另外一个bash脚本dll.sh,供运行上述脚本并调用动态链接库
```
# 首先编译一下C函数库
swift build
# 然后判断操作系统类型,如果是苹果则使用.dylib,如果是Linux就用.so
if swift --version|grep apple
then
SUFFIX=dylib
else
SUFFIX=so
fi
# 合成动态函数库名称
DLL=$PWD/.build/debug/libCSwift.$SUFFIX
# 用swift 解释器调用脚本,并执行动态链接库调用
swift dll.swift.script $DLL
```

## 其他

如果您在使用Xcode,则需要使用`swift package generate-xcodeproj`
1 change: 1 addition & 0 deletions dll.swift.script
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#if os(Linux)
import SwiftGlibc
#else
import Darwin
#endif
Expand Down

0 comments on commit c462125

Please sign in to comment.