From fe723f9d1dd45b8bf3598aaa4c81597a8f500e08 Mon Sep 17 00:00:00 2001 From: Jonathan Nobels Date: Wed, 21 May 2025 12:56:49 -0400 Subject: [PATCH] swift: add iOS simulator support updates tailscale/tailscale#15802 This adds the required targets to build for the iOS simulator. We support both ios, ios-sim, and ios-fat framework targets. The sample app is updated to link in the ios-fat version so it can be run on any target device or sim. --- .gitignore | 4 +- Makefile | 18 +- swift/Examples/TailscaleKitHello/README.md | 6 +- .../project.pbxproj | 36 ++-- .../HelloFromTailscale_iOS_Sim.xcscheme | 78 ++++++++ swift/Makefile | 29 ++- swift/README.md | 57 +++--- swift/TailscaleKit.xcodeproj/project.pbxproj | 178 ++++++++++++++++++ .../TailscaleKit (Simulator).xcscheme | 67 +++++++ swift/TailscaleKit/TailscaleNode.swift | 10 +- swift/script/clangwrap-ios-sim-arm.sh | 13 ++ swift/script/clangwrap-ios-sim-x86.sh | 13 ++ .../script/{clangwrap.sh => clangwrap-ios.sh} | 0 13 files changed, 455 insertions(+), 54 deletions(-) create mode 100644 swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/xcshareddata/xcschemes/HelloFromTailscale_iOS_Sim.xcscheme create mode 100644 swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (Simulator).xcscheme create mode 100755 swift/script/clangwrap-ios-sim-arm.sh create mode 100755 swift/script/clangwrap-ios-sim-x86.sh rename swift/script/{clangwrap.sh => clangwrap-ios.sh} (100%) diff --git a/.gitignore b/.gitignore index fe42201..3812672 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,8 @@ libtailscale.so libtailscale.a libtailscale.h libtailscale.tar* -libtailscale_ios.a -libtailscale_ios.h +libtailscale_*.a +libtailscale_*.h /tstestcontrol/libtstestcontrol.a /tstestcontrol/libtstestcontrol.h diff --git a/Makefile b/Makefile index 6e9a579..8d90781 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,21 @@ libtailscale.a: go build -buildmode=c-archive libtailscale_ios.a: - GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap.sh go build -v -ldflags -w -tags ios -o libtailscale_ios.a -buildmode=c-archive + GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap-ios.sh go build -v -ldflags -w -tags ios -o libtailscale_ios.a -buildmode=c-archive + +libtailscale_ios_sim_arm64.a: + GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap-ios-sim-arm.sh go build -v -ldflags -w -tags ios -o libtailscale_ios_sim_arm64.a -buildmode=c-archive + +libtailscale_ios_sim_x86_64.a: + GOOS=ios GOARCH=amd64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap-ios-sim-x86.sh go build -v -ldflags -w -tags ios -o libtailscale_ios_sim_x86_64.a -buildmode=c-archive .PHONY: c-archive-ios c-archive-ios: libtailscale_ios.a ## Builds libtailscale_ios.a for iOS (iOS SDK required) +.PHONY: c-archive-ios-sim +c-archive-ios-sim: libtailscale_ios_sim_arm64.a libtailscale_ios_sim_x86_64.a ## Builds a fat binary for iOS (iOS SDK required) + lipo -create -output libtailscale_ios_sim.a libtailscale_ios_sim_x86_64.a libtailscale_ios_sim_arm64.a + .PHONY: c-archive c-archive: libtailscale.a ## Builds libtailscale.a for the target platform @@ -20,10 +30,8 @@ shared: ## Builds libtailscale.so for the target platform .PHONY: clean clean: ## Clean up build artifacts - rm -f libtailscale.a - rm -f libtailscale_ios.a - rm -f libtailscale.h - rm -f libtailscale_ios.h + rm -f libtailscale*.h + rm -f libtailscale*.a .PHONY: help help: ## Show this help diff --git a/swift/Examples/TailscaleKitHello/README.md b/swift/Examples/TailscaleKitHello/README.md index 1500909..839f9f8 100644 --- a/swift/Examples/TailscaleKitHello/README.md +++ b/swift/Examples/TailscaleKitHello/README.md @@ -2,13 +2,17 @@ ## Instructions -First build TailscaleKit: +First build TailscaleKit for the platform you wish to target: From /swift: ``` $ make macos +$ make ios-fat ``` +The ios target expects the universal xcframework produced by make ios-fat and +can be run on either a device or the simulator. + In TailnetSettings, configure an auth key and a server/service to query. ``` diff --git a/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj index 6ef9e99..3697f46 100644 --- a/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj +++ b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj @@ -9,8 +9,8 @@ /* Begin PBXBuildFile section */ C25260032D7A71E800BD3CCA /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */; }; C25260052D7A71FE00BD3CCA /* TailscaleKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - C289804A2DBAA8DA0019B7EB /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C28980492DBAA7E50019B7EB /* TailscaleKit.framework */; }; - C289804B2DBAA8DA0019B7EB /* TailscaleKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C28980492DBAA7E50019B7EB /* TailscaleKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C2CE237E2DD7756B0096C105 /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2CE23762DD76A5C0096C105 /* TailscaleKit.framework */; }; + C2ED636C2DDE3C1400297161 /* TailscaleKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C2ED636B2DDE3C1400297161 /* TailscaleKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,7 +30,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - C289804B2DBAA8DA0019B7EB /* TailscaleKit.framework in Embed Frameworks */, + C2ED636C2DDE3C1400297161 /* TailscaleKit.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -43,16 +43,11 @@ C25260082D7A7DC400BD3CCA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; C289803E2DBA8A350019B7EB /* HelloFromTailscale_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloFromTailscale_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; C28980492DBAA7E50019B7EB /* TailscaleKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TailscaleKit.framework; path = "../../build/Build/Products/Release-iphoneos/TailscaleKit.framework"; sourceTree = ""; }; + C2CE23762DD76A5C0096C105 /* TailscaleKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TailscaleKit.framework; path = "../../build/Build/Products/Release-iphonesimulator/TailscaleKit.framework"; sourceTree = ""; }; + C2ED636B2DDE3C1400297161 /* TailscaleKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TailscaleKit.xcframework; path = "../../build/Build/Products/Release-iphonefat/TailscaleKit.xcframework"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - C25260072D7A7BAE00BD3CCA /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */; - }; C289803F2DBA8A360019B7EB /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale_iOS" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -62,13 +57,20 @@ ); target = C28980342DBA8A350019B7EB /* HelloFromTailscale_iOS */; }; + C2CE23752DD76A330096C105 /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info_iOS.plist, + ); + target = C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ C2525FF22D7A70B700BD3CCA /* HelloFromTailscale */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - C25260072D7A7BAE00BD3CCA /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */, + C2CE23752DD76A330096C105 /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */, C289803F2DBA8A360019B7EB /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale_iOS" target */, ); path = HelloFromTailscale; @@ -89,7 +91,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C289804A2DBAA8DA0019B7EB /* TailscaleKit.framework in Frameworks */, + C2CE237E2DD7756B0096C105 /* TailscaleKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -118,6 +120,8 @@ C2525FC42D7A69DE00BD3CCA /* Frameworks */ = { isa = PBXGroup; children = ( + C2ED636B2DDE3C1400297161 /* TailscaleKit.xcframework */, + C2CE23762DD76A5C0096C105 /* TailscaleKit.framework */, C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */, C28980492DBAA7E50019B7EB /* TailscaleKit.framework */, ); @@ -374,7 +378,7 @@ DEVELOPMENT_TEAM = W5364U7YZB; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = "../../build/Build/**"; + FRAMEWORK_SEARCH_PATHS = ../../build/Build; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HelloFromTailscale/Info.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -403,7 +407,7 @@ DEVELOPMENT_TEAM = W5364U7YZB; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = "../../build/Build/**"; + FRAMEWORK_SEARCH_PATHS = ../../build/Build; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HelloFromTailscale/Info.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -432,7 +436,7 @@ DEVELOPMENT_TEAM = W5364U7YZB; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphoneOS"; + FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphonefat"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HelloFromTailscale/Info_iOS.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -467,7 +471,7 @@ DEVELOPMENT_TEAM = W5364U7YZB; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphoneOS"; + FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphonefat"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = HelloFromTailscale/Info_iOS.plist; INFOPLIST_KEY_NSHumanReadableCopyright = ""; diff --git a/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/xcshareddata/xcschemes/HelloFromTailscale_iOS_Sim.xcscheme b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/xcshareddata/xcschemes/HelloFromTailscale_iOS_Sim.xcscheme new file mode 100644 index 0000000..845da43 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/xcshareddata/xcschemes/HelloFromTailscale_iOS_Sim.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/Makefile b/swift/Makefile index 9fe8167..4bc68f9 100644 --- a/swift/Makefile +++ b/swift/Makefile @@ -11,12 +11,12 @@ endif # the libtailscale.a and libtailscale_ios.a dependencies. .PHONY: all -all: test ios macos ## Runs the tests and builds all library targets +all: test ios macos ios-fat ## Runs the tests and builds all library targets .PHONY: macos macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release (unsigned) @echo - @echo "::: Building TailscaleKit for macOS :::" + @echo "::: Building TailscaleKit.framework for macOS :::" cd .. && make c-archive mkdir -p build xcodebuild build -scheme "TailscaleKit (macOS)" \ @@ -26,9 +26,9 @@ macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release ( CODE_SIGNING_ALLOWED=NO | $(XCPRETTIFIER) .PHONY: ios -ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release (unsigned) +ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release-iphoneos (unsigned) @echo - @echo "::: Building TailscaleKit for iOS :::" + @echo "::: Building TailscaleKit.framework for iOS :::" cd .. && make c-archive-ios mkdir -p build xcodebuild build -scheme "TailscaleKit (iOS)" \ @@ -37,6 +37,27 @@ ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release (unsi -destination 'generic/platform=iOS' \ CODE_SIGNING_ALLOWED=NO | $(XCPRETTIFIER) +.PHONY: ios-sim +ios-sim: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release-iphonesimulator (unsigned) + @echo + @echo "::: Building TailscaleKit.framework for iOS Simulator :::" + cd .. && make c-archive-ios-sim + mkdir -p build + xcodebuild build -scheme "TailscaleKit (Simulator)" \ + -derivedDataPath build \ + -configuration Release \ + -destination 'generic/platform=iOS Simulator' \ + CODE_SIGNING_ALLOWED=NO | $(XCPRETTIFIER) + +.PHONY: ios-fat +ios-fat: ios-sim ios ## Builds TailscaleKit.xcframework to swift/build/Build/Products/Release-iphonefat + @echo + @echo "::: Building TailscaleKit.xcframework for ios and ios-simulator :::" + mkdir -p ./build/Build/Products/Release-iphonefat + xcodebuild -create-xcframework \ + -framework ./build/Build/Products/Release-iphoneos/TailscaleKit.framework \ + -framework ./build/Build/Products/Release-iphonesimulator/TailscaleKit.framework \ + -output ./build/Build/Products/Release-iphonefat/TailscaleKit.xcframework .PHONY: test test: ## Run tests (macOS) diff --git a/swift/README.md b/swift/README.md index e686239..f71666c 100644 --- a/swift/README.md +++ b/swift/README.md @@ -14,38 +14,46 @@ Build Requirements: Building Tailscale.framework: -First build the libtailscale dependecies: - - - From /swift -``` +```bash $ make macos -# make ios +$ make ios +$ make ios-sim +$ make ios-fat ``` -Will build TailscaleKit.framework into /swift/build/Build/Products. +These recipes build different variants of TailscaleKit.framework into /swift/build/Build/Products. -Separate frameworks will be built for macOS and iOS. All dependencies (libtailscale.a) +Separate frameworks will be built for macOS and iOS and the iOS Simulator. All dependencies (libtailscale*.a) are built automatically. Swift 6 is supported. +The ios and ios-sim frameworks are purposefully separated. The former is free of any simulator segments +and is suitable for app-store submissions. The latter is suitable for embedding when you +wish to run on a simulator in dev though 'make ios-fat' will produce an xcframework bundle including +both simulator and device frameworks for development. + +The frameworks are not signed and must be signed when they are embedded. + Alternatively, you may build from xCode using the Tailscale scheme but the libraries must be built first (since xCode will complain about paths and permissions) -From / -``` +To build only the static libraries, from / +```bash $ make c-archive $ make c-archive-ios +$ make c-archive-ios-sim ``` -Non-apple builds are not supported (yet). We do use URLSession and Combine though -it is possible to purge both. +If you're writing pure C, or C++, link these and use the generated tailscale.h header. +make c-archive builds for the local machine architecture/platform (arm64 macOS from a mac) + +Non-apple swift builds are not supported (yet) but should be possible with a little tweaking. ## Tests From /swift -``` +```bash $ make test ``` @@ -53,16 +61,12 @@ $ make test ## Usage Nodes need to be authorized in order to function. Set an auth key via -the config.authKey parameter, or watch the log stream and respond to the printed -authorization URL. +the config.authKey parameter, or watch the ipn bus (see the example) for +the browseToURL field for interactive web-based auth. Here's a working example using an auth key: ```Swift - -// Configures a Tailscale node and starts it up. The node here (and the key we would use to -// authenticate it) are marked as 'ephemeral' - meaning that the node will be disposed of as -// soon as it goes offline. func start() -> TailscaleNode { let dataDir = getDocumentDirectoryPath().absoluteString + "tailscale" let authKey = "tsnet-auth-put-your-auth-key-key-here" @@ -70,7 +74,7 @@ func start() -> TailscaleNode { path: dataDir, authKey: authKey, controlURL: Configuration.defaultControlURL, - ephemeral: true) + ephemeral: false) // The logger is configurable. The default will just print. let node = try TailscaleNode(config: config, logger: DefaultLogger()) @@ -94,7 +98,18 @@ func fetchURL(_ url: URL, tailscale: TailscaleNode) async throws -> Data { } ``` -See the [TailscaleKitTests](./Tests/TailscaleKitTests/TailscaleKitTests.swift) for more examples. +The "node" created here should show up in the Tailscale admin panel as "TSNet-Test" + +### LocalAPI + +TailscaleKit.framework also includes a functional (though somewhat incomplete) implementation of +LocalAPI which can be used to track the state of the embedded tailscale instance in much greater +detail. + +### Examples + +See the TailscaleKitHello example for a relatively complete implementation demonstrating proxied +HTTP and usage of LocalAPI to track the tailnet state. ## Contributing diff --git a/swift/TailscaleKit.xcodeproj/project.pbxproj b/swift/TailscaleKit.xcodeproj/project.pbxproj index 417c01d..8dd72d4 100644 --- a/swift/TailscaleKit.xcodeproj/project.pbxproj +++ b/swift/TailscaleKit.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ C2BED05D2CCFC68D004A2544 /* libtstestcontrol.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */; }; C2E1C30B2CC9EF1A00ADC565 /* libtailscale.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */; }; C2E1C3142CCA8B7C00ADC565 /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */; }; + C2ED636A2DDE346900297161 /* libtailscale_ios_sim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2CE21942DD67A170096C105 /* libtailscale_ios_sim.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -59,10 +60,12 @@ C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TailscaleKitTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; C28980E02DBFE3D40019B7EB /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtstestcontrol.a; path = ../tstestconrol/libtstestcontrol.a; sourceTree = ""; }; + C2CE21942DD67A170096C105 /* libtailscale_ios_sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtailscale_ios_sim.a; path = ../libtailscale_ios_sim.a; sourceTree = ""; }; C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TailscaleKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtailscale.a; path = ../libtailscale.a; sourceTree = ""; }; C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TailscaleKitXCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C2E3E87F2D2718D0004992A2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + C2ED635B2DDE33B700297161 /* TailscaleKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TailscaleKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -93,6 +96,16 @@ ); target = C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */; }; + C2ED635C2DDE33B700297161 /* Exceptions for "TailscaleKit" folder in "TailscaleKit iOS Sim" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + TailscaleKit.docc, + ); + publicHeaders = ( + TailscaleKit.h, + ); + target = C2ED63522DDE33B700297161 /* TailscaleKit iOS Sim */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ @@ -119,6 +132,7 @@ exceptions = ( C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit macOS" target */, C2525F4F2D7A22C100BD3CCA /* Exceptions for "TailscaleKit" folder in "TailscaleKit iOS" target */, + C2ED635C2DDE33B700297161 /* Exceptions for "TailscaleKit" folder in "TailscaleKit iOS Sim" target */, ); path = TailscaleKit; sourceTree = ""; @@ -166,6 +180,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C2ED63552DDE33B700297161 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2ED636A2DDE346900297161 /* libtailscale_ios_sim.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -189,6 +211,7 @@ C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */, C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */, C2525F4E2D7A22C100BD3CCA /* TailscaleKit.framework */, + C2ED635B2DDE33B700297161 /* TailscaleKit.framework */, ); name = Products; sourceTree = ""; @@ -196,6 +219,7 @@ C2E1C2FB2CC9B9E300ADC565 /* Frameworks */ = { isa = PBXGroup; children = ( + C2CE21942DD67A170096C105 /* libtailscale_ios_sim.a */, C2525F532D7A258D00BD3CCA /* libtailscale_ios.a */, C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */, C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */, @@ -220,6 +244,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C2ED63532DDE33B700297161 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -316,6 +347,29 @@ productReference = C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + C2ED63522DDE33B700297161 /* TailscaleKit iOS Sim */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2ED63582DDE33B700297161 /* Build configuration list for PBXNativeTarget "TailscaleKit iOS Sim" */; + buildPhases = ( + C2ED63532DDE33B700297161 /* Headers */, + C2ED63542DDE33B700297161 /* Sources */, + C2ED63552DDE33B700297161 /* Frameworks */, + C2ED63572DDE33B700297161 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */, + ); + name = "TailscaleKit iOS Sim"; + packageProductDependencies = ( + ); + productName = Tailscale; + productReference = C2ED635B2DDE33B700297161 /* TailscaleKit.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -354,6 +408,7 @@ targets = ( C2E1C2D92CC9B5A400ADC565 /* TailscaleKit macOS */, C2525F432D7A22C100BD3CCA /* TailscaleKit iOS */, + C2ED63522DDE33B700297161 /* TailscaleKit iOS Sim */, C2E1C30F2CCA8B7C00ADC565 /* TailscaleKitXCTests */, C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */, C2BED0552CCF3031004A2544 /* libtstestcontrol */, @@ -390,6 +445,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C2ED63572DDE33B700297161 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -443,6 +505,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C2ED63542DDE33B700297161 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -980,6 +1049,106 @@ }; name = Release; }; + C2ED63592DDE33B700297161 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/.."; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/..", + "$(SRCROOT)/TailscaleKit", + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(SRCROOT)/.."; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.Tailscale; + PRODUCT_MODULE_NAME = TailscaleKit; + PRODUCT_NAME = TailscaleKit; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 6.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Debug; + }; + C2ED635A2DDE33B700297161 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/.."; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/..", + "$(SRCROOT)/TailscaleKit", + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(SRCROOT)/.."; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.Tailscale; + PRODUCT_MODULE_NAME = TailscaleKit; + PRODUCT_NAME = TailscaleKit; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 6.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1037,6 +1206,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C2ED63582DDE33B700297161 /* Build configuration list for PBXNativeTarget "TailscaleKit iOS Sim" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2ED63592DDE33B700297161 /* Debug */, + C2ED635A2DDE33B700297161 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = C2E1C2D12CC9B5A400ADC565 /* Project object */; diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (Simulator).xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (Simulator).xcscheme new file mode 100644 index 0000000..dff1615 --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (Simulator).xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit/TailscaleNode.swift b/swift/TailscaleKit/TailscaleNode.swift index c7cd808..34a6f94 100644 --- a/swift/TailscaleKit/TailscaleNode.swift +++ b/swift/TailscaleKit/TailscaleNode.swift @@ -179,11 +179,11 @@ public actor TailscaleNode { } public struct LoopbackConfig: Sendable { - let address: String - let proxyCredential: String - let localAPIKey: String + public let address: String + public let proxyCredential: String + public let localAPIKey: String - var ip: String? { + public var ip: String? { let parts = address.split(separator: ":") let addr = parts.first guard parts.count == 2, let addr else { @@ -192,7 +192,7 @@ public actor TailscaleNode { return String(addr) } - var port: Int? { + public var port: Int? { let parts = address.split(separator: ":") let port = parts.last guard parts.count == 2, let port else { diff --git a/swift/script/clangwrap-ios-sim-arm.sh b/swift/script/clangwrap-ios-sim-arm.sh new file mode 100755 index 0000000..4f26df4 --- /dev/null +++ b/swift/script/clangwrap-ios-sim-arm.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +SDK=iphonesimulator +PLATFORM=ios-simulator + +CLANGARCH=arm64 + +SDK_PATH=`xcrun --sdk $SDK --show-sdk-path` + +# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang. +CLANG=`xcrun --sdk $SDK --find clang` + +exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=12.0 "$@" diff --git a/swift/script/clangwrap-ios-sim-x86.sh b/swift/script/clangwrap-ios-sim-x86.sh new file mode 100755 index 0000000..30c689e --- /dev/null +++ b/swift/script/clangwrap-ios-sim-x86.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +SDK=iphonesimulator +PLATFORM=ios-simulator + +CLANGARCH=x86_64 + +SDK_PATH=`xcrun --sdk $SDK --show-sdk-path` + +# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang. +CLANG=`xcrun --sdk $SDK --find clang` + +exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=12.0 "$@" diff --git a/swift/script/clangwrap.sh b/swift/script/clangwrap-ios.sh similarity index 100% rename from swift/script/clangwrap.sh rename to swift/script/clangwrap-ios.sh