Help with Issue resolving dependency with SwiftPM

The issue is with an app that I am building based on this library that I have created as an SPM package GitHub - rchatham/LangTools.swift: Some abstractions around LLM apis.. In my app I have the main modules of the app broken out using a local Package.swift and then adding the resulting package to my app so that I can build the modules using swift build from the terminal.

What I cannot figure out is after some bad practice where I force-pushed to the LangTools.swift library after amending code to a tagged commit and trying to update the local packages I am able to build the app using XCode and xcodebuild from the command line HOWEVER swift build fails saying error: no such module 'OpenAI'.

I am adding the relevant portions of the Package.swift files for both repos, any help in diagnosing would be greatly appreciated! I have tried clearing the caches and pushing a new tag to the latest commit on the library, the latter did help resolve some issues caused by my previous force-push within XCode but not for swift build.

LangTools.swift - Package.swift

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "LangTools",
    platforms: [
        .macOS(.v13),
        .iOS(.v16),
        .watchOS(.v8)
    ],
    products: [
        .library(
            name: "LangTools",
            targets: ["LangTools"]),
        .library(
            name: "OpenAI",
            targets: ["OpenAI"]),
        .library(
            name: "Anthropic",
            targets: ["Anthropic"]),
        .library(
            name: "XAI",
            targets: ["XAI"]),
    ],
    targets: [
        .target(
            name: "LangTools"),
        .target(
            name: "OpenAI",
            dependencies: [.target(name: "LangTools")]),
        .target(
            name: "Anthropic",
            dependencies: [.target(name: "LangTools")]),
        .target(
            name: "XAI",
            dependencies: [
                .target(name: "LangTools"),
                .target(name: "OpenAI"),
            ]),
        .testTarget(
            name: "LangToolsTests",
            dependencies: ["LangTools", "OpenAI"],
            resources: [
                .process("Resources/")
            ]),
        .testTarget(
            name: "OpenAITests",
            dependencies: ["OpenAI"],
            resources: [
                .process("Resources/")
            ]),
        .testTarget(
            name: "AnthropicTests",
            dependencies: ["Anthropic"],
            resources: [
                .process("Resources/")
            ]),
        .testTarget(
            name: "XAITests",
            dependencies: ["XAI", "OpenAI", "LangToolsTests"],
            resources: [
                .process("Resources/")
            ]),
    ]
)

App - Package.swift

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "App",
    platforms: [
        .macOS(.v14),
        .iOS(.v17),
        .watchOS(.v8)
    ],
    products: [
        .library(
            name: "Chat",
            targets: ["Chat"]),
    ],
    dependencies: [
        .package(url: "https://github.com/rchatham/LangTools.swift.git", from: "0.0.1"),
    ],
    targets: [
        .target(
            name: "Chat",
            dependencies: [
                .product(name: "LangTools", package: "langtools.swift", condition: .when(platforms: [.iOS])),
                .product(name: "OpenAI", package: "langtools.swift", condition: .when(platforms: [.iOS])),
                .product(name: "Anthropic", package: "langtools.swift", condition: .when(platforms: [.iOS])),
                .product(name: "XAI", package: "langtools.swift", condition: .when(platforms: [.iOS])),
            ],
            path: "Modules/Chat"),
    ]
)

The package name for your LangTools is not langtools.swift. It is LangTools as declared in the LangTools Package.swift:

If you change it in your app's Package.swift, swift build might work.

I agree, that is weird. I don't know why that is required but I initially tried to do it without and received an error that "LangTools" was not found. However, this configuration worked until the most recent update to add XAI to LangTools. Just to be sure I'm not completely crazy I output the results from swift package dump-package and it shows all of the targets are added as dependencies. :thinking:

Error:

error: 'App': unknown package 'LangTools' in dependencies of target 'Chat'; valid packages are: ... 'LangTools.swift' (from 'https://github.com/rchatham/LangTools.swift.git')

swift package dump-package:

{
  "cLanguageStandard" : null,
  "cxxLanguageStandard" : null,
  "dependencies" : [
// ...
    {
      "sourceControl" : [
        {
          "identity" : "langtools.swift",
          "location" : {
            "remote" : [
              {
                "urlString" : "https://github.com/rchatham/LangTools.swift.git"
              }
            ]
          },
          "productFilter" : null,
          "requirement" : {
            "range" : [
              {
                "lowerBound" : "0.0.1",
                "upperBound" : "1.0.0"
              }
            ]
          }
        }
      ]
    }
  ],
  "name" : "App",
  "packageKind" : {
    "root" : [
      "/Users/username/Developer/App"
    ]
  },
  "pkgConfig" : null,
  "platforms" : [
    {
      "options" : [

      ],
      "platformName" : "macos",
      "version" : "14.0"
    },
    {
      "options" : [

      ],
      "platformName" : "ios",
      "version" : "17.0"
    },
    {
      "options" : [

      ],
      "platformName" : "watchos",
      "version" : "8.0"
    }
  ],
  "products" : [
//...
    {
      "name" : "Chat",
      "settings" : [

      ],
      "targets" : [
        "Chat"
      ],
      "type" : {
        "library" : [
          "automatic"
        ]
      }
    },
// ...
  ],
  "providers" : null,
  "swiftLanguageVersions" : null,
  "targets" : [
// ...
    {
      "dependencies" : [
        {
          "product" : [
            "LangTools",
            "langtools.swift",
            null,
            {
              "platformNames" : [
                "ios"
              ]
            }
          ]
        },
        {
          "product" : [
            "OpenAI",
            "langtools.swift",
            null,
            {
              "platformNames" : [
                "ios"
              ]
            }
          ]
        },
        {
          "product" : [
            "Anthropic",
            "langtools.swift",
            null,
            {
              "platformNames" : [
                "ios"
              ]
            }
          ]
        },
        {
          "product" : [
            "XAI",
            "langtools.swift",
            null,
            {
              "platformNames" : [
                "ios"
              ]
            }
          ]
        },
// ...
      ],
      "exclude" : [

      ],
      "name" : "Chat",
      "packageAccess" : true,
      "path" : "Modules/Chat",
      "resources" : [

      ],
      "settings" : [

      ],
      "type" : "regular"
    },
// ...
  ],
  "toolsVersion" : {
    "_version" : "5.9.0"
  }
}

After forking your repo with a single patch to the repo name it works as expected in my testing.

It did not work out-of-the box and my immediate suspect was the naming convention used (ending the repo in .swift) so I removed it from my fork and added it to a random repo I had, which fixed the importing modules problem.

I only tested it on my Linux machine, which the project doesn't support, but I can try it on my iMac later if you're still having this problem (and patch your repo name and use LangTools instead of langtools.swift in your app's Package.swift)

Maybe it is worth opening an issue in swift-package-manager?

1 Like

That's interesting! I wonder if this due to something that changed recently? SPM/swift build was working fine previously with the .swift in addition to it working in XCode. I'll open an issue on spm. Thank you!

EDIT: Spoke to soon, still seeing the same error as before :sweat:

/Users/username/Developer/App/Modules/Chat/Models/Message.swift:10:8: error: no such module 'OpenAI'
  8 |
  9 | import Foundation
 10 | import OpenAI
    |        `- error: no such module 'OpenAI'
 11 | import Anthropic
 12 |

I renamed the repo just to check but I am confident this is not the source of the issue. It had been working in multiple projects for some time with that naming and I have both used and created other repos with the same naming in SPM.

It does work in XCode but I would actually expect that it did work with the previous naming on your fork. I wonder if I was somehow able to corrupt something with SPM when I force-pushed to the repo earlier today.

1 Like

I am unable to reproduce the problem on my iMac in a Swift Package or in an iOS app (using 0.0.9 of your library). I suggest you invalidate/remove all caches (including but not limited to Xcode, Swift PM, any simulators using the library, any build files) and reboot your machine.

1 Like

I will give it a try! Planning to do the following and see how it goes, lmk if you see anything else I might be missing.

1. Delete Derived Data
2. 
rm -rf ~/Library/Caches/org.swift.swiftpm
rm -rf ~/Library/Caches/swift-package
rm -rf ~/LibraryCaches/swift-build
rm -rf ~/Library/org.swift.swiftpm
rm -rf ~/User/username/path/to/App/.build
rm -rf ~/User/username/path/to/App/.swiftpm

Nothing... Did all the above, rebooted and still the same issue :thinking:

I have a few more suggestions:

  • Run swift package clean & clean the build folder using Xcode (Command + Shift + K & Command + Shift + Option + K) in your app
  • Remove and reclone your app repo (if you have one)
  • Run swift build -v and share the error output

I would also like to know why you're using a Package.swift in an app. Is this a macOS/iOS/iPad app, or is it an executable package? If its for Apple platforms you should be using Xcode's dependency manager and not SwiftPM (you can use packages in your app using Xcode's system).

I tried the above and not seeing anything different. :man_shrugging: I was actually able to reproduce the issue on GitHub Actions so it is at least not specific to my machine. Here is the error I am seeing there, it's roughly the same but may offer a slight hint towards the issue. Perhaps related to the header though I am not clear why that would be an issue.

/Users/runner/work/App/App/.build/arm64-apple-macosx/debug/OpenAI.build/module.modulemap:2:12: error: header '/Users/runner/work/App/App/.build/arm64-apple-macosx/debug/OpenAI.build/OpenAI-Swift.h' not found
    header "/Users/runner/work/App/App/.build/arm64-apple-macosx/debug/OpenAI.build/OpenAI-Swift.h"
           ^
/Users/runner/work/App/App/Modules/Chat/Models/Message.swift:10:8: error: could not build Objective-C module 'OpenAI'
import OpenAI
       ^
error: fatalError

Using a local Package.swift shouldn't be an issue, you can add local swift packages to an app in XCode. I'm not sure if I am going to continue with this route long term because there are some minor caveats that aren't an issue when using Xcode to manage modules/dependencies but I'm doing the same with the LangTools library in the Example project and it works actually very well. The only issue with a more complicated Package.swift is that you often times have to resolve the dependencies before build if modules that depend on one another change. It is however nice for CI/Testing or editing things from the terminal if I don't want to open up XCode.

Thanks for all the help considering the holiday btw, hope you are having a nice one.

My last suggestion is to remove the .swiftpm folder from the Swift Package repo and add it to your .gitignore. This way your package should be able to build correctly on what ever device it is cloned to.

I have no other plausible solution without knowing the project structure, dependencies, tech stack, and devices you are building for. This one is a head scratcher.

Sorry it's been a few days since I could get back to you here, I gave it a shot but no avail. I extracted the pieces from the app into a sample project and was able to reproduce the error. It still builds from XCode and using xcodebuild using the local Package.swift but swift build continues to fail and is reproducible in github actions as well if you are interested. Not a blocker for development since I am able to build the project without it but this does seem rather strange and I would like to try to get it working.

I feel like I've run into something similar where local packages (path-based) use the name of the folder as the package name, but remote ones (git-based) use the name as declared in Package.swift. I doubt this is intentional behavior, but it might be hard for SPM to fix. I'm also not sure if it's the same thing you're seeing from your descriptions.

I'll also note that by default macOS formats storage as "case-preserving", i.e. it will let you create a directory named "LangTools.swift" or "langtools.swift", but then once it's created it can usually be referred to by either name. That could trip up some layer that's expecting you to use consistent case throughout.

:person_facepalming: You can't build it on any OS other than iOS because you have this condition set in the Package.swift: .when(platforms: [.iOS]). If you remove it, it works just fine.

1 Like

Gah, that seems so obvious ha. I feel like I was running into an error before where that seemed to fix things but you are absolutely right. I edited those lines and everything compiles normally now with swift build. Thank you!