diff --git a/.gitignore b/.gitignore index d36329c..fe42201 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,14 @@ libtailscale.so libtailscale.a libtailscale.h libtailscale.tar* +libtailscale_ios.a +libtailscale_ios.h /tstestcontrol/libtstestcontrol.a /tstestcontrol/libtstestcontrol.h /swift/build +**/xcuserdata/** /ruby/tmp/ /ruby/pkg/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4157cb8 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +# Copyright (c) Tailscale Inc & AUTHORS +# SPDX-License-Identifier: BSD-3-Clause + + +libtailscale.a: + go build -buildmode=c-archive + +libtailscale_ios.a: + GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap.sh /usr/local/go/bin/go build -v -ldflags -w -tags ios -o libtailscale_ios.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 +c-archive: libtailscale.a ## Builds libtailscale.a for the target platform + +.PHONY: shared +shared: ## Builds libtailscale.so for the target platform + go build -v -buildmode=c-shared + +.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 + +.PHONY: help +help: ## Show this help + @echo "\nSpecify a command. The choices are:\n" + @grep -hE '^[0-9a-zA-Z_-]+:.*?## .*$$' ${MAKEFILE_LIST} | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-12s\033[m %s\n", $$1, $$2}' + @echo "" + +.DEFAULT_GOAL := help diff --git a/README.md b/README.md index dca88f2..54375b6 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ With the latest version of Go, run: go build -buildmode=c-archive ``` +or + +``` +make archive +``` + This will produce a `libtailscale.a` file. Link it into your binary, and use the `tailscale.h` header to reference it. @@ -22,6 +28,12 @@ It is also possible to build a shared library using go build -buildmode=c-shared ``` +or + +``` +make shared +``` + ## Bugs Please file any issues about this code or the hosted service on diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/AccentColor.colorset/Contents.json b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/AppIcon.appiconset/Contents.json b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/Contents.json b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloFromTailscale.entitlements b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloFromTailscale.entitlements new file mode 100644 index 0000000..40b639e --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloFromTailscale.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloFromTailscaleApp.swift b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloFromTailscaleApp.swift new file mode 100644 index 0000000..8ec6999 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloFromTailscaleApp.swift @@ -0,0 +1,15 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import SwiftUI + +@main +struct HelloFromTailscaleApp: App { + let manager = HelloManager() + + var body: some Scene { + WindowGroup { + HelloView(dialer: manager) + } + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloManager.swift b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloManager.swift new file mode 100644 index 0000000..1c51320 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloManager.swift @@ -0,0 +1,71 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import Foundation +import TailscaleKit + +enum HelloError: Error { + case noNode +} + +typealias MessageSender = @Sendable (String) async -> Void + +struct Logger: TailscaleKit.LogSink { + var logFileHandle: Int32? = STDOUT_FILENO + + func log(_ message: String) { + print(message) + } +} + +protocol Dialer: Actor { + func phoneHome(_ setMessage: @escaping MessageSender) async +} + +actor HelloManager: Dialer { + var node: TailscaleNode? + + let logger = Logger() + let config: Configuration + + init() { + let temp = getDocumentDirectoryPath().absoluteString + "tailscale" + self.config = Configuration(hostName: Settings.hostName, + path: temp, + authKey: Settings.authKey, + controlURL: kDefaultControlURL, + ephemeral: true) + } + + func setupNode() throws -> TailscaleNode { + guard self.node == nil else { return self.node! } + self.node = try TailscaleNode(config: config, logger: logger) + return self.node! + } + + func phoneHome(_ setMessage: @escaping MessageSender) async { + do { + let node = try setupNode() + await setMessage("Connecting to Tailnet...") + + try await node.up() + + await setMessage("Phoning " + Settings.tailnetServer + "...") + + // Create a URLSession that can access nodes on the tailnet. + // .tailscaleSession(node) is the magic sauce. This sends your URLRequest via + // userspace Tailscale's SOCKS5 proxy. + let sessionConfig = try await URLSessionConfiguration.tailscaleSession(node) + let session = URLSession(configuration: sessionConfig) + + // Request a resource from the tailnet... + let url = URL(string: Settings.tailnetServer)! + let req = URLRequest(url: url) + + let (data, _) = try await session.data(for: req) + await setMessage("\(Settings.tailnetServer) says:\n \(String(data: data, encoding: .utf8) ?? "(crickets!)")") + } catch { + await setMessage("Whoops!: \(error)") + } + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloView.swift b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloView.swift new file mode 100644 index 0000000..de928a9 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloView.swift @@ -0,0 +1,41 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import SwiftUI + + +struct HelloView: View { + @ObservedObject var model : HelloViewModel + let dialer: Dialer + + init(dialer: Dialer) { + self.dialer = dialer + self.model = HelloViewModel() + } + + + var body: some View { + VStack { + Text("TailscaleKit Sample App. See README.md for setup instructions.") + .font(.title) + .padding(20) + Text(model.message) + .font(.title2) + Button("Phone Home!") { + model.runRequest(dialer) + } + } + .padding() + } +} + +actor PreviewDialer: Dialer { + func phoneHome(_ setMessage: @escaping @Sendable (String) async -> Void) async { + await setMessage("Hello from Preview!") + } +} + +#Preview { + let d = PreviewDialer() + HelloView(dialer: d) +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloViewModel.swift b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloViewModel.swift new file mode 100644 index 0000000..95cb318 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloViewModel.swift @@ -0,0 +1,21 @@ + + +import SwiftUI +import Combine +import TailscaleKit + +class HelloViewModel: ObservableObject, @unchecked Sendable { + @Published var message: String = "Ready to phone home!" + + func setMessage(_ message: String) async { + await MainActor.run { + self.message = message + } + } + + func runRequest(_ dialer: Dialer) { + Task { + await dialer.phoneHome(setMessage) + } + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/Info.plist b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Info.plist new file mode 100644 index 0000000..365ab61 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Info.plist @@ -0,0 +1,19 @@ + + + + + NSAppTransportSecurity + + NSExceptionDomains + + ts.net + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + + + + diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/Preview Content/Preview Assets.xcassets/Contents.json b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/swift/Examples/TailscaleKitHello/HelloFromTailscale/TailnetSettings.swift b/swift/Examples/TailscaleKitHello/HelloFromTailscale/TailnetSettings.swift new file mode 100644 index 0000000..ea24492 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/HelloFromTailscale/TailnetSettings.swift @@ -0,0 +1,20 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import Foundation + +struct Settings { + // Replace with an actual auth key generated from the Tailscale admin console + static let authKey = "tskey-auth-somekey" + // Note: The sample has a transport exception for http on ts.net so http:// is ok... + static let tailnetServer = "http://myserver.my-tailnet.ts.net" + // Identifies this application in the Tailscale admin console. + static let hostName = "Hello-From-Tailsacle-Sample-App" +} + + +func getDocumentDirectoryPath() -> URL { + let arrayPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + let docDirectoryPath = arrayPaths[0] + return docDirectoryPath +} diff --git a/swift/Examples/TailscaleKitHello/README.md b/swift/Examples/TailscaleKitHello/README.md new file mode 100644 index 0000000..1500909 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/README.md @@ -0,0 +1,19 @@ +# TailscaleKitHello + +## Instructions + +First build TailscaleKit: + +From /swift: +``` +$ make macos +``` + +In TailnetSettings, configure an auth key and a server/service to query. + +``` +let authKey = "your-auth-key-here" +let tailnetServer = "http://your-server-here.your-tailnet.ts.net" +``` + +Run the project. Phone Home. The output should be the response from the server. diff --git a/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ee16377 --- /dev/null +++ b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj @@ -0,0 +1,373 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* 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, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + C25260042D7A71F300BD3CCA /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + C25260052D7A71FE00BD3CCA /* TailscaleKit.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TailscaleKit.framework; path = ../../build/Build/Products/Release/TailscaleKit.framework; sourceTree = ""; }; + C2525FF12D7A70B700BD3CCA /* HelloFromTailscale.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloFromTailscale.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C25260082D7A7DC400BD3CCA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + C25260072D7A7BAE00BD3CCA /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + C2525FF22D7A70B700BD3CCA /* HelloFromTailscale */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + C25260072D7A7BAE00BD3CCA /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */, + ); + path = HelloFromTailscale; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + C2525FEE2D7A70B700BD3CCA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C25260032D7A71E800BD3CCA /* TailscaleKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C2525FB12D7A69A500BD3CCA = { + isa = PBXGroup; + children = ( + C25260082D7A7DC400BD3CCA /* README.md */, + C2525FF22D7A70B700BD3CCA /* HelloFromTailscale */, + C2525FC42D7A69DE00BD3CCA /* Frameworks */, + C2525FBB2D7A69A500BD3CCA /* Products */, + ); + sourceTree = ""; + }; + C2525FBB2D7A69A500BD3CCA /* Products */ = { + isa = PBXGroup; + children = ( + C2525FF12D7A70B700BD3CCA /* HelloFromTailscale.app */, + ); + name = Products; + sourceTree = ""; + }; + C2525FC42D7A69DE00BD3CCA /* Frameworks */ = { + isa = PBXGroup; + children = ( + C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2525FFD2D7A70B900BD3CCA /* Build configuration list for PBXNativeTarget "HelloFromTailscale" */; + buildPhases = ( + C2525FED2D7A70B700BD3CCA /* Sources */, + C2525FEE2D7A70B700BD3CCA /* Frameworks */, + C2525FEF2D7A70B700BD3CCA /* Resources */, + C25260042D7A71F300BD3CCA /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + C2525FF22D7A70B700BD3CCA /* HelloFromTailscale */, + ); + name = HelloFromTailscale; + packageProductDependencies = ( + ); + productName = HelloFromTailscale; + productReference = C2525FF12D7A70B700BD3CCA /* HelloFromTailscale.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C2525FB22D7A69A500BD3CCA /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1620; + LastUpgradeCheck = 1620; + TargetAttributes = { + C2525FF02D7A70B700BD3CCA = { + CreatedOnToolsVersion = 16.2; + }; + }; + }; + buildConfigurationList = C2525FB52D7A69A500BD3CCA /* Build configuration list for PBXProject "TailscaleKitHello" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C2525FB12D7A69A500BD3CCA; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = C2525FBB2D7A69A500BD3CCA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C2525FEF2D7A70B700BD3CCA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C2525FED2D7A70B700BD3CCA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + C2525FBF2D7A69A500BD3CCA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C2525FC02D7A69A500BD3CCA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + C2525FFE2D7A70B900BD3CCA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = HelloFromTailscale/HelloFromTailscale.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"HelloFromTailscale/Preview Content\""; + DEVELOPMENT_TEAM = W5364U7YZB; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = "../../build/Build/**"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HelloFromTailscale/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.HelloFromTailscale; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 6.0; + }; + name = Debug; + }; + C2525FFF2D7A70B900BD3CCA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = HelloFromTailscale/HelloFromTailscale.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"HelloFromTailscale/Preview Content\""; + DEVELOPMENT_TEAM = W5364U7YZB; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = "../../build/Build/**"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = HelloFromTailscale/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.HelloFromTailscale; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 6.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C2525FB52D7A69A500BD3CCA /* Build configuration list for PBXProject "TailscaleKitHello" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2525FBF2D7A69A500BD3CCA /* Debug */, + C2525FC02D7A69A500BD3CCA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C2525FFD2D7A70B900BD3CCA /* Build configuration list for PBXNativeTarget "HelloFromTailscale" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2525FFE2D7A70B900BD3CCA /* Debug */, + C2525FFF2D7A70B900BD3CCA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C2525FB22D7A69A500BD3CCA /* Project object */; +} diff --git a/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/swift/Makefile b/swift/Makefile index 375ceb2..5e89882 100644 --- a/swift/Makefile +++ b/swift/Makefile @@ -7,14 +7,45 @@ ifeq (, $(shell which $(XCPRETTIFIER))) XCPRETTIFIER := cat endif -OUTPUT_DIR=build +# The xcodebuild schemes will run the Makefile in the root directory to build +# the libtailscale.a and libtailscale_ios.a dependencies. -build: - mkdir -p $(OUTPUT_DIR) - xcodebuild build -scheme TailscaleKit -derivedDataPath $(OUTPUT_DIR) -configuration Release -destination 'generic/platform=macOS,arch=arm64' -destination 'generic/platform=iOS' | $(XCPRETTIFIER) +.PHONY: macos +macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release + cd .. && make c-archive + mkdir -p build + xcodebuild build -scheme "TailscaleKit (macOS)" \ + -derivedDataPath build \ + -configuration Release \ + -destination 'platform=macOS,arch=arm64' | $(XCPRETTIFIER) -test: - xcodebuild test -scheme TailscaleKitXCTests -derivedDataPath $(OUTPUT_DIR) -configuration Debug | $(XCPRETTIFIER) +.PHONY: ios +ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release + cd .. && make c-archive-ios + mkdir -p build + xcodebuild build -scheme "TailscaleKit (iOS)" \ + -derivedDataPath build \ + -configuration Release \ + -destination 'generic/platform=iOS' | $(XCPRETTIFIER) -clean: - rm -rf $(OUTPUT_DIR) \ No newline at end of file +.PHONY: test +test: ## Run tests (macOS) + cd .. && make c-archive + mkdir -p build + xcodebuild test -scheme TailscaleKitXCTests \ + -derivedDataPath build \ + -configuration Debug \ + -destination 'platform=macOS,arch=arm64' | $(XCPRETTIFIER) + +.PHONY: clean +clean: ## Clean up build artifacts (including the libtailscale dependencies) + cd .. && make clean + rm -rf build + +.PHONY: help +help: ## Show this help + @echo "\nSpecify a command. The choices are:\n" + @grep -hE '^[0-9a-zA-Z_-]+:.*?## .*$$' ${MAKEFILE_LIST} | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-12s\033[m %s\n", $$1, $$2}' + @echo "" + +.DEFAULT_GOAL := help diff --git a/swift/README.md b/swift/README.md index 1ca7199..e686239 100644 --- a/swift/README.md +++ b/swift/README.md @@ -14,9 +14,14 @@ Build Requirements: Building Tailscale.framework: +First build the libtailscale dependecies: + + + From /swift ``` -$ make build +$ make macos +# make ios ``` Will build TailscaleKit.framework into /swift/build/Build/Products. @@ -24,7 +29,15 @@ Will build TailscaleKit.framework into /swift/build/Build/Products. Separate frameworks will be built for macOS and iOS. All dependencies (libtailscale.a) are built automatically. Swift 6 is supported. -Alternatively, you may build from xCode using the Tailscale scheme. +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 / +``` +$ make c-archive +$ make c-archive-ios +``` Non-apple builds are not supported (yet). We do use URLSession and Combine though it is possible to purge both. diff --git a/swift/TailscaleKit.xcodeproj/project.pbxproj b/swift/TailscaleKit.xcodeproj/project.pbxproj index a132530..cd81c47 100644 --- a/swift/TailscaleKit.xcodeproj/project.pbxproj +++ b/swift/TailscaleKit.xcodeproj/project.pbxproj @@ -20,22 +20,10 @@ ); productName = libtailscale; }; - C2EE3B622CCBE88400CF5BE0 /* libtailscale */ = { - isa = PBXAggregateTarget; - buildConfigurationList = C2EE3B632CCBE88400CF5BE0 /* Build configuration list for PBXAggregateTarget "libtailscale" */; - buildPhases = ( - C2EE3B662CCBE88E00CF5BE0 /* ShellScript */, - ); - dependencies = ( - ); - name = libtailscale; - packageProductDependencies = ( - ); - productName = libtailscale; - }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + C2525F542D7A258D00BD3CCA /* libtailscale_ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2525F532D7A258D00BD3CCA /* libtailscale_ios.a */; }; 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 */; }; @@ -63,32 +51,39 @@ remoteGlobalIDString = C2E1C2D92CC9B5A400ADC565; remoteInfo = Tailscale; }; - C2EE3B692CCBED1E00CF5BE0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C2E1C2D12CC9B5A400ADC565 /* Project object */; - proxyType = 1; - remoteGlobalIDString = C2EE3B622CCBE88400CF5BE0; - remoteInfo = libtailscale; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + C2525F4E2D7A22C100BD3CCA /* TailscaleKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TailscaleKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C2525F532D7A258D00BD3CCA /* libtailscale_ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtailscale_ios.a; path = ../libtailscale_ios.a; sourceTree = ""; }; C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TailscaleKitTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtstestcontrol.a; path = ../tstestconrol/libtstestcontrol.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 = ""; }; - C2E3E8802D2718D6004992A2 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit" target */ = { + C2525F4F2D7A22C100BD3CCA /* Exceptions for "TailscaleKit" folder in "TailscaleKit iOS" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + TailscaleKit.docc, + ); + publicHeaders = ( + TailscaleKit.h, + ); + target = C2525F432D7A22C100BD3CCA /* TailscaleKit iOS */; + }; + C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit macOS" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + TailscaleKit.docc, + ); publicHeaders = ( TailscaleKit.h, ); - target = C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */; + target = C2E1C2D92CC9B5A400ADC565 /* TailscaleKit macOS */; }; C2E3E87E2D2711BF004992A2 /* Exceptions for "TailscaleKitTestHost" folder in "TailscaleKitTestHost" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; @@ -121,7 +116,8 @@ C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit" target */, + C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit macOS" target */, + C2525F4F2D7A22C100BD3CCA /* Exceptions for "TailscaleKit" folder in "TailscaleKit iOS" target */, ); path = TailscaleKit; sourceTree = ""; @@ -137,6 +133,14 @@ /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + C2525F482D7A22C100BD3CCA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2525F542D7A258D00BD3CCA /* libtailscale_ios.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C286405F2CCA8C9D00CD5EBC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -167,7 +171,6 @@ C2E1C2D02CC9B5A400ADC565 = { isa = PBXGroup; children = ( - C2E3E8802D2718D6004992A2 /* Makefile */, C2E3E87F2D2718D0004992A2 /* README.md */, C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */, C2E1C3112CCA8B7C00ADC565 /* TailscaleKitXCTests */, @@ -183,6 +186,7 @@ C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */, C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */, C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */, + C2525F4E2D7A22C100BD3CCA /* TailscaleKit.framework */, ); name = Products; sourceTree = ""; @@ -190,6 +194,7 @@ C2E1C2FB2CC9B9E300ADC565 /* Frameworks */ = { isa = PBXGroup; children = ( + C2525F532D7A258D00BD3CCA /* libtailscale_ios.a */, C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */, C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */, ); @@ -199,6 +204,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + C2525F462D7A22C100BD3CCA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C2E1C2D52CC9B5A400ADC565 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -209,6 +221,29 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + C2525F432D7A22C100BD3CCA /* TailscaleKit iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2525F4B2D7A22C100BD3CCA /* Build configuration list for PBXNativeTarget "TailscaleKit iOS" */; + buildPhases = ( + C2525F462D7A22C100BD3CCA /* Headers */, + C2525F472D7A22C100BD3CCA /* Sources */, + C2525F482D7A22C100BD3CCA /* Frameworks */, + C2525F4A2D7A22C100BD3CCA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */, + ); + name = "TailscaleKit iOS"; + packageProductDependencies = ( + ); + productName = Tailscale; + productReference = C2525F4E2D7A22C100BD3CCA /* TailscaleKit.framework */; + productType = "com.apple.product-type.framework"; + }; C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */ = { isa = PBXNativeTarget; buildConfigurationList = C28640842CCA8C9E00CD5EBC /* Build configuration list for PBXNativeTarget "TailscaleKitTestHost" */; @@ -231,9 +266,9 @@ productReference = C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */; productType = "com.apple.product-type.application"; }; - C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */ = { + C2E1C2D92CC9B5A400ADC565 /* TailscaleKit macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = C2E1C2ED2CC9B5A400ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKit" */; + buildConfigurationList = C2E1C2ED2CC9B5A400ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKit macOS" */; buildPhases = ( C2E1C2D52CC9B5A400ADC565 /* Headers */, C2E1C2D62CC9B5A400ADC565 /* Sources */, @@ -243,12 +278,11 @@ buildRules = ( ); dependencies = ( - C2EE3B6A2CCBED1E00CF5BE0 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */, ); - name = TailscaleKit; + name = "TailscaleKit macOS"; packageProductDependencies = ( ); productName = Tailscale; @@ -300,9 +334,6 @@ CreatedOnToolsVersion = 16.1; TestTargetID = C28640612CCA8C9D00CD5EBC; }; - C2EE3B622CCBE88400CF5BE0 = { - CreatedOnToolsVersion = 16.1; - }; }; }; buildConfigurationList = C2E1C2D42CC9B5A400ADC565 /* Build configuration list for PBXProject "TailscaleKit" */; @@ -319,16 +350,23 @@ projectDirPath = ""; projectRoot = ""; targets = ( - C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */, + C2E1C2D92CC9B5A400ADC565 /* TailscaleKit macOS */, + C2525F432D7A22C100BD3CCA /* TailscaleKit iOS */, C2E1C30F2CCA8B7C00ADC565 /* TailscaleKitXCTests */, C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */, - C2EE3B622CCBE88400CF5BE0 /* libtailscale */, C2BED0552CCF3031004A2544 /* libtstestcontrol */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + C2525F4A2D7A22C100BD3CCA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C28640602CCA8C9D00CD5EBC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -372,28 +410,16 @@ shellPath = /bin/sh; shellScript = "# Type a script or drag a script file from your workspace to insert its path.\npushd .\ncd $(SCROOT)/../tstestcontrol\nmake all\npopd\n"; }; - C2EE3B662CCBE88E00CF5BE0 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C2525F472D7A22C100BD3CCA /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - "$(SRCROOT)/../libtailscale.a", - "$(SRCROOT)/../libtailscale.h", - ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\npushd .\ncd $(SCROOT)/..\nmake libtailscale\npopd\n"; }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ C286405E2CCA8C9D00CD5EBC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -430,17 +456,110 @@ }; C2E1C3162CCA8B7C00ADC565 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */; + target = C2E1C2D92CC9B5A400ADC565 /* TailscaleKit macOS */; targetProxy = C2E1C3152CCA8B7C00ADC565 /* PBXContainerItemProxy */; }; - C2EE3B6A2CCBED1E00CF5BE0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C2EE3B622CCBE88400CF5BE0 /* libtailscale */; - targetProxy = C2EE3B692CCBED1E00CF5BE0 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + C2525F4C2D7A22C100BD3CCA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = W5364U7YZB; + 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; + 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; + }; + C2525F4D2D7A22C100BD3CCA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = W5364U7YZB; + 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; + 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; + }; C28640852CCA8C9E00CD5EBC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -536,7 +655,7 @@ GENERATE_INFOPLIST_FILE = YES; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/..", - "$(SRCROOT)", + "$(SRCROOT)/TailscaleKit", ); INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -555,10 +674,11 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.Tailscale; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = auto; + PRODUCT_MODULE_NAME = TailscaleKit; + PRODUCT_NAME = TailscaleKit; + SDKROOT = macosx; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = macosx; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INSTALL_OBJC_HEADER = NO; SWIFT_VERSION = 6.0; @@ -584,7 +704,7 @@ GENERATE_INFOPLIST_FILE = YES; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/..", - "$(SRCROOT)", + "$(SRCROOT)/TailscaleKit", ); INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -603,10 +723,11 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.Tailscale; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = auto; + PRODUCT_MODULE_NAME = TailscaleKit; + PRODUCT_NAME = TailscaleKit; + SDKROOT = macosx; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTED_PLATFORMS = macosx; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INSTALL_OBJC_HEADER = NO; SWIFT_VERSION = 6.0; @@ -777,27 +898,18 @@ }; name = Release; }; - C2EE3B642CCBE88400CF5BE0 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = W5364U7YZB; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - C2EE3B652CCBE88400CF5BE0 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = W5364U7YZB; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + C2525F4B2D7A22C100BD3CCA /* Build configuration list for PBXNativeTarget "TailscaleKit iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2525F4C2D7A22C100BD3CCA /* Debug */, + C2525F4D2D7A22C100BD3CCA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C28640842CCA8C9E00CD5EBC /* Build configuration list for PBXNativeTarget "TailscaleKitTestHost" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -825,7 +937,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - C2E1C2ED2CC9B5A400ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKit" */ = { + C2E1C2ED2CC9B5A400ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKit macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( C2E1C2EE2CC9B5A400ADC565 /* Debug */, @@ -843,15 +955,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - C2EE3B632CCBE88400CF5BE0 /* Build configuration list for PBXAggregateTarget "libtailscale" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C2EE3B642CCBE88400CF5BE0 /* Debug */, - C2EE3B652CCBE88400CF5BE0 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = C2E1C2D12CC9B5A400ADC565 /* Project object */; diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (iOS).xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (iOS).xcscheme new file mode 100644 index 0000000..f12fc73 --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (iOS).xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit.xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (macOS).xcscheme similarity index 94% rename from swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit.xcscheme rename to swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (macOS).xcscheme index 1c775c4..f13aa28 100644 --- a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit.xcscheme +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit (macOS).xcscheme @@ -1,6 +1,6 @@ @@ -52,7 +52,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "C2E1C2D92CC9B5A400ADC565" BuildableName = "TailscaleKit.framework" - BlueprintName = "TailscaleKit" + BlueprintName = "TailscaleKit macOS" ReferencedContainer = "container:TailscaleKit.xcodeproj"> diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtailscale (ios).xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtailscale (ios).xcscheme new file mode 100644 index 0000000..9419789 --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtailscale (ios).xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtailscale (macOS).xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtailscale (macOS).xcscheme new file mode 100644 index 0000000..30f1c1c --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtailscale (macOS).xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit/IncomingConnection.swift b/swift/TailscaleKit/IncomingConnection.swift index 9a13ce6..b8cacb8 100644 --- a/swift/TailscaleKit/IncomingConnection.swift +++ b/swift/TailscaleKit/IncomingConnection.swift @@ -2,6 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause import Combine +import Foundation /// IncomingConnection is use to read incoming message from an inbound /// connection. IncomingConnections are not instantiated directly, diff --git a/swift/TailscaleKit/Listener.swift b/swift/TailscaleKit/Listener.swift index c1d8d2f..89c1e83 100644 --- a/swift/TailscaleKit/Listener.swift +++ b/swift/TailscaleKit/Listener.swift @@ -2,6 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause import Combine +import Foundation /// A Listener is used to await incoming connections from another /// Tailnet node. diff --git a/swift/TailscaleKit/LogSink.swift b/swift/TailscaleKit/LogSink.swift index 30931b4..73f45dd 100644 --- a/swift/TailscaleKit/LogSink.swift +++ b/swift/TailscaleKit/LogSink.swift @@ -1,6 +1,8 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +import Foundation + /// A generic interface for sinking log messages from the Swift wrapper /// and go public protocol LogSink: Sendable { @@ -13,19 +15,19 @@ public protocol LogSink: Sendable { } /// Dumps all internal logs to NSLog and go logs to stdout -struct DefaultLogger: LogSink { - var logFileHandle: Int32? = STDOUT_FILENO +public struct DefaultLogger: LogSink { + public var logFileHandle: Int32? = STDOUT_FILENO - func log(_ message: String) { + public func log(_ message: String) { NSLog(message) } } /// Discards all logs -struct BlackholeLogger: LogSink { - var logFileHandle: Int32? - - func log(_ message: String) { +public struct BlackholeLogger: LogSink { + public var logFileHandle: Int32? + + public func log(_ message: String) { // Go back to the Shadow! } } diff --git a/swift/TailscaleKit/OutgoingConnection.swift b/swift/TailscaleKit/OutgoingConnection.swift index cca0f61..86d07c6 100644 --- a/swift/TailscaleKit/OutgoingConnection.swift +++ b/swift/TailscaleKit/OutgoingConnection.swift @@ -1,6 +1,7 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +import Foundation import Combine /// ConnectionState indicates the state of individual TSConnection instances diff --git a/swift/TailscaleKit/TailscaleError.swift b/swift/TailscaleKit/TailscaleError.swift index 9cfe7eb..00dd274 100644 --- a/swift/TailscaleKit/TailscaleError.swift +++ b/swift/TailscaleKit/TailscaleError.swift @@ -1,6 +1,8 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +import Foundation + public enum TailscaleError: Error { case badInterfaceHandle ///< The tailscale handle is bad. diff --git a/swift/TailscaleKit/TailscaleNode.swift b/swift/TailscaleKit/TailscaleNode.swift index 7523ba5..b0d0875 100644 --- a/swift/TailscaleKit/TailscaleNode.swift +++ b/swift/TailscaleKit/TailscaleNode.swift @@ -1,15 +1,30 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +public let kDefaultControlURL = "https://controlplane.tailscale.com" + + /// Configuration for a tailscale application node public struct Configuration: Sendable { - let hostName: String ///< The hostname of the node/application instance - let path: String - let authKey: String? ///< An auth key. Leave empty to use web auth - let controlURL: String ///< URL for Tailscale control - let ephemeral: Bool + public let hostName: String ///< The hostname of the node/application instance + public let path: String + public let authKey: String? ///< An auth key. Leave empty to use web auth + public let controlURL: String ///< URL for Tailscale control + public let ephemeral: Bool + + public init(hostName: String, + path: String, + authKey: String?, + controlURL: String, + ephemeral: Bool = false) + { + self.hostName = hostName + self.path = path + self.authKey = authKey + self.controlURL = controlURL + self.ephemeral = ephemeral + } - static let defaultControlURL = "https://controlplane.tailscale.com" } /// The layer 3 protocol to use diff --git a/swift/TailscaleKit/URLSession+Tailscale.swift b/swift/TailscaleKit/URLSession+Tailscale.swift index 825b61f..349490b 100644 --- a/swift/TailscaleKit/URLSession+Tailscale.swift +++ b/swift/TailscaleKit/URLSession+Tailscale.swift @@ -1,8 +1,16 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause -extension URLSessionConfiguration { +#if os(iOS) +import UIKit +#endif +public extension URLSessionConfiguration { + + // (barnstar) TODO: kCFNetworkProxiesSOCKS* is not available on iOS + // is there another way to make this work on non desktops? + + #if os(macOS) /// Adds the a connectionProxyDictionary to a URLSessionConfiguration to /// proxy all requests through the given TailscaleNode. /// @@ -28,10 +36,11 @@ extension URLSessionConfiguration { ] } - static func tailscaleSession(_ node: TailscaleNode) async throws -> URLSessionConfiguration { + public static func tailscaleSession(_ node: TailscaleNode) async throws -> URLSessionConfiguration { let config = URLSessionConfiguration.default try await config.proxyVia(node) return config } + #endif } diff --git a/swift/TailscaleKitXCTests/TailscaleKitTests.swift b/swift/TailscaleKitXCTests/TailscaleKitTests.swift index bb3b7e1..7d543a5 100644 --- a/swift/TailscaleKitXCTests/TailscaleKitTests.swift +++ b/swift/TailscaleKitXCTests/TailscaleKitTests.swift @@ -171,7 +171,7 @@ final class TailscaleKitTests: XCTestCase { let config = Configuration(hostName: "TSNet-Test", path: temp, authKey: authKey, - controlURL: Configuration.defaultControlURL, + controlURL: kDefaultControlURL, ephemeral: true) let ts1 = try TailscaleNode(config: config, logger: logger) diff --git a/swift/script/clangwrap.sh b/swift/script/clangwrap.sh new file mode 100755 index 0000000..ff29975 --- /dev/null +++ b/swift/script/clangwrap.sh @@ -0,0 +1,14 @@ +#!/bin/sh + + +SDK=iphoneos +PLATFORM=ios + +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 "$@"