If you substitute the word “hack” for “goodness”, you’re probably closer to the truth. Here’s what I did. Thought I’d share. Caveat programmer/ hackteur.
Building a Custom Module
I created a Packages directory on Ubuntu (/home/erica/Packages). In it I built a TestPackage subdirectory and added TestPackage/Sources, along with a Package.swift file and a Makefile:
% ls
./ ../ Makefile Package.swift Sources/
The text-based Package.swift file specifies the name of the package, and that’s about all. When you make your own module, substitute a different name as needed.
% cat Package.swift
import PackageDescription
let package = Package(
name: "TestPackage"
)
I also created a really bottom-of-the-barrel Makefile. You can use this without edits.
all:
swift build
git init ; git add . ; git commit -m "Commit" ; git tag 1.0.0
clean:
rm -rf .build
rm -rf .git
rm *~ Sources/*~
Notice how this builds the sources and then establishes a git repo. You need that for accessing the module. I went with a brute force “clean everything, build everything” approach to keep things simple.
Adding Content
In the Sources directory, I built a minimal test file. You can replace this with any file name and file content you want (except main.swift) and can add any number of files as desired for your module.
% ls
./ ../ testpackage.swift
% cat testpackage.swift
public let testString = "Test Package is Installed"
public func testFunc() -> String {
return testString
}
Then I ran make in the main TestPackage folder to build the module:
% make
swift build
Compiling Swift Module 'TestPackage' (1 sources)
Linking Library: .build/debug/TestPackage.a
git init ; git add . ; git commit -m "Commit" ; git tag 1.0.0
Initialized empty Git repository in /home/erica/Packages/TestPackage/.git/
[master (root-commit) 9f41c4e] Commit
15 files changed, 97 insertions(+)
create mode 100644 .build/debug/TestPackage.a
create mode 100644 .build/debug/TestPackage.o/Sources/testpackage.swift.o
create mode 100644 .build/debug/TestPackage.o/TestPackage/master.swiftdeps
create mode 100644 .build/debug/TestPackage.o/TestPackage/output-file-map.json
create mode 100644 .build/debug/TestPackage.o/TestPackage/testpackage.d
create mode 100644 .build/debug/TestPackage.o/TestPackage/testpackage.swiftdeps
create mode 100644 .build/debug/TestPackage.o/TestPackage/testpackage~partial.swiftdoc
create mode 100644 .build/debug/TestPackage.o/TestPackage/testpackage~partial.swiftmodule
create mode 100644 .build/debug/TestPackage.o/build.db
create mode 100644 .build/debug/TestPackage.o/llbuild.yaml
create mode 100644 .build/debug/TestPackage.swiftdoc
create mode 100644 .build/debug/TestPackage.swiftmodule
create mode 100644 Makefile
create mode 100644 Package.swift
create mode 100644 Sources/testpackage.swift
Consuming a Module
It’s just as simple to build an application that imports and utilizes a Swift module. In my personal Dev folder, I created a new directory. Its structure is basically identical to the module one:
% ls
./ ../ Makefile Package.swift Sources/
The Makefile here is slightly different, as you see in the following. I decided to name my target “myutility” but obviously, you can change that to whatever name you like. Other than that, you should be able to preserve this as-is.
TARGET=myutility
all:
swift build
install:
cp .build/debug/$(TARGET) .
clean :
rm -rf Packages
rm -rf .build
rm *~ Sources/*~ $(TARGET)
The Package.swift file looks like this from the consumer end, using the same target name for the package results. Since this example uses a local module, a dependency url points to the module directory. Make sure that url matches the folder for your module.
% cat Package.swift
import PackageDescription
let package = Package (
name: "myutility",
dependencies: [
.Package(url: "/home/erica/Packages/TestPackage", majorVersion:1)
]
)
Once again, you can stick anything you like in the Sources folder, but here I went with a main.swift.
% cd Sources
% ls
./ ../ main.swift
% cat main.swift
import TestPackage
print("About to run tests on TestPackage")
print("Base test string: ", testString)
print("Module-specific test string: ", TestPackage.testString)
print("Test function", testFunc())
%
And go…
To conclude, I built and “installed” the application and ran it.
% make
swift build
Cloning Packages/TestPackage
Using version 1.0.0 of package TestPackage
Compiling Swift Module 'TestPackage' (1 sources)
Linking Library: .build/debug/TestPackage.a
Compiling Swift Module 'myutility' (1 sources)
Linking Executable: .build/debug/myutility
% make install
cp .build/debug/myutility .
% ./myutility
About to run tests on TestPackage
Base test string: Test Package is Installed
Module-specific test string: Test Package is Installed
Test function Test Package is Installed
And next?
So I’m still trying to figure out how to build modules around normal C-code that isn’t system provided (that is, through /usr/include, /usr/lib stuff). If you have any tips for creating a basic module around cc -c / ar rvs / etc, please let me know.
I’ll probably hook into the built-in libraries using dependencies but I’d like to write some raw C utilities too.
Update: To be clear, I’m not trying to link to /usr/lib. When I build my own .o/.a files, I cannot get Swift to properly link to them as the search path for libraries doesn’t include local files.