SPM Dependency Causes System Header Parsing Errors in Tuist-Generated Xcode Project

Question or problem

I have an SPM package that compiles C code and includes it as a dependency in my iOS app via Tuist. When building with tuist build, I get compilation errors in system headers (dns_sd.h and xpc/connection.h) that should never fail to parse.

Error Messages:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/dns_sd.h:207:30: expected parameter declarator
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/dns_sd.h:207:30: expected ')'
...
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/dns_sd.h:207:74: expected function body after function declarator

The problematic line 207 in dns_sd.h:

} DNS_SD_API_AVAILABLE(macos(12.0), ios(15.0), tvos(15.0), watchos(8.0)) DNSServiceAAAAPolicy;

This line uses Apple’s availability attribute macros. The error suggests these macros are being disrupted by something in my C library during Xcode’s module compilation.

Expectation

I expect that building my iOS app with tuist build should work the same way as swift build does when using the same SPM dependency. System headers like dns_sd.h should parse correctly regardless of which C library I link.

Key observation:

  • :white_check_mark: swift build (SPM directly): Works perfectly
  • :cross_mark: tuist build (Xcode project generated by Tuist): Fails with dns_sd.h parsing errors

This suggests that Tuist or the generated Xcode project is handling module compilation differently, causing my C library’s headers/macros to pollute the global namespace and break system headers.

Context

Tuist version: 4.79.7
Xcode version: 26.0.1 (Build 17A400)
Platform: iOS 16.0+
Swift Tools Version: 5.9

I’m building an iOS app that depends on an SPM package containing a large C codebase (~400 C source files). The SPM package compiles fine with swift build, but when I integrate it into my app via Tuist and run tuist build, the build fails with parsing errors in system headers.

My SPM Package Structure:

MyPackage/
├── Package.swift
├── Sources/
│   └── NetworkLib/
│       ├── lib-ios-config/
│       │   ├── config.h            # Custom config header (~250 #defines)
│       │   └── src/                # ~400 C source files
│       │       ├── core/
│       │       ├── feature/
│       │       ├── lib/
│       │       └── ext/
│       └── include/
└── output/
    ├── openssl/                    # Vendored OpenSSL (static lib)
    ├── libevent/                   # Vendored libevent (static lib)
    └── xz/                         # Vendored XZ (static lib)

Simplified Package.swift:

// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "MyPackage",
    platforms: [.iOS(.v16)],
    products: [
        .library(name: "MyPackage", type: .static, targets: ["NetworkLib"])
    ],
    targets: [
        // Vendored dependencies with dummy.c files
        .target(
            name: "COpenSSL",
            path: "output/openssl",
            sources: ["dummy.c"],
            cSettings: [.headerSearchPath("include")],
            linkerSettings: [.unsafeFlags(["-Loutput/openssl/lib", "-lssl", "-lcrypto"])]
        ),
        // Similar for CLibevent and CXZ...
        
        // Main C library target
        .target(
            name: "NetworkLib",
            dependencies: ["COpenSSL", "CLibevent", "CXZ"],
            path: "Sources/NetworkLib",
            exclude: [/* ~60 exclusions for tests, docs, NSS files */],
            cSettings: [
                .headerSearchPath("lib-ios-config"),
                .headerSearchPath("lib-ios-config/src"),
                // ... ~20 more header search paths
                .define("HAVE_CONFIG_H"),
                .define("ENABLE_OPENSSL", to: "1"),
                // ... ~50 more defines
                .unsafeFlags(["-w", "-fno-modules", "-fno-objc-arc"])
            ],
            linkerSettings: [
                .linkedLibrary("z"),
                .linkedLibrary("resolv")
            ]
        )
    ]
)

App’s Dependencies.swift (Tuist):

import ProjectDescription

let dependencies = Dependencies(
    swiftPackageManager: SwiftPackageManagerDependencies(
        [
            .remote(
                url: "https://github.com/user/MyPackage.git",
                requirement: .exact("1.0.98")
            )
        ],
        productTypes: [
            "MyPackage": .staticLibrary  // Also tried .framework
        ]
    )
)

What I’ve tried:

  1. :white_check_mark: Changed product type to .static in Package.swift
  2. :white_check_mark: Removed TUIST conditional compilation block
  3. :white_check_mark: Added -fno-modules and -fno-objc-arc flags
  4. :white_check_mark: Removed @import MyPackage from bridging header
  5. :white_check_mark: Verified that my C headers don’t directly define ios, macos, tvos, or watchos
  6. :white_check_mark: Tested with standalone swift build – works perfectly!

Additional notes:

  • My C library’s config.h has ~250 platform-specific #define statements
  • Manual test with clang including both config.h and dns_sd.h compiles successfully
  • This issue only appears in Tuist-generated Xcode projects, not with SPM directly

Reproduction (mandatory for problems)

Steps to Reproduce

  1. Create an SPM package with a C library target (~400 C files, ~250 #defines in config header)
  2. Add vendored static libraries (OpenSSL, libevent, XZ) as targets with dummy.c sources
  3. Configure the main target with multiple header search paths and preprocessor defines
  4. Create an iOS app project using Tuist
  5. Add the SPM package as a dependency in Dependencies.swift
  6. Run tuist install and tuist generate
  7. Run tuist build --configuration Debug --platform iOS

Expected Result

Build succeeds, just like it does with swift build --triple arm64-apple-ios16.0-simulator

Actual Result

Build fails with parsing errors in system headers:

dns_sd.h:207:30: expected parameter declarator
dns_sd.h:207:74: expected function body after function declarator

Minimal Reproduction

I can test with a minimal project if needed. The key components are:

  • Large C codebase with custom config header (many #defines)
  • Vendored static libraries as SPM targets
  • Integration via Tuist’s SwiftPackageManagerDependencies

Logs

Build log shows the errors occur during compilation of the NetworkLib target in the Tuist-generated Xcode project. The same code compiles successfully when built directly with SPM.

Question

How can I properly isolate my C library’s headers/macros in a Tuist-generated Xcode project to prevent them from polluting the global namespace and breaking system headers?

Is there a Tuist-specific configuration I’m missing, or is this a potential bug in how Tuist handles C library dependencies?