Skip to content

Commit 40f559c

Browse files
committed
Makefile/swift: update Makefile and add support for iOS
updates tailscale/tailscale#13937 Adds a Makefile for building various targets. Adds support for building iOS compatible libtailscale_ios.a. Removes SOCKS5 support for iOS where it looks like the CFNetwork support is lacking. Fixes a few unexported symbols in TailscaleKit. Added macOS sample app.
1 parent 9d45e58 commit 40f559c

33 files changed

+1245
-122
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ libtailscale.so
22
libtailscale.a
33
libtailscale.h
44
libtailscale.tar*
5+
libtailscale_ios.a
6+
libtailscale_ios.h
57

68
/tstestcontrol/libtstestcontrol.a
79
/tstestcontrol/libtstestcontrol.h
810

911
/swift/build
12+
**/xcuserdata/**
1013

1114
/ruby/tmp/
1215
/ruby/pkg/

Makefile

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright (c) Tailscale Inc & AUTHORS
2+
# SPDX-License-Identifier: BSD-3-Clause
3+
4+
5+
libtailscale.a:
6+
go build -buildmode=c-archive
7+
8+
libtailscale_ios.a:
9+
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
10+
11+
.PHONY: c-archive-ios
12+
c-archive-ios: libtailscale_ios.a ## Builds libtailscale_ios.a for iOS (iOS SDK required)
13+
14+
.PHONY: c-archive
15+
c-archive: libtailscale.a ## Builds libtailscale.a for the target platform
16+
17+
.PHONY: shared
18+
shared: ## Builds libtailscale.so for the target platform
19+
go build -v -buildmode=c-shared
20+
21+
.PHONY: clean
22+
clean: ## Clean up build artifacts
23+
rm -f libtailscale.a
24+
rm -f libtailscale_ios.a
25+
rm -f libtailscale.h
26+
rm -f libtailscale_ios.h
27+
28+
.PHONY: help
29+
help: ## Show this help
30+
@echo "\nSpecify a command. The choices are:\n"
31+
@grep -hE '^[0-9a-zA-Z_-]+:.*?## .*$$' ${MAKEFILE_LIST} | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-12s\033[m %s\n", $$1, $$2}'
32+
@echo ""
33+
34+
.DEFAULT_GOAL := help

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ With the latest version of Go, run:
1313
go build -buildmode=c-archive
1414
```
1515

16+
or
17+
18+
```
19+
make archive
20+
```
21+
1622
This will produce a `libtailscale.a` file. Link it into your binary,
1723
and use the `tailscale.h` header to reference it.
1824

@@ -22,6 +28,12 @@ It is also possible to build a shared library using
2228
go build -buildmode=c-shared
2329
```
2430

31+
or
32+
33+
```
34+
make shared
35+
```
36+
2537
## Bugs
2638

2739
Please file any issues about this code or the hosted service on
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "mac",
5+
"scale" : "1x",
6+
"size" : "16x16"
7+
},
8+
{
9+
"idiom" : "mac",
10+
"scale" : "2x",
11+
"size" : "16x16"
12+
},
13+
{
14+
"idiom" : "mac",
15+
"scale" : "1x",
16+
"size" : "32x32"
17+
},
18+
{
19+
"idiom" : "mac",
20+
"scale" : "2x",
21+
"size" : "32x32"
22+
},
23+
{
24+
"idiom" : "mac",
25+
"scale" : "1x",
26+
"size" : "128x128"
27+
},
28+
{
29+
"idiom" : "mac",
30+
"scale" : "2x",
31+
"size" : "128x128"
32+
},
33+
{
34+
"idiom" : "mac",
35+
"scale" : "1x",
36+
"size" : "256x256"
37+
},
38+
{
39+
"idiom" : "mac",
40+
"scale" : "2x",
41+
"size" : "256x256"
42+
},
43+
{
44+
"idiom" : "mac",
45+
"scale" : "1x",
46+
"size" : "512x512"
47+
},
48+
{
49+
"idiom" : "mac",
50+
"scale" : "2x",
51+
"size" : "512x512"
52+
}
53+
],
54+
"info" : {
55+
"author" : "xcode",
56+
"version" : 1
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.security.app-sandbox</key>
6+
<true/>
7+
<key>com.apple.security.files.user-selected.read-only</key>
8+
<true/>
9+
<key>com.apple.security.network.client</key>
10+
<true/>
11+
<key>com.apple.security.network.server</key>
12+
<true/>
13+
</dict>
14+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
import SwiftUI
5+
6+
@main
7+
struct HelloFromTailscaleApp: App {
8+
let manager = HelloManager()
9+
10+
var body: some Scene {
11+
WindowGroup {
12+
HelloView(dialer: manager)
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
import Foundation
5+
import TailscaleKit
6+
7+
enum HelloError: Error {
8+
case noNode
9+
}
10+
11+
typealias MessageSender = @Sendable (String) async -> Void
12+
13+
struct Logger: TailscaleKit.LogSink {
14+
var logFileHandle: Int32? = STDOUT_FILENO
15+
16+
func log(_ message: String) {
17+
print(message)
18+
}
19+
}
20+
21+
protocol Dialer: Actor {
22+
func phoneHome(_ setMessage: @escaping MessageSender) async
23+
}
24+
25+
actor HelloManager: Dialer {
26+
var node: TailscaleNode?
27+
28+
let logger = Logger()
29+
let config: Configuration
30+
31+
init() {
32+
let temp = getDocumentDirectoryPath().absoluteString + "tailscale"
33+
self.config = Configuration(hostName: Settings.hostName,
34+
path: temp,
35+
authKey: Settings.authKey,
36+
controlURL: kDefaultControlURL,
37+
ephemeral: true)
38+
}
39+
40+
func setupNode() throws -> TailscaleNode {
41+
guard self.node == nil else { return self.node! }
42+
self.node = try TailscaleNode(config: config, logger: logger)
43+
return self.node!
44+
}
45+
46+
func phoneHome(_ setMessage: @escaping MessageSender) async {
47+
do {
48+
let node = try setupNode()
49+
await setMessage("Connecting to Tailnet...")
50+
51+
try await node.up()
52+
53+
await setMessage("Phoning " + Settings.tailnetServer + "...")
54+
55+
// Create a URLSession that can access nodes on the tailnet.
56+
// .tailscaleSession(node) is the magic sauce. This sends your URLRequest via
57+
// userspace Tailscale's SOCKS5 proxy.
58+
let sessionConfig = try await URLSessionConfiguration.tailscaleSession(node)
59+
let session = URLSession(configuration: sessionConfig)
60+
61+
// Request a resource from the tailnet...
62+
let url = URL(string: Settings.tailnetServer)!
63+
let req = URLRequest(url: url)
64+
65+
let (data, _) = try await session.data(for: req)
66+
await setMessage("\(Settings.tailnetServer) says:\n \(String(data: data, encoding: .utf8) ?? "(crickets!)")")
67+
} catch {
68+
await setMessage("Whoops!: \(error)")
69+
}
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
import SwiftUI
5+
6+
7+
struct HelloView: View {
8+
@ObservedObject var model : HelloViewModel
9+
let dialer: Dialer
10+
11+
init(dialer: Dialer) {
12+
self.dialer = dialer
13+
self.model = HelloViewModel()
14+
}
15+
16+
17+
var body: some View {
18+
VStack {
19+
Text("TailscaleKit Sample App. See README.md for setup instructions.")
20+
.font(.title)
21+
.padding(20)
22+
Text(model.message)
23+
.font(.title2)
24+
Button("Phone Home!") {
25+
model.runRequest(dialer)
26+
}
27+
}
28+
.padding()
29+
}
30+
}
31+
32+
actor PreviewDialer: Dialer {
33+
func phoneHome(_ setMessage: @escaping @Sendable (String) async -> Void) async {
34+
await setMessage("Hello from Preview!")
35+
}
36+
}
37+
38+
#Preview {
39+
let d = PreviewDialer()
40+
HelloView(dialer: d)
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
3+
import SwiftUI
4+
import Combine
5+
import TailscaleKit
6+
7+
class HelloViewModel: ObservableObject, @unchecked Sendable {
8+
@Published var message: String = "Ready to phone home!"
9+
10+
func setMessage(_ message: String) async {
11+
await MainActor.run {
12+
self.message = message
13+
}
14+
}
15+
16+
func runRequest(_ dialer: Dialer) {
17+
Task {
18+
await dialer.phoneHome(setMessage)
19+
}
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>NSAppTransportSecurity</key>
6+
<dict>
7+
<key>NSExceptionDomains</key>
8+
<dict>
9+
<key>ts.net</key>
10+
<dict>
11+
<key>NSExceptionAllowsInsecureHTTPLoads</key>
12+
<true/>
13+
<key>NSIncludesSubdomains</key>
14+
<true/>
15+
</dict>
16+
</dict>
17+
</dict>
18+
</dict>
19+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
import Foundation
5+
6+
struct Settings {
7+
// Replace with an actual auth key generated from the Tailscale admin console
8+
static let authKey = "tskey-auth-somekey"
9+
// Note: The sample has a transport exception for http on ts.net so http:// is ok...
10+
static let tailnetServer = "http://myserver.my-tailnet.ts.net"
11+
// Identifies this application in the Tailscale admin console.
12+
static let hostName = "Hello-From-Tailsacle-Sample-App"
13+
}
14+
15+
16+
func getDocumentDirectoryPath() -> URL {
17+
let arrayPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
18+
let docDirectoryPath = arrayPaths[0]
19+
return docDirectoryPath
20+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# TailscaleKitHello
2+
3+
## Instructions
4+
5+
First build TailscaleKit:
6+
7+
From /swift:
8+
```
9+
$ make macos
10+
```
11+
12+
In TailnetSettings, configure an auth key and a server/service to query.
13+
14+
```
15+
let authKey = "your-auth-key-here"
16+
let tailnetServer = "http://your-server-here.your-tailnet.ts.net"
17+
```
18+
19+
Run the project. Phone Home. The output should be the response from the server.

0 commit comments

Comments
 (0)