Generating project for Carthage

Hello!

I have trouble getting Carthage support for a project using the SwiftPM.

From my limited understanding, Carthage needs an xcodeproj that it finds on a git repo, and it "just builds it". More than that, Carthage has a system to declare Carthage dependencies (the "Cartfile").

Say now you have a project based on the SwiftPM (grpc-swift is such a project). For the project to support Carthage, you need to generate an xcodeproj, which we do here. But for the project to build with Carthage, I needed to add a new script phase to the xcodeproj (done here, running swift package resolve.

And that kind of solves the problem: if you run make project-carthage, it creates an xcodeproj that you can push on the repo, and Carthage can access it. So a workaround sounds like "everytime you release grpc-swift, run make project-carthage and version the resulting project, so that the commit of this release works".

Now, the generated xcodeproj has some dependencies that look like: grpc-swift<U+2069>/.build<U+2069>/checkouts<U+2069>/swift-protobuf.git-442897717507903005<U+2069> (in the .build folder). And it seems like the "id" of that link is deterministic, but changes depending on the version of Xcode (at least we observe that between Xcode 9 and Xcode 10 here.

In other words, a project generated from Xcode 9 with swift package generate-xcodeproj and then swift package resolve can be compiled by another Xcode 9, but cannot be compiled by Xcode 10.

The question then is: where does that number come from (442897717507903005⁩), and would it be possible to disable it, such that the project is always consistent? I don't know what that would imply, I guess there is a reason for this number.

I have this issue right now with grpc-swift, but I believe there are probably other SwiftPM projects out there that would also like to support Carthage, so maybe that's a known issue on your side?

The only solution I can think of is quite sad but should work... Maybe (hopefully) @Aciid has a better idea.

You could temporarily put the packages you are using into edit mode, build, and then put it back into unedit mode. This will ensure that the projects are consistently put into the Packages/ directory without any hash information on the end of the path during your build phase.

1 Like

Thanks for the answer!

So you mean that in the script phase, I should:

  1. Somehow read the xcodeproj and list the dependencies (not sure if possible with the xcodeproj utility)
  2. Set the edit mode for those dependencies with swift package edit <dep_name>
  3. Run swift package resolve
  4. Remove the edit mode for the dependencies with swift package unedit <dep_name>

Is that right? Where can I find information about the edit mode? I have no idea what that is actually :confused:.

Also, do you know what this "id" or "hash" is for? Why does it have to be different every time swift package resolve is run, and why was it not necessary with Xcode 9?

My thought was you would do this in a pre-build phase.

swift package resolve
swift package edit <dep_1_name>
swift package edit <dep_2_name>

Then in a post-build phase do

swift package unedit <dep_1_name>
swift package unedit <dep_2_name>

This will give your Xcode project that consistent name and path but make it so the package manager can have things where it expects and so you can update and resolve them as normal.

You could write a script to parse out information from the Package.resolved file so you know what dependencies you have. The issue is that then the xcodeproj doesn't know about them and won't compile or link against them. So you will likely need to manually maintain them in the xcodeproj. You will just want to put dependencies into edit mode when you add them to the project file.

I must say it's not extremely convenient to have to maintain SwiftPMs dependencies inside yet another project :confused:. But in that case, I could also just remove the SwiftPM dependencies and have Carthage manage them from a Cartfile, right?

Would you happen to know why that changed between swift 4.1 and swift 4.2 (I mean the fact that the .build/checkouts/<dep>-number number is not deterministic anymore)?

It's a pity, because running swift package resolve as a pre-build phase in the xcodeproj generated for Carthage was actually quite convenient. But I guess that's how it is: developers of libraries need to maintain Cocoapods, Carthage and SwiftPM in parallel.

I don’t know what changed.

Using a Cartfile is definitely another valid option. I haven't ever actually had to setup Carthage with dependencies like that so I won't be much help. Your "challenge" there will be keeping the SwiftPM and Carthage files for sure using the same builds of the dependencies. That isn't very difficult though.

Yes it is sad needing to support multiple tools for the same task. I know that many of the goals that have been stated for SwiftPM will hopefully remove that need at some point.

Right, I see.

I know that many of the goals that have been stated for SwiftPM will hopefully remove that need at some point.

Yes, SwiftPM seems very nice, looking forward to seeing support for platforms like iOS!

Thanks a lot for your help!

We've been using XcodeGen to support Carthage. SwiftPM's generated project file unfortunately doesn't produce the schemes needed to support multiple platforms on Carthage.

That's very good to know! So that would be a way to support Carthage standalone (i.e. by creating the xcodeproj with XcodeGen instead of depending on SPM), right?

Yup! Here's the project.yml we settled on if you (like me) found the documentation a bit hard to navigate for a multi-platform, unit-tested project:

Sounds good! I'll have a look at that ASAP!