Skip to content

Move platform checks out of Package.swift to improve SwiftPM compatibility on Linux #762

@djpearce

Description

@djpearce

Hi team 👋, thanks for maintaining this project!

I’d like to propose a change to the Package.swift manifest that would improve compatibility with Swift tooling (such as Dependabot and CI environments) that run in Linux-based environments.

Problem
The current Package.swift file wraps several product and target definitions in a #if canImport(Darwin) block and a #if canImport(ObjectiveC) block:

func addPlatformSpecific() -> Self {
    #if canImport(ObjectiveC)
      dependencies.append(
        .package(url: "https://github.com/undefinedlabs/opentracing-objc", from: "0.5.2")
      )
      products.append(
        .library(name: "OpenTracingShim-experimental", targets: ["OpenTracingShim"])
      )
      targets.append(contentsOf: [
        .target(name: "OpenTracingShim",
                dependencies: [
                  "OpenTelemetrySdk",
                  .product(name: "Opentracing", package: "opentracing-objc")
                ],
                path: "Sources/Importers/OpenTracingShim",
                exclude: ["README.md"]),
        .testTarget(name: "OpenTracingShimTests",
                    dependencies: ["OpenTracingShim",
                                   "OpenTelemetrySdk"],
                    path: "Tests/ImportersTests/OpenTracingShim")
      ])
    #endif

    #if canImport(Darwin)
      dependencies.append(
        .package(url: "https://github.com/undefinedlabs/Thrift-Swift", from: "1.1.1")
      )
      products.append(contentsOf: [
        .library(name: "JaegerExporter", targets: ["JaegerExporter"]),
        .executable(name: "simpleExporter", targets: ["SimpleExporter"]),
        .library(name: "NetworkStatus", targets: ["NetworkStatus"]),
        .library(name: "URLSessionInstrumentation", targets: ["URLSessionInstrumentation"]),
        .library(name: "ZipkinExporter", targets: ["ZipkinExporter"]),
        .executable(name: "OTLPExporter", targets: ["OTLPExporter"]),
        .executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]),
        .library(name: "SignPostIntegration", targets: ["SignPostIntegration"]),
        .library(name: "ResourceExtension", targets: ["ResourceExtension"])
      ])
      targets.append(contentsOf: [
        .target(name: "JaegerExporter",
                dependencies: [
                  "OpenTelemetrySdk",
                  .product(name: "Thrift", package: "Thrift-Swift", condition: .when(platforms: [.iOS, .macOS, .tvOS, .macCatalyst, .linux]))
                ],
                path: "Sources/Exporters/Jaeger"),
        .testTarget(name: "JaegerExporterTests",
                    dependencies: ["JaegerExporter"],
                    path: "Tests/ExportersTests/Jaeger"),
        .executableTarget(name: "SimpleExporter",
                          dependencies: ["OpenTelemetrySdk", "JaegerExporter", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
                          path: "Examples/Simple Exporter",
                          exclude: ["README.md"]),
        .target(name: "NetworkStatus",
                dependencies: [
                  "OpenTelemetryApi"
                ],
                path: "Sources/Instrumentation/NetworkStatus",
                linkerSettings: [.linkedFramework("CoreTelephony", .when(platforms: [.iOS]))]),
        .testTarget(name: "NetworkStatusTests",
                    dependencies: [
                      "NetworkStatus"
                    ],
                    path: "Tests/InstrumentationTests/NetworkStatusTests"),
        .target(name: "URLSessionInstrumentation",
                dependencies: ["OpenTelemetrySdk", "NetworkStatus"],
                path: "Sources/Instrumentation/URLSession",
                exclude: ["README.md"]),
        .testTarget(name: "URLSessionInstrumentationTests",
                    dependencies: ["URLSessionInstrumentation",
                                   .product(name: "NIO", package: "swift-nio"),
                                   .product(name: "NIOHTTP1", package: "swift-nio")],
                    path: "Tests/InstrumentationTests/URLSessionTests"),
        .executableTarget(name: "NetworkSample",
                          dependencies: ["URLSessionInstrumentation", "StdoutExporter"],
                          path: "Examples/Network Sample",
                          exclude: ["README.md"]),
        .target(name: "ZipkinExporter",
                dependencies: ["OpenTelemetrySdk"],
                path: "Sources/Exporters/Zipkin"),
        .testTarget(name: "ZipkinExporterTests",
                    dependencies: ["ZipkinExporter"],
                    path: "Tests/ExportersTests/Zipkin"),
        .executableTarget(name: "OTLPExporter",
                          dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterGrpc", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"],
                          path: "Examples/OTLP Exporter",
                          exclude: ["README.md"]),
        .executableTarget(name: "OTLPHTTPExporter",
                          dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration", "DataCompression"],
                          path: "Examples/OTLP HTTP Exporter",
                          exclude: ["README.md"]),
        .target(name: "SignPostIntegration",
                dependencies: ["OpenTelemetrySdk"],
                path: "Sources/Instrumentation/SignPostIntegration",
                exclude: ["README.md"]),
        .target(name: "ResourceExtension",
                dependencies: ["OpenTelemetrySdk"],
                path: "Sources/Instrumentation/SDKResourceExtension",
                exclude: ["README.md"]),
        .testTarget(name: "ResourceExtensionTests",
                    dependencies: ["ResourceExtension", "OpenTelemetrySdk"],
                    path: "Tests/InstrumentationTests/SDKResourceExtensionTests"),
        .executableTarget(name: "PrometheusSample",
                          dependencies: ["OpenTelemetrySdk", "PrometheusExporter"],
                          path: "Examples/Prometheus Sample",
                          exclude: ["README.md"])
      ])
    #endif

    return self
  }

This prevents SwiftPM on Linux from resolving the manifest at all, since it cannot see those conditionally defined targets/products. As a result, tooling like Dependabot fails with errors such as:

product 'URLSessionInstrumentation' required by target 'OpenTelemetryClient' not found in package 'opentelemetry-swift'.

Proposed Solution
Move the platform specific checks out of the manifest and into the source files. This allows SwiftPM to always see all products and targets, while keeping platform-specific implementation details correctly guarded.

In Package.swift:

Define all products and targets unconditionally:

.target(name: "URLSessionInstrumentation", dependencies: [ ... ]),
.target(name: "OtherDarwinOnlyTarget", dependencies: [ ... ]),
.product(name: "URLSessionInstrumentation", package: "opentelemetry-swift"),
...

In the implementation (e.g., Sources/URLSessionInstrumentation/*.swift):

Guard platform-specific logic:

#if canImport(Darwin)
// Real implementation
#else
// Stub types or empty implementations
#endif

I would recommend doing this to:

  • Keeps Darwin-only logic intact
  • Makes the package manifest parseable on all platforms
  • Unblocks tooling like Dependabot, SwiftPM indexers, and cross-platform CI/CD workflows
  • Follows a pattern used in other popular Swift packages (e.g. Alamofire)

Let me know if you’re open to this change, I’d be happy to help contribute a PR if useful!

Thanks again 🙏

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions