swiftswiftpm

Conditionally including a system library (FFmpeg) in Swift Package Manager builds?


I’m working on a Swift project and would like to create two builds of my package:

Here’s what I have so far is two separate Package.swift files:

    ...
    targets: [
        .executableTarget(
            name: "macSubtitleOCR",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser")
            ]),
    ...

and:

    ...
    targets: [
        .systemLibrary(
            name: "CFFmpeg",
            pkgConfig: "libavformat libavcodec libavutil",
            providers: [
                .brew(["ffmpeg"])
            ]),
        .executableTarget(
            name: "macSubtitleOCR",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
                "CFFmpeg"
            ]),
     ...

I've tried adding compile flags like #if FFMPEG and then running swift build -Xswiftc -DFFMPEG which works for the source files, but compile flags don't work properly, and understandably so, in Package.swift.

My question is:

  1. How can I conditionally include a system library like FFmpeg in Package.swift based on build flags or any other approach?
  2. Is using multiple Package.swift files (one with and one without FFmpeg) a reasonable approach, or is there a more correct way to handle optional dependencies in SwiftPM?

I’m relatively new to Swift development, so any guidance or alternative approaches would be greatly appreciated!


Solution

  • Swift Package Manager (SwiftPM) doesn’t directly support conditional dependencies, but you can manage optional dependencies using environment variables. This approach enables conditional inclusion of a dependency in a single Package.swift file.

    Solution

    Define an environment variable and use conditional logic in Package.swift to include the CFFmpeg target only when needed:

    import PackageDescription
    
    let hasFFmpeg = ProcessInfo.processInfo.environment["USE_FFMPEG"] == "1"
    
    let package = Package(
        name: "macSubtitleOCR",
        dependencies: [
            .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0")
        ],
        targets: [
            .executableTarget(
                name: "macSubtitleOCR",
                dependencies: [
                    .product(name: "ArgumentParser", package: "swift-argument-parser")
                ] + (hasFFmpeg ? ["CFFmpeg"] : [])
            )
        ] + (hasFFmpeg ? [
            .systemLibrary(
                name: "CFFmpeg",
                pkgConfig: "libavformat libavcodec libavutil",
                providers: [
                    .brew(["ffmpeg"])
                ]
            )
        ] : [])
    )