Add SwiftPM and initial linux support for swift - #55
Open
hiimtmac wants to merge 10 commits into
Open
Conversation
hiimtmac
force-pushed
the
spm
branch
4 times, most recently
from
July 12, 2026 00:23
78b9c67 to
2415990
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TailscaleKit currently only builds via Xcode/xcframework, which requires macOS and doesn't work for server-side or Linux-hosted Swift consumers. This adds a SwiftPM path alongside the existing Xcode build. I tried to do the most limited code change to achieve this goal without breaking the existing xcframework path (
make teststill passes).SwiftPM doesn't build the underlying Go/C library the way the Xcode project does. Instead,
libtailscale.ais prebuilt per platform/arch and shipped as aTailscaleKit.artifactbundle, which the package'sCTailscalebinaryTarget consumes. Downstream consumers of the SwiftPM package never need Go or a C toolchain, only the artifactbundle.CurrentValueSubject, and Combine isn't available on LinuxURLSessionConfiguration.tailscaleSession/proxyViawhich usesNetwork.frameworkis Apple-only, now#if canImport(Network)so it is not included on LinuxPlatformShims.swiftadds aSystemnamespace forclose/read/write, resolved toDarwin/Glibc/Musldepending on platform. Some types declare their ownclose()/read()methods which shadow the global libc functions of the same name.devcontainer/swift/is a ready-to-use Linux dev environment (Swift + Go toolchains) for exercising the Linux SwiftPM path without setting up cross-compilation locallyTesting
In the Xcode build for testing,
libtstestcontrol.alives in a separate test-support framework, so it never toucheslibtailscale.a. SwiftPM's test target links everything into one flat binary, and since both archives share the same Go runtime, they carry duplicate runtime symbols that collide when linked together.fix-tstestcontrol-archive.shrewrites those symbols inlibtstestcontrol.aso they no longer clash (thanks Claude! 🤖), andCTstestControl(a systemLibrary target) exposes the fixed archive to the test target as a C dependency. This is only needed forswift test, notswift build/swift run. Tests also migrated to swift testing.TailscaleKitCLI
swift/Examples/TailscaleKitCLIis a minimalswift-argument-parserCLI that brings up a node and lists tailnet devices viaLocalAPIClientto test that TailscaleKit works through plain SwiftPM, including on Linux, with no Xcode involved. Verified end-to-end against a real Tailscale account (output sanitized).URLSession on Linux
Foundation's
URLSessionon Linux is the libcurl-backedswift-corelibs-foundationimplementation rather than Apple's CFNetwork, and has historically been less robust for server-side use. If useful, I'd be happy to switch the Linux-side HTTP usage (LocalAPI client, IPN-bus event stream) toAsyncHTTPClientinstead. Flagging this as a possible follow-up rather than doing it in this PR, likely with a trait/platform gate so that it doesn't pull in the dependency outside of Linux.