SPM shared targets files use case, what's the alternative

I'm writing a Bluetooth library around CoreBluetooth, to create a solid code coverage I'm using as a Dependency this library.
This library requires that you substitute each import CoreBluetooth to import CoreBluetoothMock and since the mock are protocol based I could not use some functionalities. To avoid compiler errors I've created a new Xcode configuration that defines a TEST flag and I've set up the scheme to use this configuration duration using testing. Thus I have some part of my code wrapped around #if #endif including import statements.
Using the SPM and the option -Xswifc "-DTEST" I'm able to safely run unit tests.
I would like that my library won't come out with this dependency that is used only for CI and I've figured out that setting my SPM file into some like that, I could provide a target user library, a target library with that dependency compiled with that flag defined and test target that depends on this last one.
Unfortunately this configuration led me to the

error: target 'LittleBlueToothForTest' has sources overlapping sources:

I've read that SPM is made that way so my solution it doesn't seem to be applicable. I've read different post on the forum.
Here my Package file:

let package = Package(
    name: "LittleBlueTooth",
    platforms: [
        // Add support for all platforms starting from a specific version.
        .macOS(.v10_15),
        .iOS(.v13),
        .watchOS(.v6),
        .tvOS(.v13)
    ],
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "LittleBlueTooth",
            targets: ["LittleBlueTooth"]),
        .library(
            name: "LittleBlueToothForTest",
            targets: ["LittleBlueToothForTest"])
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
         .package(name: "CoreBluetoothMock",
                  url: "https://github.com/enricodk/IOS-CoreBluetooth-Mock.git",
                  .branch("multiplatform")),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "LittleBlueTooth",
            dependencies: []),
        .target(
            name: "LittleBlueToothForTest",
            dependencies: ["CoreBluetoothMock"],
            path: "./Sources/LittleBlueTooth",
            swiftSettings: [.define("TEST")]
        ),
        .testTarget(
            name: "LittleBlueToothTests",
            dependencies: ["LittleBlueToothForTest","CoreBluetoothMock"])
    ]
)

Has someone any idea/suggestion about how can I solve that issue?

Hi! I'm not sure there actually is a solution. Ideally, you would remove the mock target and library and be able to set the TEST flag on your LittleBlueTooth target. What you would need is something like .define("TEST", condition: .when(configuration: .test)), but BuildConfiguration only has .release or .debug. Short of suggesting a change in SPM (and I'm not sure a "test" configuration is something that exists), if this is only for CI and code coverage, you could actually define two separate manifests!
Package.swift would be the one without the mock dependency and something like Package-CI.swift would have it. The CI manifest would also add the TEST flag to your LittleBlueTooth target and those would be the only differences. Your test target would in both cases depend on LittleBlueTooth. Then, in your travis.yml or whatever CI you're using you would add a step that temporarily renames Package-CI.swift. If that can be done, CI would do tests and code coverage with the mock dependency, but the distributed PAckage would not contain it.

1 Like

Hello Jonas,
yes I think that renaming the package manifest could be a viable options, I'm using GitHub Actions. I will give a try, but it's a pity that an operation like that would require this kind of trick.
I thought that I was missing something or doing something wrong at design level.
Thank you so much for your suggestion.
Andrea

I think you can use symlinks to avoid the overlapping sources error.

Hello Boris,
It does work, I gave a first try by doing aliases using the finder and it gives me error.
Then I've made symlink using the terminal and it worked.
Thank you a lot,
andrea

Terms of Service

Privacy Policy

Cookie Policy