@@ -116,14 +116,14 @@ https://github.com/apple/example-package-fisheryates
116
116
117
117
## Requiring System Libraries
118
118
119
- You can link against system libraries using the package manager. To do so, there
120
- needs to be a special package for each system library that contains a modulemap
121
- for that library. Such a wrapper package does not contain any code of its own .
119
+ You can link against system libraries using the package manager. To do so, you'll
120
+ need to add a special ` target ` of type ` .systemLibrary ` , and a ` module. modulemap`
121
+ for each system library you're using .
122
122
123
- Let's see an example of using [ libgit2] ( https://libgit2.github.com ) from an
124
- executable.
123
+ Let's see an example of adding [ libgit2] ( https://libgit2.github.com ) as a
124
+ dependency to an executable target .
125
125
126
- First, create a directory called ` example ` , and initialize it as a package that
126
+ Create a directory called ` example ` , and initialize it as a package that
127
127
builds an executable:
128
128
129
129
$ mkdir example
@@ -140,87 +140,124 @@ print(options)
140
140
```
141
141
142
142
To ` import Clibgit ` , the package manager requires that the libgit2 library has
143
- been installed by a system packager (eg. ` apt ` , ` brew ` , ` yum ` , etc.). The
143
+ been installed by a system packager (eg. ` apt ` , ` brew ` , ` yum ` , ` nuget ` , etc.). The
144
144
following files from the libgit2 system-package are of interest:
145
145
146
146
/usr/local/lib/libgit2.dylib # .so on Linux
147
147
/usr/local/include/git2.h
148
148
149
- Swift packages that provide module maps for system libraries are handled
150
- differently from regular Swift packages.
149
+ ** Note:** the system library may be located elsewhere on your system, such as:
150
+ - ` /usr/ ` , or ` /opt/homebrew/ ` if you're using Homebrew on an Apple Silicon Mac.
151
+ - ` C:\vcpkg\installed\x64-windows\include ` on Windows, if you're using ` vcpkg ` .
152
+ On most Unix-like systems, you can use ` pkg-config ` to lookup where a library is installed:
151
153
152
- Note that the system library may be located elsewhere on your system, such as
153
- ` /usr/ ` rather than ` /usr/local/ ` .
154
+ example$ pkg-config --cflags libgit2
155
+ -I /usr/local/libgit2/1.6.4/include
154
156
155
- Create a directory called ` Clibgit ` next to the ` example ` directory and
156
- initialize it as a package that builds a system module:
157
157
158
- example$ cd ..
159
- $ mkdir Clibgit
160
- $ cd Clibgit
161
- Clibgit$ swift package init --type empty
162
-
163
- This creates a ` Package.swift ` file in the directory.
164
- Edit ` Package.swift ` and add ` pkgConfig ` parameter:
158
+ ** First, let's define the ` target ` in the package description** :
165
159
166
160
``` swift
167
- // swift-tools-version:5.3
161
+ // swift-tools-version: 5.8
162
+ // The swift-tools-version declares the minimum version of Swift required to build this package.
168
163
169
164
import PackageDescription
170
165
171
166
let package = Package (
172
- name : " Clibgit" ,
173
- pkgConfig : " libgit2"
167
+ name : " example" ,
168
+ targets : [
169
+ // systemLibrary is a special type of build target that wraps a system library
170
+ // in a target that other targets can require as their depencency.
171
+ .systemLibrary (
172
+ name : " Clibgit" ,
173
+ pkgConfig : " libgit2" ,
174
+ providers : [
175
+ .brew ([" libgit2" ]),
176
+ .apt ([" libgit2-dev" ])
177
+ ]
178
+ )
179
+ ]
174
180
)
181
+
175
182
```
176
183
177
- The ` pkgConfig ` parameter helps SwiftPM in figuring out the include and library
178
- search paths for the system library. Note: If you don't want to use the ` pkgConfig `
179
- parameter you can pass the path of a directory containing the library using the
180
- ` -L ` flag in commandline when building your app:
184
+ ** Note:** For Windows-only packages ` pkgConfig ` should be omitted as
185
+ ` pkg-config ` is not expected to be available. If you don't want to use the
186
+ ` pkgConfig ` parameter you can pass the path of a directory containing the
187
+ library using the ` -L ` flag in the command line when building your package
188
+ instead.
181
189
182
190
example$ swift build -Xlinker -L/usr/local/lib/
183
191
184
- Create a ` module.modulemap ` file so it consists of the following:
192
+ Next, create a directory ` Sources/Clibgit ` in your ` example ` project, and
193
+ add a ` module.modulemap ` and the header file to it:
185
194
186
195
module Clibgit [system] {
187
- header "/usr/local/include/ git2.h"
196
+ header "git2.h"
188
197
link "git2"
189
198
export *
190
199
}
191
200
201
+ The header file should look like this:
202
+
203
+ ``` c
204
+ // git2.h
205
+ #pragma once
206
+ #include < git2.h>
207
+ ```
208
+
209
+ ** Note:** Alternatively, you can provide an absolute path to ` git2.h ` provided
210
+ by the library in the ` modile.modulemap ` . However, doing so might break
211
+ cross-platform compatibility of your project.
212
+
192
213
> The convention we hope the community will adopt is to prefix such modules
193
214
> with ` C ` and to camelcase the modules as per Swift module name conventions.
194
215
> Then the community is free to name another module simply ` libgit ` which
195
216
> contains more “Swifty” function wrappers around the raw C interface.
196
217
197
- Packages are Git repositories, tagged with semantic versions, containing a
198
- ` Package.swift ` file at their root. Initializing the package created a
199
- ` Package.swift ` file, but to make it a usable package we need to initialize a
200
- Git repository with at least one version tag:
218
+ The ` example ` directory structure should look like this now:
201
219
202
- Clibgit$ git init
203
- Clibgit$ git add .
204
- Clibgit$ git commit -m "Initial Commit"
205
- Clibgit$ git tag 1.0.0
220
+ .
221
+ ├── Package.swift
222
+ └── Sources
223
+ ├── Clibgit
224
+ │ ├── git2.h
225
+ │ └── module.modulemap
226
+ └── main.swift
206
227
207
- Now to use the Clibgit package we must declare our dependency in our example
208
- app’s ` Package.swift ` :
228
+ At this point, your system library target is fully defined, and you can now use
229
+ that target as a dependency in other targets in your ` Package.swift ` , like this :
209
230
210
231
``` swift
232
+
211
233
import PackageDescription
212
234
213
235
let package = Package (
214
236
name : " example" ,
215
- dependencies : [
216
- .package (url : " ../Clibgit" , from : " 1.0.0" )
237
+ targets : [
238
+ .executableTarget (
239
+ name : " example" ,
240
+
241
+ // example executable requires "Clibgit" target as its dependency.
242
+ // It's a systemLibrary target defined below.
243
+ dependencies : [" Clibgit" ],
244
+ path : " Sources"
245
+ ),
246
+
247
+ // systemLibrary is a special type of build target that wraps a system library
248
+ // in a target that other targets can require as their depencency.
249
+ .systemLibrary (
250
+ name : " Clibgit" ,
251
+ pkgConfig : " libgit2" ,
252
+ providers : [
253
+ .brew ([" libgit2" ]),
254
+ .apt ([" libgit2-dev" ])
255
+ ]
256
+ )
217
257
]
218
258
)
219
- ```
220
259
221
- Here we used a relative URL to speed up initial development. If you push your
222
- module map package to a public repository you must change the above URL
223
- reference so that it is a full, qualified Git URL.
260
+ ```
224
261
225
262
Now if we type ` swift build ` in our example app directory we will create an
226
263
executable:
@@ -231,6 +268,8 @@ executable:
231
268
git_repository_init_options(version: 0, flags: 0, mode: 0, workdir_path: nil, description: nil, template_path: nil, initial_head: nil, origin_url: nil)
232
269
example$
233
270
271
+ ### Requiring a System Library Without ` pkg-config `
272
+
234
273
Let’s see another example of using [ IJG’s JPEG library] ( http://www.ijg.org )
235
274
from an executable, which has some caveats.
236
275
@@ -250,22 +289,16 @@ let jpegData = jpeg_common_struct()
250
289
print (jpegData)
251
290
```
252
291
253
- Install JPEG library using a system packager, e.g, ` $ brew install jpeg `
254
-
255
- Create a directory called ` CJPEG ` next to the ` example ` directory and
256
- initialize it as a package that builds a system module:
257
-
258
- example$ cd ..
259
- $ mkdir CJPEG
260
- $ cd CJPEG
261
- CJPEG$ swift package init --type empty
292
+ Install the JPEG library, on macOS you can use Homebrew package manager: ` brew install jpeg ` .
293
+ ` jpeg ` is a keg-only formula, meaning it won't be linked to ` /usr/local/lib ` ,
294
+ and you'll have to link it manually at build time.
262
295
263
- This creates ` Package.swift ` file in the directory.
264
- Create a ` module.modulemap ` file so it consists of the following :
296
+ Just like in the previous example, run ` mkdir Sources/CJPEG ` and add the
297
+ following ` module.modulemap ` :
265
298
266
299
module CJPEG [system] {
267
300
header "shim.h"
268
- header "/usr/local/include/jpeglib.h"
301
+ header "/usr/local/opt/jpeg/ include/jpeglib.h"
269
302
link "jpeg"
270
303
export *
271
304
}
@@ -279,31 +312,34 @@ This is because `jpeglib.h` is not a correct module, that is, it does not contai
279
312
the required line ` #include <stdio.h> ` . Alternatively, you can add ` #include <stdio.h> `
280
313
to the top of jpeglib.h to avoid creating the ` shim.h ` file.
281
314
282
- Create a Git repository and tag it:
283
-
284
- CJPEG$ git init
285
- CJPEG$ git add .
286
- CJPEG$ git commit -m "Initial Commit"
287
- CJPEG$ git tag 1.0.0
288
-
289
315
Now to use the CJPEG package we must declare our dependency in our example
290
316
app’s ` Package.swift ` :
291
317
292
318
``` swift
319
+
293
320
import PackageDescription
294
321
295
322
let package = Package (
296
323
name : " example" ,
297
- dependencies : [
298
- .package (url : " ../CJPEG" , from : " 1.0.0" )
324
+ targets : [
325
+ .executableTarget (
326
+ name : " example" ,
327
+ dependencies : [" CJPEG" ],
328
+ path : " Sources"
329
+ ),
330
+ .systemLibrary (
331
+ name : " CJPEG" ,
332
+ providers : [
333
+ .brew ([" jpeg" ])
334
+ ])
299
335
]
300
336
)
301
337
```
302
338
303
339
Now if we type ` swift build ` in our example app directory we will create an
304
340
executable:
305
341
306
- example$ swift build -Xlinker -L/usr/local/lib/
342
+ example$ swift build -Xlinker -L/usr/local/jpeg/lib
307
343
…
308
344
example$ .build/debug/example
309
345
jpeg_common_struct(err: nil, mem: nil, progress: nil, client_data: nil, is_decompressor: 0, global_state: 0)
@@ -353,9 +389,9 @@ we hope that system libraries and system packagers will provide module maps and
353
389
thus this component of the package manager will become redundant.
354
390
355
391
* Notably* the above steps will not work if you installed JPEG and JasPer with
356
- [ Homebrew] ( http://brew.sh ) since the files will be installed to ` /usr/local ` . For
357
- now adapt the paths, but as said, we plan to support basic relocations like
358
- these.
392
+ [ Homebrew] ( http://brew.sh ) since the files will be installed to ` /usr/local ` on
393
+ Intel Macs, or /opt/homebrew on Apple silicon Macs. For now adapt the paths,
394
+ but as said, we plan to support basic relocations like these.
359
395
360
396
### Module Map Versioning
361
397
@@ -471,9 +507,9 @@ In case the current Swift version doesn't match any version-specific manifest,
471
507
the package manager will pick the manifest with the most compatible tools
472
508
version. For example, if there are three manifests:
473
509
474
- ` Package.swift ` (tools version 3.0)
475
- ` [email protected] ` (tools version 4.0)
476
- ` [email protected] ` (tools version 4.2)
510
+ ` Package.swift ` (tools version 3.0)
511
+ ` [email protected] ` (tools version 4.0)
512
+ ` [email protected] ` (tools version 4.2)
477
513
478
514
The package manager will pick
` Package.swift ` on Swift 3,
` [email protected] ` on
479
515
Swift 4, and
` [email protected] ` on Swift 4.2 and above because its tools
0 commit comments