[2026-05-12] taiga sync: family/.DS_Store family/_index.md family/how-to/htpc-access.md family/how-to/htpc-gaming-plans.md family/how-to/htpc-magic4pc.md family/how-to/htpc-migration-plan.md family/how-to/htpc-system.md family/how-to/htpc-webos-luna.md family/how-to/htpc.md family/how-to/magic4pc-webos.md
This commit is contained in:
+204
@@ -0,0 +1,204 @@
|
||||
```markdown
|
||||
# Conversation Summary
|
||||
|
||||
## Main Topic
|
||||
Exploration of Swift development across Windows, macOS, and Linux/Armbian — including:
|
||||
- Managing multiple Swift toolchains & PATH on Windows
|
||||
- Swift timers (Foundation.Timer, GCD timers, asyncAfter loops)
|
||||
- Swift Package Manager configuration
|
||||
- Cross-compiling Swift applications and tests to Linux (incl. Static Linux SDK)
|
||||
- Swift Concurrency examples (tasks, MainActor hops, async URL fetches)
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions & Findings
|
||||
|
||||
### Swift on Windows
|
||||
- Changing default toolchain requires editing **PATH** manually.
|
||||
- To view PATH:
|
||||
- PowerShell: `$env:Path -split ';'`
|
||||
- CMD: `echo %PATH%`
|
||||
- System PATH: `[Environment]::GetEnvironmentVariable("Path","Machine")`
|
||||
- Uninstall Swift:
|
||||
- Remove toolchain folders.
|
||||
- Clean PATH entries.
|
||||
|
||||
### Timer & GCD Behavior on Windows
|
||||
- `DispatchSource.makeTimerSource()` **crashes with illegal instruction** on Windows 10 → avoid.
|
||||
- Use **`DispatchQueue.main.asyncAfter` loop** or **Foundation.Timer** instead.
|
||||
|
||||
### Swift Package Manager
|
||||
- `platforms:` in `Package.swift` **only supports Apple platforms**.
|
||||
- Linux/Windows builds **must omit** the `platforms` constraint.
|
||||
- Can still use `.when(platforms: [.linux, .windows])` for conditional build flags.
|
||||
|
||||
### Swift Cross-Compilation
|
||||
- Swift supports **cross-compiling from macOS to Linux** via the Static Linux SDK.
|
||||
- Static builds produce **large binaries** (40–150MB), can be reduced with:
|
||||
- `-Osize`
|
||||
- `--gc-sections`
|
||||
- `strip` / `llvm-strip`
|
||||
- avoiding Foundation where possible
|
||||
- Dynamic builds (glibc) are much smaller but require Swift runtime on target.
|
||||
|
||||
### Swift Testing Framework
|
||||
- Yes — **Swift Testing** tests can be cross-compiled and shipped to run on Linux **without Swift installed** using the Static SDK:
|
||||
|
||||
```
|
||||
|
||||
swift build --build-tests --swift-sdk aarch64-swift-linux-musl -c release
|
||||
|
||||
```
|
||||
- Run the produced test binary directly on the target machine.
|
||||
|
||||
---
|
||||
|
||||
## Important Code Created
|
||||
|
||||
### 1. GCD asyncAfter repeating loop (Windows-safe)
|
||||
```swift
|
||||
import Foundation
|
||||
import Dispatch
|
||||
|
||||
func startRepeating(on queue: DispatchQueue, interval: TimeInterval) {
|
||||
queue.asyncAfter(deadline: .now() + interval) {
|
||||
print("[tick] \(Date())")
|
||||
startRepeating(on: queue, interval: interval)
|
||||
}
|
||||
}
|
||||
|
||||
print("Start…")
|
||||
startRepeating(on: .main, interval: 1.0)
|
||||
RunLoop.main.run()
|
||||
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
### 2. Full Swift Concurrency test app (tasks, hops, sleep, throwing)
|
||||
|
||||
```swift
|
||||
enum DemoError: Error, CustomStringConvertible {
|
||||
case boom(id: String)
|
||||
var description: String { "DemoError.boom(\(id))" }
|
||||
}
|
||||
|
||||
func sleepSeconds(_ seconds: Double) async throws {
|
||||
#if compiler(>=6.0)
|
||||
try await Task.sleep(for: .seconds(seconds))
|
||||
#else
|
||||
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
|
||||
#endif
|
||||
}
|
||||
|
||||
func runScenario(id: String, shouldThrow: Bool) async -> Result<Int,Error> {
|
||||
await MainActor.run { print("[\(id)] start on MainActor") }
|
||||
|
||||
let worker = Task.detached(priority: .background) { () throws -> Int in
|
||||
print("[\(id)] background: begin")
|
||||
try await sleepSeconds(1)
|
||||
print("[\(id)] background: after 1s")
|
||||
|
||||
if shouldThrow { throw DemoError.boom(id: id) }
|
||||
|
||||
try await sleepSeconds(1)
|
||||
print("[\(id)] background: after 2s")
|
||||
return id.unicodeScalars.reduce(100) { $0 + Int($1.value) }
|
||||
}
|
||||
|
||||
let result = await worker.result
|
||||
await MainActor.run { print("[\(id)] result: \(result)") }
|
||||
return result
|
||||
}
|
||||
|
||||
@main
|
||||
struct App {
|
||||
static func main() async {
|
||||
let t1 = Task { await runScenario(id:"A", shouldThrow:false) }
|
||||
let t2 = Task { await runScenario(id:"B", shouldThrow:true) }
|
||||
print(await t1.value, await t2.value)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
### 3. Enhanced concurrency test with real URLSession async fetches
|
||||
|
||||
```swift
|
||||
import Foundation
|
||||
#if canImport(FoundationNetworking)
|
||||
import FoundationNetworking
|
||||
#endif
|
||||
|
||||
enum DemoError: Error, CustomStringConvertible {
|
||||
case boom(id: String)
|
||||
var description: String { "DemoError.boom(\(id))" }
|
||||
}
|
||||
|
||||
func runScenario(id: String, shouldThrow: Bool) async -> Result<Int,Error> {
|
||||
await MainActor.run { print("[\(id)] start") }
|
||||
|
||||
let worker = Task.detached(priority: .background) { () throws -> Int in
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000)
|
||||
let session = URLSession(configuration: .ephemeral)
|
||||
|
||||
let (data1, _) = try await session.data(from: URL(string:"https://example.com")!)
|
||||
if shouldThrow {
|
||||
_ = try await session.data(from: URL(string:"https://invalid.invalid")!)
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000)
|
||||
let (data2, _) = try await session.data(from: URL(string:"https://worldtimeapi.org/api/timezone/Etc/UTC")!)
|
||||
|
||||
return data1.count + data2.count
|
||||
}
|
||||
|
||||
let result = await worker.result
|
||||
await MainActor.run { print("[\(id)] -> \(result)") }
|
||||
return result
|
||||
}
|
||||
|
||||
@main
|
||||
struct App {
|
||||
static func main() async {
|
||||
let r1 = await Task { await runScenario(id:"A", shouldThrow:false) }.value
|
||||
let r2 = await Task { await runScenario(id:"B", shouldThrow:true) }.value
|
||||
print(r1, r2)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
### 4. Minimal cross-platform `Package.swift`
|
||||
|
||||
```swift
|
||||
// swift-tools-version: 5.10
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MyApp",
|
||||
products: [ .executable(name: "MyApp", targets: ["MyApp"]) ],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "MyApp",
|
||||
swiftSettings: [ .unsafeFlags(["-Osize"], .when(configuration: .release)) ],
|
||||
linkerSettings: [ .unsafeFlags(["--gc-sections"], .when(platforms: [.linux])) ]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
## Unresolved / Open Items
|
||||
|
||||
- Detailed example of dynamic-linking cross-compile workflow (glibc) if reducing binary size further is needed.
|
||||
|
||||
- Automatic helper for selecting Swift toolchains and PATH switching on Windows.
|
||||
|
||||
- Setting up CI to run cross-compiled Swift Testing binaries on remote Linux/ARM targets.
|
||||
Reference in New Issue
Block a user