1 manifest, 2 targets in subdirs, cannot figure out syntax

I want to create a single Package.swift file at the root of a Git repos, and it has two library targets, and the code for each are in subdirs. The syntax for this seemingly simple config is eluding me.

Here are my files:

🆑 ls -R
Cheese		Ham		Package.swift

./Cheese:
Sources	Tests

./Cheese/Sources:
Cheese

./Cheese/Sources/Cheese:
Cheese.swift

./Cheese/Tests:
CheeseTests

./Cheese/Tests/CheeseTests:
CheeseTests.swift

./Ham:
Sources	Tests

./Ham/Sources:
Ham

./Ham/Sources/Ham:
Ham.swift

./Ham/Tests:
HamTests

./Ham/Tests/HamTests:
HamTests.swift

Here is the Package.swift file:

// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Melon",
    products: [
        .library(
            name: "Ham",
            targets: ["Ham"]
        ),
        .library(
            name: "Cheese",
            targets: ["Cheese"]
        ),
    ],
    targets: [
        .target(
            name: "Ham",
            path: "Ham",
            sources: [ "Sources" ]
        ),
        .testTarget(
            name: "HamTests",
            dependencies: ["Ham"],
            path: "Ham",
            sources: [ "Tests" ]
        ),
        .target(
            name: "Cheese",
            path: "Cheese",
            sources: [ "Sources" ]
        ),
        .testTarget(
            name: "CheeseTests",
            dependencies: ["Cheese"],
            path: "Cheese",
            sources: [ "Tests" ]
        ),
    ]
)

And, after a swift package clean here is what happens when I build:

🆑 swift build
warning: 'rats': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
    /Users/jeff/Code/Rats/Ham/Tests/HamTests/HamTests.swift
warning: 'rats': found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target
    /Users/jeff/Code/Rats/Cheese/Tests/CheeseTests/CheeseTests.swift
Building for debugging...
[7/7] Emitting module Ham
Build complete! (0.74s)

I do not get it. I have tried lots of things.

I started with the Package.swift from Ham. I moved it up a level. I added in the elements from Cheese. I added path args and then added sources args to make various build errors go away. If I showed how I built up this syntax as I fixed error after error this note would be too long.

There must be some way to make this simple example work with having to individually list every source file. I should be able to tell it what to do using only folder names. But how to do it eludes me.

Could you try something like tree to print this out?

I think something like this should work:

Rats
├── Package.swift
├── Sources
│   ├── Cheese
│   └── Ham
└── Tests
    ├── CheeseTests
    └── HamTests

Howdy, and thanks for responding.

However, I am looking for syntax to match a folder structure, not asking about how to change a folder structure to work with config syntax behavior defaults.

My macOS has no such command as tree, and I do not know the equiv. However, I will try to manually create it.

So my original question, in addition to the requirement of not listing source files individually, the other requirements is that the products must be completely separate inside subdirectories. I cannot mix them like you have shown with Sources and Tests in the top folder, and the different target names beneath each of those folders.

Here is my current folder layout:

Rats
├── Package.swift
├── Ham
│   ├── Sources
│   │  └── Ham
│   │     └── Ham.swift
│   └── Tests
│      └── HamTests
│         └── HamTests.swift
├── Cheese
│   ├── Sources
│   │  └── Cheese
│   │     └── Cheese.swift
│   └── Tests
│      └── CheeseTests
│         └── CheeseTests.swift

But now that I look at the duplicate naming, I suppose that this would also be okay for my purposes:

Rats
├── Package.swift
├── Ham
│   ├── Sources
│   │    └── Ham.swift
│   └── Tests
│         └── HamTests.swift
├── Cheese
│   ├── Sources
│   │     └── Cheese.swift
│   └── Tests
│         └── CheeseTests.swift

But I would think this layout would confuse SPM more than I already am. Or maybe I am more confused than I realize, and that this is better. :person_shrugging:

I think this should work:

targets: [
  .target(
    name: "Cheese",
    path: "Cheese/Sources"
  ),
  .testTarget(
    name: "CheeseTests",
    dependencies: ["Cheese"],
    path: "Cheese/Tests"
  ),
  .target(
    name: "Ham",
    path: "Ham/Sources"
  ),
  .testTarget(
    name: "HamTests",
    dependencies: ["Ham"],
    path: "Ham/Tests"
  ),
]

That did it! Thank you.

Looking back over my history, I came so close to this. But I had neglected the subdir paths in the regular targets, and that triggered an error.

For completeness, I had had this as my Package.swift:

// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Melon",
    products: [
        .library(
            name: "Ham",
            targets: ["Ham"]
        ),
        .library(
            name: "Cheese",
            targets: ["Cheese"]
        ),
    ],
    targets: [
        .target(
            name: "Ham",
            path: "Ham"
        ),
        .testTarget(
            name: "HamTests",
            dependencies: ["Ham"],
            path: "Ham/Tests"
        ),
        .target(
            name: "Cheese",
            path: "Cheese"
        ),
        .testTarget(
            name: "CheeseTests",
            dependencies: ["Cheese"],
            path: "Cheese/Tests"
        ),
    ]
)

And that had triggered this error:

🆑 swift build        
error: 'rats': target 'HamTests' has overlapping sources: /Users/jeff/Code/Rats/Ham/Tests/HamTests/HamTests.swift

And what confused me was the error message only listed 1 target. How can only 1 target have overlapping files? Shouldn't it list 2 targets? As in, there is a single file claimed by 2 targets, and so both targets should be listed in the error message, right?

Also, I kept reading and re-reading the documentation for Target and the path property, and I do not see how a non-test target would recursively search a folder named Tests. So if SPM never searched a folder named 'Tests' for source files for regular targets, how did it stumble across Ham/Tests/HamTests/HamTests.swift ?

Here is the docs I refer to: Documentation

And also, the --vv flag in swift build did not help for my problem. I wanted to see logs on how it decided what file went with what target, but that info was never printed.

Thanks again, tho. Solved.

Feel free to raise an enhancement issue here GitHub - swiftlang/swift-package-manager: The Package Manager for the Swift Programming Language and contributors can have a look.

Also, PRs are welcome :)