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.
One Comment
Please make a post when you figure this out. Requiring consumers of modules to build from source is a first step, but it’s a mess for large projects.