Constructing a customized C library utilizing SPM
You should utilize the Swift Bundle Supervisor to create C household primarily based supply information (C, C++, Goal-C and Goal-C++) and ship them as standalone elements. If you do not know a lot concerning the Swift Bundle Supervisor, you need to learn my complete tutorial about how SPM works. 📦
The one factor that you could setup a library is a normal Bundle.swift
manifest file with a barely altered listing construction to assist header information. Let’s make a MyPoint
library.
import PackageDescription
let bundle = Bundle(
title: "MyPoint",
merchandise: [
.library(name: "MyPoint", targets: ["MyPoint"]),
],
targets: [
.target(name: "MyPoint"),
]
)
Every little thing that you simply put into the header file might be publicly accessible for different builders to make use of, the implementation particulars are going to be situated immediately beneath the Sources/[target]/
listing, however it’s a must to create an extra embody
folder to your headers. Let’s make a MyPoint.h
file beneath the Sources/MyPoint/embody
path with the next contents.
struct MyPoint {
int x;
int y;
};
We have simply outlined the general public interface for our library. Now if you happen to attempt to compile it by means of the swift construct
command, it will complain that the challenge is lacking some supply information. We will simply repair this by creating an empty MyPoint.c
file beneath the Sources/MyPoint
listing.
While you import an area header file to make use of in your implementation code, you possibly can skip the “embody” path and easily write #embody "MyPoint.h"
. You could possibly additionally put every kind of C household elements into this challenge, this technique works with C++, Goal-C and even Goal-C++ information.
You could possibly additionally place header information subsequent to the implementation supply code, however in that case the system will not have the ability to auto-locate your public (umbrella) header information, so that you additionally must create a modulemap file and supply the right location of your headers explicitly. Should you use the construction with the embody listing SPM will generate all the pieces for you robotically.
Congratulations, you simply shipped your first C code with Swift Bundle Supervisor. 🥳
Interacting with C libraries utilizing Swift
We’ll create a model new Swift bundle to construct an executable software primarily based on the beforehand created C library. With a view to use an area bundle you possibly can merely specify it as with the trail argument beneath the dependencies in your Bundle.swift
manifest file.
import PackageDescription
let bundle = Bundle(
title: "Pattern",
merchandise: [
.executable(name: "Sample", targets: ["Sample"]),
],
dependencies: [
.package(path: "../MyPoint")
],
targets: [
.target(name: "Sample", dependencies: [
.product(name: "MyPoint", package: "MyPoint"),
]),
]
)
This time we’re going to use the MyPoint library as an area dependency, however in fact you possibly can handle and publish your personal libraries utilizing a git repository someplace within the cloud. Subsequent we should always create our Sources/Pattern/primary.swift
file, import the library and write some code.
import MyPoint
let p = MyPoint(x: 4, y: 20)
print("Hi there, world!", p.x, p.y)
If each packages can be found domestically, ensure you place them subsequent to one another, then all the pieces ought to work like a allure. You may open the Pattern challenge manifest file utilizing Xcode as nicely, the IDE can resolve bundle dependencies robotically for you, however if you happen to choose the command line, you should use the swift run
command to compile & run the executable goal.
With this method you possibly can import the MyPoint
module from every other Swift bundle and use the accessible public elements from it. You simply have so as to add this module as a dependency, by the way in which you possibly can even name this module from one other C (C++, ObjC, Objc++) challenge made with SPM. 😎
How one can use C system libraries from Swift?
There are millions of accessible instruments which you could set up in your working system (Linux, macOS) with a bundle supervisor (apt, brew). For instance there’s the well-known curl command line software and library, that can be utilized for transferring information from or to a server. In different phrases, you may make HTTP requests with it, simply kind curl "https://www.apple.com/"
right into a terminal window.
These system elements are normally constructed round libraries. In our case curl comes with libcurl, the multiprotocol file switch library. Generally you may need to use these low degree elements (normally written in C) in your software, however how will we add them as a dependency? 🤔
The reply is straightforward, we are able to outline a brand new systemLibrary goal in our bundle manifest file.
import PackageDescription
let bundle = Bundle(
title: "Pattern",
merchandise: [
.executable(name: "Sample", targets: ["Sample"]),
],
dependencies: [
.package(path: "../MyPoint")
],
targets: [
.systemLibrary(
name: "libcurl",
providers: [
.apt(["libcurl4-openssl-dev"]),
.brew(["curl"])
]
),
.goal(title: "Pattern", dependencies: [
.product(name: "MyPoint", package: "MyPoint"),
.target(name: "libcurl"),
]),
]
)
Contained in the Bundle.swif
t file you possibly can set the suppliers for the library (corresponding to brew for macOS or aptitude for a lot of Linux distributions). Sadly you continue to must manually set up these packages, as a result of SPM will not do that for you, consider it as “only a reminder” for now… 😅
This can enable us to create a customized modulemap file with further headers (common or umbrella) and linker flags inside our challenge folder. First, we should always add the next modulemap definition to the Sources/libcurl/module.modulemap
file. Please create the libcurl listing, if wanted.
module libcurl [system] {
header "libcurl.h"
hyperlink "curl"
export *
}
The idea of modules are coming from (clang) LLVM, I extremely advocate checking the linked article if you wish to know extra about modulemaps. This fashion we inform the compiler that we need to construct a module primarily based on the curl library, therefore we hyperlink curl
. We additionally need to present our customized header file to make some further stuff accessible or extra handy. Individuals normally name these header information shims, umbrella headers or bridging headers.
An umberlla header is the primary header file for a framework or library. A bridging header permits us to make use of two languages in the identical software. The shim header works across the limitation that module maps should include absolute or native paths. All of them exposes APIs from a library or language to a different, they’re very comparable, however they don’t seem to be the identical idea. 🙄
In our case we will create a libcurl.h header file contained in the Sources/libcurl
folder. The module map merely refers to this header file. This is what we will place inside it.
typedef size_t (*curl_func)(void * ptr, size_t dimension, size_t num, void * ud);
CURLcode curl_easy_setopt_string(CURL *curl, CURLoption possibility, const char *param) {
return curl_easy_setopt(curl, possibility, param);
}
CURLcode curl_easy_setopt_func(CURL *deal with, CURLoption possibility, curl_func param) {
return curl_easy_setopt(deal with, possibility, param);
}
CURLcode curl_easy_setopt_pointer(CURL *deal with, CURLoption possibility, void* param) {
return curl_easy_setopt(deal with, possibility, param);
}
This code comes from the archived SoTS/CCurl repository, however if you happen to test the shim file contained in the Kitura/CCurl bundle, you will discover a just about comparable method with much more handy helpers.
The principle purpose why we’d like these capabilities is that variadic capabilities cannot be imported by Swift (but), so we have now to wrap the curl_easy_setopt
calls, so we’ll have the ability to use it from Swift.
Okay, let me present you how one can write a low-level curl name utilizing the libcurl & Swift.
import Basis
import MyPoint
import libcurl
class Response {
var information = Knowledge()
var physique: String { String(information: information, encoding: .ascii)! }
}
var response = Response()
let deal with = curl_easy_init()
curl_easy_setopt_string(deal with, CURLOPT_URL, "http://www.google.com")
let pointerResult = curl_easy_setopt_pointer(deal with, CURLOPT_WRITEDATA, &response)
guard pointerResult == CURLE_OK else {
fatalError("Couldn't set response pointer")
}
curl_easy_setopt_func(deal with, CURLOPT_WRITEFUNCTION) { buffer, dimension, n, reference in
let size = dimension * n
let information = buffer!.assumingMemoryBound(to: UInt8.self)
let p = reference?.assumingMemoryBound(to: Response.self).pointee
p?.information.append(information, rely: size)
return size
}
let ret = curl_easy_perform(deal with)
guard ret == CURLE_OK else {
fatalError("One thing went fallacious with the request")
}
curl_easy_cleanup(deal with)
print(response.physique)
I do know, I do know. This appears horrible for the primary sight, however sadly C interoperability is all about coping with pointers, unfamiliar varieties and reminiscence addresses. Anyway, here is what occurs within the code snippet. First we have now to outline a response object that may maintain the information coming from the server as a response. Subsequent we name the system funtions from the curl library to create a deal with and set the choices on it. We merely present the request URL as a string, we move the consequence pointer and a write perform that may append the incoming information to the storage when one thing arrives from the server. Lastly we carry out the request, test for errors and cleanup the deal with.
It isn’t so dangerous, however nonetheless it appears nothing such as you’d anticipate from Swift. It is only a primary instance I hope it will enable you to to grasp what is going on on beneath the hood and the way low degree C-like APIs can work in Swift. If you wish to observe you need to attempt to try the Kanna library and parse the response utilizing a customized libxml2 wrapper (or you possibly can examine a SQLite3 wrapper). 🤓
The system library goal function is a pleasant method of wrapping C [system] modules with SPM. You may learn extra about it on the official Swift boards. In case you are nonetheless utilizing the outdated system library bundle kind format, please migrate, because it’s deprecated and it will be fully eliminated in a while.