Skip to content

Commit 42fe23b

Browse files
committed
Add tvOS and simulator support
1 parent 64881dc commit 42fe23b

File tree

7 files changed

+245
-56
lines changed

7 files changed

+245
-56
lines changed

.github/workflows/naive-build.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,20 @@ jobs:
147147
name: darwin-amd64
148148
- target: darwin/arm64
149149
name: darwin-arm64
150+
# iOS
150151
- target: ios/arm64
151152
name: ios-arm64
153+
- target: ios/arm64/simulator
154+
name: ios-arm64-simulator
155+
- target: ios/amd64
156+
name: ios-amd64-simulator
157+
# tvOS
158+
- target: tvos/arm64
159+
name: tvos-arm64
160+
- target: tvos/arm64/simulator
161+
name: tvos-arm64-simulator
162+
- target: tvos/amd64
163+
name: tvos-amd64-simulator
152164
steps:
153165
- uses: actions/checkout@v4
154166
with:
@@ -190,7 +202,7 @@ jobs:
190202
- name: Package
191203
run: go run ./cmd/build-naive --target=${{ matrix.target }} package
192204
- name: Build and run test binary
193-
if: matrix.target != 'ios/arm64'
205+
if: startsWith(matrix.target, 'darwin/')
194206
run: |
195207
CGO_ENABLED=1 \
196208
CGO_LDFLAGS="$(go run ./cmd/build-naive --target=${{ matrix.target }} cgo-flags)" \

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Go bindings for [naiveproxy](https://github.com/klzgrad/naiveproxy).
1515
| darwin/amd64 | mac | x64 |
1616
| darwin/arm64 | mac | arm64 |
1717
| ios/arm64 | ios | arm64 |
18+
| ios/amd64 | ios | amd64 |
1819
| linux/386 | linux | x86 |
1920
| linux/amd64 | linux | x64 |
2021
| linux/arm | linux | arm |
@@ -28,21 +29,22 @@ Go bindings for [naiveproxy](https://github.com/klzgrad/naiveproxy).
2829
| Platform | Minimum Version |
2930
|---------------|-----------------|
3031
| macOS | 12.0 (Monterey) |
31-
| iOS | 15.0 |
32+
| iOS/tvOS | 15.0 |
3233
| Windows | 10 |
3334
| Android | 5.0 (API 21) |
3435
| Linux (glibc) | glibc 2.31 |
3536
| Linux (musl) | any |
3637

3738
## Downstream Build Requirements
3839

39-
| Platform | Requirements | Go Build Flags |
40-
|---------------|--------------------|-----------------------------------|
41-
| Linux (glibc) | Chromium toolchain | - |
42-
| Linux (musl) | Chromium toolchain | `-tags with_musl` |
43-
| macOS / iOS | macOS Xcode | - |
44-
| Windows | - | `CGO_ENABLED=0 -tags with_purego` |
45-
| Android | Android NDK | - |
40+
| Platform | Requirements | Go Build Flags |
41+
|--------------------------------------|---------------------------------|-----------------------------------|
42+
| Linux (glibc) | Chromium toolchain | - |
43+
| Linux (musl) | Chromium toolchain | `-tags with_musl` |
44+
| macOS / iOS | macOS Xcode | - |
45+
| iOS simulator/ tvOS / tvOS simulator | macOS Xcode + SagerNet/gomobile | - |
46+
| Windows | - | `CGO_ENABLED=0 -tags with_purego` |
47+
| Android | Android NDK | - |
4648

4749
## Linux Build instructions
4850

cmd/build-naive/cmd.go

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import (
1515

1616
// Target represents a build target platform
1717
type Target struct {
18-
OS string // gn target_os: linux, mac, win, android, ios, openwrt
19-
CPU string // gn target_cpu: x64, arm64, x86, arm
20-
GOOS string // Go GOOS
21-
ARCH string // Go GOARCH
22-
Libc string // C library: "" (default), "glibc", or "musl" (Linux only)
18+
OS string // gn target_os: linux, mac, win, android, ios, openwrt
19+
CPU string // gn target_cpu: x64, arm64, x86, arm
20+
GOOS string // Go GOOS
21+
ARCH string // Go GOARCH
22+
Libc string // C library: "" (default), "glibc", or "musl" (Linux only)
23+
Platform string // Apple: "iphoneos", "tvos" (GN target_platform)
24+
Environment string // Apple: "device", "simulator" (GN target_environment)
2325
}
2426

2527
var allTargets = []Target{
@@ -32,7 +34,15 @@ var allTargets = []Target{
3234
{OS: "win", CPU: "x64", GOOS: "windows", ARCH: "amd64"},
3335
{OS: "win", CPU: "arm64", GOOS: "windows", ARCH: "arm64"},
3436
{OS: "win", CPU: "x86", GOOS: "windows", ARCH: "386"},
35-
{OS: "ios", CPU: "arm64", GOOS: "ios", ARCH: "arm64"},
37+
// iOS
38+
{OS: "ios", CPU: "arm64", GOOS: "ios", ARCH: "arm64", Platform: "iphoneos", Environment: "device"},
39+
{OS: "ios", CPU: "arm64", GOOS: "ios", ARCH: "arm64", Platform: "iphoneos", Environment: "simulator"},
40+
{OS: "ios", CPU: "x64", GOOS: "ios", ARCH: "amd64", Platform: "iphoneos", Environment: "simulator"},
41+
// tvOS
42+
{OS: "ios", CPU: "arm64", GOOS: "ios", ARCH: "arm64", Platform: "tvos", Environment: "device"},
43+
{OS: "ios", CPU: "arm64", GOOS: "ios", ARCH: "arm64", Platform: "tvos", Environment: "simulator"},
44+
{OS: "ios", CPU: "x64", GOOS: "ios", ARCH: "amd64", Platform: "tvos", Environment: "simulator"},
45+
// Android
3646
{OS: "android", CPU: "arm64", GOOS: "android", ARCH: "arm64"},
3747
{OS: "android", CPU: "x64", GOOS: "android", ARCH: "amd64"},
3848
{OS: "android", CPU: "arm", GOOS: "android", ARCH: "arm"},
@@ -113,13 +123,26 @@ func parseTargets() []Target {
113123
for _, part := range strings.Split(targetStr, ",") {
114124
part = strings.TrimSpace(part)
115125
parts := strings.Split(part, "/")
126+
if len(parts) < 2 || len(parts) > 3 {
127+
log.Fatalf("invalid target format: %s (expected os/arch or os/arch/variant)", part)
128+
}
129+
goos, goarch := parts[0], parts[1]
130+
131+
// Handle iOS and tvOS targets with optional simulator suffix
132+
if goos == "ios" || goos == "tvos" {
133+
target := parseAppleTarget(goos, goarch, parts)
134+
targets = append(targets, target)
135+
continue
136+
}
137+
138+
// Standard targets
116139
if len(parts) != 2 {
117140
log.Fatalf("invalid target format: %s (expected os/arch)", part)
118141
}
119-
goos, goarch := parts[0], parts[1]
142+
120143
found := false
121144
for _, t := range allTargets {
122-
if t.GOOS == goos && t.ARCH == goarch {
145+
if t.GOOS == goos && t.ARCH == goarch && t.Platform == "" {
123146
target := applyLibc(t)
124147
targets = append(targets, target)
125148
found = true
@@ -133,6 +156,41 @@ func parseTargets() []Target {
133156
return targets
134157
}
135158

159+
// parseAppleTarget parses iOS/tvOS target strings
160+
// Formats:
161+
// - ios/arm64 -> iOS device arm64
162+
// - ios/arm64/simulator -> iOS simulator arm64
163+
// - ios/amd64 -> iOS simulator amd64 (only simulator supports amd64)
164+
// - tvos/arm64 -> tvOS device arm64
165+
// - tvos/arm64/simulator -> tvOS simulator arm64
166+
// - tvos/amd64 -> tvOS simulator amd64
167+
func parseAppleTarget(goos, goarch string, parts []string) Target {
168+
isTvOS := goos == "tvos"
169+
platform := "iphoneos"
170+
if isTvOS {
171+
platform = "tvos"
172+
}
173+
174+
// Determine environment
175+
environment := "device"
176+
if len(parts) == 3 && parts[2] == "simulator" {
177+
environment = "simulator"
178+
} else if goarch == "amd64" {
179+
// amd64 is only available for simulator
180+
environment = "simulator"
181+
}
182+
183+
// Find matching target
184+
for _, t := range allTargets {
185+
if t.GOOS == "ios" && t.ARCH == goarch && t.Platform == platform && t.Environment == environment {
186+
return t
187+
}
188+
}
189+
190+
log.Fatalf("unsupported target: %s/%s", goos, goarch)
191+
return Target{}
192+
}
193+
136194
// applyLibc applies the --libc flag to a target
137195
func applyLibc(target Target) Target {
138196
if libcStr == "" || libcStr == "glibc" {

cmd/build-naive/cmd_build.go

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,43 @@ func init() {
2525
mainCommand.AddCommand(commandBuild)
2626
}
2727

28+
// formatTargetLog returns a human-readable log string for a target
29+
func formatTargetLog(t Target) string {
30+
if t.Platform != "" {
31+
// Apple platform with Platform/Environment
32+
platformName := "iOS"
33+
if t.Platform == "tvos" {
34+
platformName = "tvOS"
35+
}
36+
if t.Environment == "simulator" {
37+
return fmt.Sprintf("%s Simulator %s", platformName, t.ARCH)
38+
}
39+
return fmt.Sprintf("%s %s", platformName, t.ARCH)
40+
}
41+
if t.Libc == "musl" {
42+
return fmt.Sprintf("%s/%s (musl)", t.GOOS, t.ARCH)
43+
}
44+
return fmt.Sprintf("%s/%s", t.GOOS, t.ARCH)
45+
}
46+
47+
// getOutputDirectory returns the GN output directory for a target
48+
func getOutputDirectory(t Target) string {
49+
if t.Platform != "" {
50+
// Apple platform: out/cronet-{platform}-{cpu}[-simulator]
51+
directory := fmt.Sprintf("out/cronet-%s-%s", t.Platform, t.CPU)
52+
if t.Environment == "simulator" {
53+
directory += "-simulator"
54+
}
55+
return directory
56+
}
57+
return fmt.Sprintf("out/cronet-%s-%s", t.OS, t.CPU)
58+
}
59+
2860
func build(targets []Target) {
2961
log.Printf("Building cronet_static for %d target(s)", len(targets))
3062

3163
for _, t := range targets {
32-
if t.Libc == "musl" {
33-
log.Printf("Building %s/%s (musl)...", t.GOOS, t.ARCH)
34-
} else {
35-
log.Printf("Building %s/%s...", t.GOOS, t.ARCH)
36-
}
64+
log.Printf("Building %s...", formatTargetLog(t))
3765
buildTarget(t)
3866
}
3967

@@ -171,7 +199,7 @@ func buildTarget(t Target) {
171199
// Run get-clang.sh to ensure toolchain is available
172200
runGetClang(t)
173201

174-
outputDirectory := fmt.Sprintf("out/cronet-%s-%s", t.OS, t.CPU)
202+
outputDirectory := getOutputDirectory(t)
175203

176204
// Prepare GN args
177205
args := []string{
@@ -250,10 +278,19 @@ func buildTarget(t Target) {
250278
"android_ndk_major_version=28",
251279
)
252280
case "ios":
281+
platform := t.Platform
282+
if platform == "" {
283+
platform = "iphoneos"
284+
}
285+
environment := t.Environment
286+
if environment == "" {
287+
environment = "device"
288+
}
253289
args = append(args,
254290
"use_sysroot=false",
255291
"ios_enable_code_signing=false",
256-
`target_environment="device"`,
292+
fmt.Sprintf(`target_platform="%s"`, platform),
293+
fmt.Sprintf(`target_environment="%s"`, environment),
257294
`ios_deployment_target="15.0"`,
258295
)
259296
}

cmd/build-naive/cmd_package.go

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func packageTargets(targets []Target) {
5757
targetDirectory := filepath.Join(libraryDirectory, getLibraryDirectoryName(t))
5858
os.MkdirAll(targetDirectory, 0o755)
5959

60-
outputDirectory := fmt.Sprintf("out/cronet-%s-%s", t.OS, t.CPU)
60+
outputDirectory := getOutputDirectory(t)
6161

6262
if t.GOOS == "windows" {
6363
// Windows: only copy DLL (static linking not supported - Chromium uses MSVC, Go CGO only supports MinGW)
@@ -74,14 +74,10 @@ func packageTargets(targets []Target) {
7474
sourceStatic := filepath.Join(srcRoot, outputDirectory, "obj/components/cronet/libcronet_static.a")
7575
destinationStatic := filepath.Join(targetDirectory, "libcronet.a")
7676
if _, err := os.Stat(sourceStatic); os.IsNotExist(err) {
77-
log.Printf("Warning: static library not found for %s/%s, skipping", t.GOOS, t.ARCH)
77+
log.Printf("Warning: static library not found for %s, skipping", formatTargetLog(t))
7878
} else {
7979
copyFile(sourceStatic, destinationStatic)
80-
if t.Libc == "musl" {
81-
log.Printf("Copied static library for %s/%s (musl)", t.GOOS, t.ARCH)
82-
} else {
83-
log.Printf("Copied static library for %s/%s", t.GOOS, t.ARCH)
84-
}
80+
log.Printf("Copied static library for %s", formatTargetLog(t))
8581
}
8682

8783
// For Linux glibc, also copy shared library (for testing and release, not for go module)
@@ -121,10 +117,65 @@ import "C"
121117

122118
// getLibraryDirectoryName returns the library directory name for a target
123119
func getLibraryDirectoryName(t Target) string {
120+
// Determine OS name for directory
121+
osName := t.GOOS
122+
if t.Platform == "tvos" {
123+
osName = "tvos"
124+
}
125+
126+
name := fmt.Sprintf("%s_%s", osName, t.ARCH)
127+
128+
// Add simulator suffix for simulator targets
129+
if t.Environment == "simulator" {
130+
name += "_simulator"
131+
}
132+
133+
// Add musl suffix for musl targets
134+
if t.Libc == "musl" {
135+
name += "_musl"
136+
}
137+
138+
return name
139+
}
140+
141+
// getBuildTag returns the Go build tag for a target
142+
func getBuildTag(t Target) string {
143+
// iOS/tvOS use gomobile-compatible tags
144+
if t.GOOS == "ios" {
145+
parts := []string{"ios", t.ARCH}
146+
147+
if t.Platform == "tvos" {
148+
// tvOS targets
149+
parts = append(parts, "tvos")
150+
if t.Environment == "simulator" {
151+
parts = append(parts, "tvossimulator")
152+
} else {
153+
parts = append(parts, "!tvossimulator")
154+
}
155+
} else {
156+
// iOS targets
157+
parts = append(parts, "!tvos")
158+
if t.Environment == "simulator" {
159+
parts = append(parts, "iossimulator")
160+
} else {
161+
parts = append(parts, "!iossimulator")
162+
}
163+
}
164+
165+
return strings.Join(parts, " && ")
166+
}
167+
168+
// Other platforms
124169
if t.Libc == "musl" {
125-
return fmt.Sprintf("%s_%s_musl", t.GOOS, t.ARCH)
170+
return fmt.Sprintf("%s && !android && %s && with_musl", t.GOOS, t.ARCH)
171+
}
172+
if t.GOOS == "linux" {
173+
return fmt.Sprintf("%s && !android && %s && !with_musl", t.GOOS, t.ARCH)
126174
}
127-
return fmt.Sprintf("%s_%s", t.GOOS, t.ARCH)
175+
if t.GOOS == "darwin" {
176+
return fmt.Sprintf("%s && !ios && %s", t.GOOS, t.ARCH)
177+
}
178+
return fmt.Sprintf("%s && %s", t.GOOS, t.ARCH)
128179
}
129180

130181
// LinkFlags contains linking parameters extracted from ninja build files
@@ -247,25 +298,16 @@ go 1.20
247298
}
248299

249300
// Extract linking flags from ninja file
250-
outputDirectory := fmt.Sprintf("out/cronet-%s-%s", t.OS, t.CPU)
301+
outputDirectory := getOutputDirectory(t)
251302
linkFlags, err := extractLinkFlags(outputDirectory)
252303
if err != nil {
253-
log.Fatalf("failed to extract link flags for %s/%s: %v", t.GOOS, t.ARCH, err)
304+
log.Fatalf("failed to extract link flags for %s: %v", formatTargetLog(t), err)
254305
}
255306

256-
log.Printf("Extracted link flags for %s/%s: libs=%v frameworks=%v ldflags=%v", t.GOOS, t.ARCH, linkFlags.Libs, linkFlags.Frameworks, linkFlags.LDFlags)
307+
log.Printf("Extracted link flags for %s: libs=%v frameworks=%v ldflags=%v", formatTargetLog(t), linkFlags.Libs, linkFlags.Frameworks, linkFlags.LDFlags)
257308

258309
// Build tags
259-
var buildTag string
260-
if t.Libc == "musl" {
261-
buildTag = fmt.Sprintf("%s && !android && %s && with_musl", t.GOOS, t.ARCH)
262-
} else if t.GOOS == "linux" {
263-
buildTag = fmt.Sprintf("%s && !android && %s && !with_musl", t.GOOS, t.ARCH)
264-
} else if t.GOOS == "darwin" {
265-
buildTag = fmt.Sprintf("%s && !ios && %s", t.GOOS, t.ARCH)
266-
} else {
267-
buildTag = fmt.Sprintf("%s && %s", t.GOOS, t.ARCH)
268-
}
310+
buildTag := getBuildTag(t)
269311

270312
// Generate libcronet_cgo.go with CGO config
271313
var ldFlags []string

0 commit comments

Comments
 (0)