Skip to content

Conversation

techouse
Copy link
Owner

@techouse techouse commented Sep 16, 2025

This pull request introduces experimental Linux support for QsSwift by conditionally using ReerKit’s WeakMap as a replacement for NSMapTable on non-Apple platforms. It updates the build system, adapts the encoder’s cycle-detection logic for Linux, and adjusts tests and documentation to reflect cross-platform behavior and known issues. Some tests are marked as known issues or adjusted for Linux-specific differences, and CI now includes an experimental Ubuntu job.

Linux platform support and dependency changes:

  • Added ReerKit as a conditional dependency for Linux in both Package.swift and Package@swift-5.10.swift, enabling the use of WeakMap as a weak-to-weak object map on non-Apple platforms. (Package.swift, Package@swift-5.10.swift) [1] [2] [3] [4]
  • Introduced a Linux-specific shim (NSMapTable+Linux.swift) that adapts ReerKit’s WeakMap to the minimal API surface required by the encoder, allowing transparent use of NSMapTable.weakToWeakObjects() on Linux.

Continuous Integration and workflow updates:

  • Added a new linux-tests job to the GitHub Actions workflow, running Swift tests on Ubuntu with continue-on-error enabled for experimental support. Updated job names for clarity regarding platform and toolchain. (.github/workflows/test.yml) [1] [2] [3]

Documentation and project status:

  • Removed the outdated LinuxSupport.md and updated the README.md to document experimental Linux support, the use of ReerKit, and caveats regarding test failures and platform differences. [1] [2]

Test suite adjustments for cross-platform compatibility:

  • Modified various tests to handle Linux-specific issues, such as self-referential NSDictionary crashes, MainActor thread guarantees, and Shift_JIS encoding/decoding differences. Tests that cannot be reliably run on Linux are marked with withKnownIssue or have expectations adjusted. (Tests/QsSwiftTests/ConvenienceTests.swift, Tests/QsSwiftTests/CoreExtrasTests.swift, Tests/QsSwiftTests/DecodeTests.swift, Tests/QsSwiftTests/EncodeTests.swift) [1] [2] [3] [4] [5] [6] [7] [8] [9]

Development environment and ignore settings:

  • Updated .vscode/settings.json to exclude .cache directories from the editor’s file search and indexing.

Summary by CodeRabbit

  • New Features

    • Experimental Linux support (Swift 6.0+): Linux shim for weak-to-weak maps and Linux-only dependency added; manifest updated for platform-specific deps.
  • Documentation

    • README rewritten with Linux guidance and caveats; standalone LinuxSupport.md removed; logo URL fixed.
  • Tests

    • Extensive platform-aware test changes: Linux branches, known-issue wrappers, adjusted expectations, and new Linux-focused tests.
  • Chores

    • CI: added Ubuntu test job (continue-on-error) and clarified workflow job names.
    • Repo hygiene: added cache/core ignore patterns and excluded .cache in editor settings.
    • Tools/script now Darwin-guarded.

@techouse techouse self-assigned this Sep 16, 2025
@techouse techouse added the enhancement New feature or request label Sep 16, 2025
Copy link

coderabbitai bot commented Sep 16, 2025

Walkthrough

Adds Linux support scaffolding: introduces a Linux NSMapTable shim using ReerKit, conditions package manifests to include ReerKit on Linux, adapts many tests with Linux guards/known-issue wrappers, revises README and removes LinuxSupport.md, updates CI with an Ubuntu job (duplicated), updates ignores and Darwin-guards a tools script.

Changes

Cohort / File(s) Summary
CI workflow updates
.github/workflows/test.yml
Renamed two job display names to include platform context; added an Ubuntu linux-tests job (runs-on ubuntu-latest, timeout 40m, continue-on-error: true, depends on analyze, caches SwiftPM, prints dmesg on failure). The linux-tests block appears duplicated. Minor whitespace tweak in ensure-compatibility step.
Ignore / editor config
.gitignore, .vscode/settings.json
Added ignore patterns: .swiftpm/cache, .cache, core, core.*, vgcore.*; added "**/.cache/**" to VS Code local-history.exclude.
Documentation
README.md, LinuxSupport.md
README.md updated: corrected logo URL and new "Linux support" (Experimental) section describing ReerKit WeakMap approach and CI caveats; LinuxSupport.md deleted.
Package manifests & lock
Package.swift, Package@swift-5.10.swift, Package.resolved
Refactored to top-level deps and targetDeps arrays; append ReerKit dependency and product on Linux via #if os(Linux); Package.resolved originHash updated.
Linux NSMapTable shim
Sources/QsSwift/Internal/NSMapTable+Linux.swift
New Linux-only shim implementing NSMapTable<Key: AnyObject, Value: AnyObject> backed by ReerKit.WeakMap, providing weakToWeakObjects(), setObject(_:forKey:), and object(forKey:); compile-time error if ReerKit missing.
Tests adapted for Linux
Tests/QsSwiftTests/* (multiple)
Added #if os(Linux) branches and withKnownIssue wrappers, adjusted expectations and control flow for corelibs-foundation differences (NSNumber/CFNumber, Shift_JIS), marked or skipped unstable cases on Linux, added a Linux-only NSMapTable shim test, and changed several test signatures to throws or split Darwin/Linux variants. Files: ConvenienceTests.swift, CoreExtrasTests.swift, DecodeTests.swift, EncodeTests.swift, UtilsTests.swift.
Tools script gating
Tools/QsSwiftComparison/main.swift
Wrapped entire script in #if canImport(Darwin) so it is a no-op on non‑Darwin platforms; logic preserved inside the guard.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant GH as GitHub Actions
  participant Analyze as analyze
  participant MacSwift as tests (macOS)
  participant ObjC as objc-tests (macOS)
  participant Linux as linux-tests (Ubuntu)

  Dev->>GH: Push / PR
  GH->>Analyze: Run analyze
  Analyze-->>GH: Completed
  GH->>MacSwift: Run Swift tests + coverage (macOS, Xcode)
  GH->>ObjC: Run Objective‑C E2E tests (macOS, Xcode)
  GH->>Linux: Run Swift tests (Linux) (continue‑on‑error)
  Note over Linux: Setup Swift, cache SwiftPM, run tests<br/>On failure: print dmesg
  MacSwift-->>GH: Status
  ObjC-->>GH: Status
  Linux-->>GH: Status (may be non‑blocking)
Loading
sequenceDiagram
  autonumber
  participant App as App Code
  participant Qs as QsSwift Encoder
  participant Darwin as Foundation.NSMapTable
  participant Shim as NSMapTable+Linux
  participant RK as ReerKit.WeakMap

  App->>Qs: Encode object graph
  alt Darwin platform
    Qs->>Darwin: NSMapTable.weakToWeakObjects()
    Qs->>Darwin: setObject / object(forKey:)
  else Linux / non‑Darwin
    Qs->>Shim: NSMapTable.weakToWeakObjects()
    Shim->>RK: create WeakMap (weak keys/values)
    Qs->>Shim: setObject / object(forKey:)
    Shim->>RK: store/retrieve (weak semantics)
  end
  Qs-->>App: Encoded output (with cycle detection)
  note over Shim,RK: Shim requires ReerKit and compiles only on Linux
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

hop hop, I sniff the weak map trail,
ReerKit burrowed in a Linux vale.
Tests wrapped gently, CI ticks and hums,
README whispers: "Experimental"—it comes.
the rabbit hops on—builds compile with smiles 🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description provides a thorough, file-level summary and clear motivation for adding experimental Linux support, but it does not follow the repository's required template: it is missing the "Fixes #(issue)" line, the "Type of change" section with checkbox selections, the "How Has This Been Tested?" section with concrete test steps/results and checkboxes, and the required checklist items. Because those required template sections and explicit testing/checklist information are absent, the description does not fully meet the repository's template requirements. Update the PR description to match the repository template by adding a "Fixes #" line if applicable, filling the "Type of change" section (selecting the appropriate checkbox), providing concrete testing steps and results under "How Has This Been Tested?" (including commands, platforms, and which unit tests passed), and completing the checklist or explaining any items that are not applicable.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title ":children_crossing: add Linux support" is concise and accurately summarizes the primary change (adding experimental Linux support) so it reflects the main changes in the PR; it is directly related to the changeset. The emoji and lowercase styling are minor stylistic noise but do not make the title misleading.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-linux-support

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b92d047 and 8f8df0b.

📒 Files selected for processing (1)
  • Tests/QsSwiftTests/ConvenienceTests.swift (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Tests/QsSwiftTests/ConvenienceTests.swift
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Swift tests + coverage (macOS, Xcode)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Analyze (swift)

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@techouse techouse changed the title Feat/add linux support 🚸 add Linux support Sep 16, 2025
@techouse techouse added the linux Linux specific label Sep 16, 2025
Copy link

codecov bot commented Sep 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.27%. Comparing base (5b2a5fb) to head (8f8df0b).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main       #6   +/-   ##
=======================================
  Coverage   86.27%   86.27%           
=======================================
  Files          53       53           
  Lines        3453     3453           
=======================================
  Hits         2979     2979           
  Misses        474      474           
Flag Coverage Δ
macos-15 86.27% <ø> (ø)
swift 86.27% <ø> (ø)
swift-6.1 86.27% <ø> (ø)
xcode-16.3 86.27% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@techouse techouse marked this pull request as ready for review September 17, 2025 21:42
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (15)
.vscode/settings.json (1)

17-17: Also exclude .cache from VS Code search/indexing

Add the same pattern to files.exclude (and optionally search.exclude) to reduce noise and speed up indexing.

   "files.exclude": {
     "**/.build": true,
-    "**/.swiftpm": true
+    "**/.swiftpm": true,
+    "**/.cache": true
   },
+ "search.exclude": {
+   "**/.cache/**": true
+ },
.gitignore (1)

8-8: Tighten ignores for caches and core dumps

  • Prefer directory form for SwiftPM cache.
  • Core dumps can appear as core. or vgcore.; ignore those too.
-.swiftpm/cache
+.swiftpm/cache/
 .netrc
 .history
 .idea
-.cache
+.cache/
 coverage/
 docs/
-core
+core
+core.*
+vgcore.*

Also applies to: 12-12, 15-15

Package.swift (1)

18-18: Linux‑only dependency fetch (optional)

SPM will still fetch ReerKit on non‑Linux since the top‑level dependency is unconditional. If you want to avoid that, gate the package declaration in the manifest (host‑evaluated) and keep the target dependency condition as-is.

var deps: [Package.Dependency] = [
  .package(url: "https://github.com/apple/swift-algorithms.git", from: "1.2.1"),
  .package(url: "https://github.com/apple/swift-collections.git", from: "1.2.1"),
  .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"),
]
#if os(Linux)
deps.append(.package(url: "https://github.com/reers/ReerKit.git", from: "1.1.9"))
#endif

let package = Package(
  // ...
  dependencies: deps,
  // ...
)
Tests/QsSwiftTests/UtilsTests.swift (1)

448-452: Avoid force‑cast in test; use helper for safer downcast

Use asDictAnyHashable(result) to avoid a hard crash if the shape changes.

-        let out = result as! [AnyHashable: Any]
+        guard let out = asDictAnyHashable(result) else {
+            #expect(Bool(false), "unexpected result type")
+            return
+        }
Tests/QsSwiftTests/DecodeTests.swift (1)

3014-3029: Shift_JIS on Linux: graceful probe + known‑issue path

Good early‑return structure. Tiny nit: consider logging the actual decoded value when it fails to aid future triage.

-        } else {
-            try withKnownIssue {
+        } else {
+            try withKnownIssue("Got: \(decoded.debugDescription)") {
Tests/QsSwiftTests/ConvenienceTests.swift (1)

23-34: Make Linux expectation deterministic or mark as known issue.

Allowing either nil or "" can mask regressions. Prefer skipping via withKnownIssue (consistent with other tests) or assert a single outcome via encodeOrEmpty on Linux.

-        #if os(Linux)
-            // On Linux, a self-referential NSDictionary can crash due to corelibs-foundation differences.
-            // Using an unsupported top-level type should fail; corelibs may return "" instead of nil.
-            let input: Any = NSNumber(value: 1)
-            let got = Qs.encodeOrNil(input)
-            #expect(got == nil || got == "")
-        #else
+        #if os(Linux)
+            try withKnownIssue(Comment("Linux: corelibs-foundation segfault constructing NSDictionary self-cycle")) {
+                #expect(Bool(false), Comment("Skipped: cannot safely build a cyclic container on Linux"))
+            }
+        #else
             let inner = NSMutableDictionary()
             inner["self"] = inner  // reference cycle
             let input: [String: Any] = ["a": inner]  // supported top-level type
             #expect(Qs.encodeOrNil(input) == nil)
         #endif
.github/workflows/test.yml (1)

253-299: Linux job likely calls a macOS‑specific coverage script; run swift test directly on Linux.

coverage.sh commonly uses xcodebuild/xccov (Darwin only). Use swift test here to get deterministic Linux results.

-      - name: Run tests (Linux)
-        env:
-          SKIP_EXPENSIVE_TESTS: 1
-          SWIFT_DETERMINISTIC_HASHING: 1
-        run: bash scripts/coverage.sh
+      - name: Run tests (Linux)
+        env:
+          SKIP_EXPENSIVE_TESTS: 1
+          SWIFT_DETERMINISTIC_HASHING: 1
+        run: |
+          set -euo pipefail
+          swift --version
+          swift test --parallel --configuration debug
README.md (2)

4-4: Fix duplicated query string in logo URL.

Small nit: ?raw=true is duplicated.

-    <img src="https://github.com/techouse/qs-swift/raw/main/logo.png?raw=true?raw=true" width="256" alt="QsSwift" />
+    <img src="https://github.com/techouse/qs-swift/raw/main/logo.png?raw=true" width="256" alt="QsSwift" />

564-571: Address markdownlint: use a proper heading instead of emphasis.

Conforms to MD036/MD049.

-**Experimental** (Swift 6.0+)
+### Experimental (Swift 6.0+)
Tools/QsSwiftComparison/main.swift (1)

1-197: Darwin‑guarded tool is fine; consider a helpful message on non‑Apple.

Optional: print a short note when run on Linux so users know it’s intentionally inactive.

 #if canImport(Darwin)
   ...
 #else
-    // nothing on non-Apple platforms
+    import Foundation
+    fputs("QsSwiftComparison: this tool runs on Apple platforms only.\n", stderr)
 #endif
Tests/QsSwiftTests/EncodeTests.swift (2)

175-189: NSNumber “integer‑like” detection on Linux can misclassify large values (> 2^53).

Double round‑trip equality fails for large Int64 that can’t be exactly represented in Double; this is a test encoder, but worth noting.

  • If you need robustness, prefer inspecting NSNumber.objCType (when available) or Decimal to test isIntegral without going through Double.

3246-3262: Strengthen Linux shim test to observe weak semantics when possible.

Non‑deterministic GC makes strict asserts hard, but you can at least exercise lookup after scoped lifetimes.

-        var k: Foo? = Foo()
-        var v: Foo? = Foo()
-        side.setObject(v!, forKey: k!)
-        #expect(side.object(forKey: k!) != nil)
-        // Drop strong refs; cannot force collection deterministically here.
-        k = nil
-        v = nil
-        // Just exercising API without crash is sufficient.
-        #expect(true)
+        do {
+            let k = Foo()
+            let v = Foo()
+            side.setObject(v, forKey: k)
+            #expect(side.object(forKey: k) != nil)
+        }
+        // Post-scope, both key and value had only weak references in the table.
+        // We can’t force ARC to collect deterministically; existence checks here are best‑effort.
+        #expect(true)
Sources/QsSwift/Internal/NSMapTable+Linux.swift (3)

1-5: Scope the guard to Linux and align the error message.

If Linux is the only non‑Darwin target, prefer #if os(Linux) to avoid breaking other future non‑Darwin ports (e.g., Windows). Also, make the #error message Linux‑specific for clarity.

-#if !canImport(Darwin)
+#if os(Linux)
@@
-    #else
-        #error("ReerKit is required on non-Apple platforms. Add it as a conditional dependency for Linux.")
-    #endif
+    #else
+        #error("ReerKit is required on Linux. Add it as a conditional dependency for Linux in Package.swift.")
+    #endif
 #endif

Also applies to: 39-41


21-30: Don’t remove existing entries when value isn’t an NSObject; assert instead.

Currently, passing a non‑NSObject value removes the mapping, which is surprising and can drop data silently. Also, silently ignoring non‑NSObject keys hides misuse.

-            @inline(__always)
-            func setObject(_ obj: Value?, forKey key: Key) {
-                guard let keyNS = key as? NSObject else { return }
-                if let vNS = obj as? NSObject {
-                    map[keyNS] = vNS
-                } else {
-                    _ = map.removeValue(forKey: keyNS)
-                }
-            }
+            @inline(__always)
+            func setObject(_ obj: Value?, forKey key: Key) {
+                guard let keyNS = key as? NSObject else {
+                    assertionFailure("NSMapTable(Linux shim) requires keys to be NSObject-backed.")
+                    return
+                }
+                guard let obj = obj else {
+                    _ = map.removeValue(forKey: keyNS)
+                    return
+                }
+                guard let vNS = obj as? NSObject else {
+                    assertionFailure("NSMapTable(Linux shim) requires values to be NSObject-backed.")
+                    return
+                }
+                map[keyNS] = vNS
+            }

22-22: Drop @inline(__always); let the optimizer decide.

These are tiny methods; forcing inlining is unnecessary and can hinder diagnostics across modules.

-            @inline(__always)
             func setObject(_ obj: Value?, forKey key: Key) {
@@
-            @inline(__always)
             func object(forKey key: Key) -> Value? {

Also applies to: 33-33

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b2a5fb and e849152.

📒 Files selected for processing (15)
  • .github/workflows/test.yml (4 hunks)
  • .gitignore (1 hunks)
  • .vscode/settings.json (1 hunks)
  • LinuxSupport.md (0 hunks)
  • Package.resolved (1 hunks)
  • Package.swift (2 hunks)
  • Package@swift-5.10.swift (2 hunks)
  • README.md (1 hunks)
  • Sources/QsSwift/Internal/NSMapTable+Linux.swift (1 hunks)
  • Tests/QsSwiftTests/ConvenienceTests.swift (3 hunks)
  • Tests/QsSwiftTests/CoreExtrasTests.swift (1 hunks)
  • Tests/QsSwiftTests/DecodeTests.swift (2 hunks)
  • Tests/QsSwiftTests/EncodeTests.swift (7 hunks)
  • Tests/QsSwiftTests/UtilsTests.swift (3 hunks)
  • Tools/QsSwiftComparison/main.swift (1 hunks)
💤 Files with no reviewable changes (1)
  • LinuxSupport.md
🧰 Additional context used
🧬 Code graph analysis (4)
Tests/QsSwiftTests/ConvenienceTests.swift (2)
Sources/QsObjC/QsBridge+EncodeConvenience.swift (4)
  • encodeOrNil (25-28)
  • encodeOrNil (36-45)
  • encodeOrEmpty (54-57)
  • encodeOrEmpty (65-74)
Sources/QsSwift/Internal/Utils.swift (1)
  • isEmpty (45-63)
Tests/QsSwiftTests/CoreExtrasTests.swift (3)
Sources/QsObjC/QsBridge.swift (1)
  • encode (62-79)
Sources/QsSwift/Internal/Encoder.swift (1)
  • encode (34-413)
Sources/QsSwift/Qs+Encode.swift (1)
  • encode (41-259)
Tests/QsSwiftTests/UtilsTests.swift (1)
Sources/QsSwift/Internal/Utils+NumericEntities.swift (1)
  • interpretNumericEntities (11-188)
Tests/QsSwiftTests/EncodeTests.swift (4)
Sources/QsObjC/QsBridge.swift (1)
  • encode (62-79)
Sources/QsSwift/Internal/Encoder.swift (1)
  • encode (34-413)
Sources/QsSwift/Qs+Encode.swift (1)
  • encode (41-259)
Sources/QsSwift/Internal/NSMapTable+Linux.swift (1)
  • weakToWeakObjects (19-19)
🪛 markdownlint-cli2 (0.17.2)
README.md

572-572: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


573-573: Emphasis style
Expected: underscore; Actual: asterisk

(MD049, emphasis-style)


573-573: Emphasis style
Expected: underscore; Actual: asterisk

(MD049, emphasis-style)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Analyze (swift)
🔇 Additional comments (16)
Package.swift (1)

27-27: LGTM: target‑scoped, Linux‑only product dependency

Conditional product on .linux is correct and keeps Apple builds unaffected.

Tests/QsSwiftTests/UtilsTests.swift (2)

692-694: LGTM: clearer expectations for Undefined overlay behavior

Comments + explicit element checks improve readability across platforms.


850-850: LGTM: hex lower‑case coverage

Nice addition to cover 'J' → 'J'.

Package.resolved (1)

2-12: Pin matches manifest; OK to commit

ReerKit 1.1.9 pin aligns with Package.swift.

Tests/QsSwiftTests/DecodeTests.swift (1)

1676-1686: Linux MainActor expectation handled as a known issue

Reasonable compromise to record the divergence without failing the suite outright.

Please confirm your CI’s swift-testing version supports withKnownIssue on Linux; older builds may lack it.

Tests/QsSwiftTests/CoreExtrasTests.swift (1)

22-40: LGTM: sensible Linux skip around known corelibs segfault; Darwin path asserts the real contract.

Tests/QsSwiftTests/ConvenienceTests.swift (2)

44-54: LGTM: Linux fallback via unsupported top-level type keeps the contract “empty on failure.”


101-111: LGTM: MainActor assertion adapted for Linux runtime semantics.

Package@swift-5.10.swift (1)

18-29: LGTM: ReerKit added and scoped to Linux target dependency.

One caveat: SPM still fetches all declared dependencies during resolution on any platform; acceptable tradeoff here.

Tests/QsSwiftTests/EncodeTests.swift (3)

6-8: LGTM: CF import is correctly Darwin‑gated.


2211-2311: LGTM: Cycle tests are safely skipped on Linux and assert Encoder behavior on Darwin.


1-3298: Confirmed: Linux NSMapTable shim is weak‑to‑weak via ReerKit WeakMap

Sources/QsSwift/Internal/NSMapTable+Linux.swift uses ReerKit’s WeakMap<WeakKeyValue, NSObject, NSObject> and implements weakToWeakObjects(), setObject(_:forKey:), and object(forKey:) — keys and values are stored weakly (not a strong map).

.github/workflows/test.yml (1)

253-299: Confirm no duplicate linux-tests job and verify coverage.sh is Linux-safe

  • Found a single linux-tests job in .github/workflows/test.yml (line 253).
  • scripts/coverage.sh exists and includes macOS-specific xcrun logic (around lines 153–156); ensure those commands are guarded for non‑macOS runners and search the repo for other references to coverage.sh or additional linux-tests jobs.
Sources/QsSwift/Internal/NSMapTable+Linux.swift (3)

18-20: Access level: confirm intent (internal vs public).

Placed under Internal/, these APIs are currently internal. If external users need to call weakToWeakObjects()/setObject/object(forKey:) on Linux, mark the type and members public (and @inlinable if cross‑module inlining is desired). Otherwise, keep as is.


6-11: Nice, minimal shim with clear compiler gating.

Good isolation behind platform checks and a fail‑fast when ReerKit is missing. This keeps call sites uniform across platforms.


12-16: Resolved — ReerKit WeakMap uses identity (pointer) semantics; no change required

ReerKit’s WeakMap is identity-based and the WeakKeyValue variant preserves pointer semantics for keys, so WeakMap<WeakKeyValue, NSObject, NSObject>() matches NSMapTable’s pointer-personality; no changes needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
Tests/QsSwiftTests/EncodeTests.swift (1)

2218-2230: Linux cycle tests: consistent known‑issue wrapper is good; ensure all such tests are throws

Pattern is consistent and avoids UB on Linux. Quick pass: these are marked throws (good). Keep the skip reason messages short and uniform for easier grepping.

Also applies to: 2240-2251, 2253-2277, 2279-2305, 2712-2725, 2842-2855, 2858-2872, 2874-2888, 2891-2907, 2909-2924

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e849152 and b92d047.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • Package.resolved (1 hunks)
  • Package.swift (2 hunks)
  • Package@swift-5.10.swift (2 hunks)
  • README.md (2 hunks)
  • Sources/QsSwift/Internal/NSMapTable+Linux.swift (1 hunks)
  • Tests/QsSwiftTests/ConvenienceTests.swift (3 hunks)
  • Tests/QsSwiftTests/DecodeTests.swift (2 hunks)
  • Tests/QsSwiftTests/EncodeTests.swift (7 hunks)
  • Tests/QsSwiftTests/UtilsTests.swift (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • Package.resolved
  • Tests/QsSwiftTests/UtilsTests.swift
  • Tests/QsSwiftTests/DecodeTests.swift
  • .gitignore
  • Package@swift-5.10.swift
🧰 Additional context used
🧬 Code graph analysis (1)
Tests/QsSwiftTests/EncodeTests.swift (6)
Sources/QsObjC/QsBridge.swift (1)
  • encode (62-79)
Sources/QsSwift/Internal/Encoder.swift (1)
  • encode (34-413)
Sources/QsSwift/Qs+Encode.swift (1)
  • encode (41-259)
Sources/QsObjC/QsBridge+EncodeConvenience.swift (4)
  • encodeOrNil (25-28)
  • encodeOrNil (36-45)
  • encodeOrEmpty (54-57)
  • encodeOrEmpty (65-74)
Sources/QsSwift/Qs+EncodeConvenience.swift (2)
  • encodeOrNil (14-20)
  • encodeOrEmpty (31-37)
Sources/QsSwift/Internal/NSMapTable+Linux.swift (1)
  • weakToWeakObjects (19-19)
🪛 markdownlint-cli2 (0.17.2)
README.md

572-572: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


573-573: Emphasis style
Expected: underscore; Actual: asterisk

(MD049, emphasis-style)


573-573: Emphasis style
Expected: underscore; Actual: asterisk

(MD049, emphasis-style)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Swift tests (Linux)
  • GitHub Check: Swift tests + coverage (macOS, Xcode)
  • GitHub Check: Analyze (swift)
🔇 Additional comments (10)
Tests/QsSwiftTests/ConvenienceTests.swift (2)

42-53: LGTM: Linux fallback verifies contract without unsafe self-cycle

Good adaptation to avoid the corelibs-foundation crash while still asserting the empty-on-failure contract.


99-110: LGTM: MainActor check adjusted for Linux semantics

Reasonable platform distinction (MainActor ≠ OS main thread on Linux).

README.md (2)

4-4: LGTM: fixed logo URL

The duplicate ?raw=true was removed correctly.


564-575: Tidy README headings and clarify Linux/Swift versions

  • Replace emphasized labels with headings (nit). Apply diff below.
  • README's "Experimental (Swift 6.0+)" is misleading — the repo includes Package@swift-5.10.swift which conditionally adds ReerKit for Linux, so Linux support exists for Swift 5.10; clarify whether the experimental note refers only to the main 6.1 manifest or update the README to reference the alternate manifest.

Apply:

-## Linux support
-
-**Experimental** (Swift 6.0+)
+## Linux support
+
+### Experimental (Swift 6.0+)
@@
-**Caveats**
+### Caveats

Likely an incorrect or invalid review comment.

Sources/QsSwift/Internal/NSMapTable+Linux.swift (1)

39-41: LGTM: explicit compile‑time failure when ReerKit is absent

Clear error helps users wire the Linux dep correctly.

Tests/QsSwiftTests/EncodeTests.swift (4)

6-8: LGTM: Darwin‑guarded CoreFoundation import

Correctly avoids pulling CF on Linux.


174-189: LGTM with a couple of edge‑case checks for Linux NSNumber fallback

The integer‑like heuristic is reasonable. Please add quick asserts for -0.0, nan, infinity, and a large magnitude Double to ensure no accidental “n” suffixing.

Example additions:

// -0.0 -> not integer-like on Darwin, Linux fallback should treat as integer-like; ensure desired behavior
#expect(encodeWithN(NSNumber(value: -0.0), "", .utf8) == "0n" || encodeWithN(NSNumber(value: -0.0), "", .utf8) == "0")
// NaN/inf -> never integer-like
#expect(!encodeWithN(NSNumber(value: Double.nan), "", .utf8).hasSuffix("n"))
#expect(!encodeWithN(NSNumber(value: Double.infinity), "", .utf8).hasSuffix("n"))
#expect(!encodeWithN(NSNumber(value: -Double.infinity), "", .utf8).hasSuffix("n"))
// Large Double not exactly representable as Int64
#expect(!encodeWithN(NSNumber(value: 9.22e18), "", .utf8).hasSuffix("n"))

1481-1503: LGTM: Shift_JIS test gracefully handles Linux availability

Good capability probe with a known‑issue path when unsupported.


3247-3262: Augment shim test: include a plain Swift class (non‑NSObject) key/value

To validate the shim truly mimics NSMapTable<AnyObject,AnyObject>, add a case using a Swift class that does not inherit from NSObject. This will also catch the current shim’s NSObject constraint if it slips back.

Example:

final class Plain {}
let side = NSMapTable<AnyObject, AnyObject>.weakToWeakObjects()
do {
    let k = Plain()
    let v = Plain()
    side.setObject(v, forKey: k)
    #expect(side.object(forKey: k) != nil)
}
Package.swift (1)

6-18: LGTM: Linux-only ReerKit dependency wiring looks correct
Confirmed identical #if os(Linux) blocks adding ReerKit to deps and targetDeps in Package.swift and Package@swift-5.10.swift.

@techouse techouse merged commit 52c794f into main Sep 17, 2025
16 of 17 checks passed
@techouse techouse deleted the feat/add-linux-support branch September 17, 2025 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request linux Linux specific
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant