SwiftSyntax with Swift 5.1

The swift-syntax repo provides a Swift interface but it doesn't implement its own parser, it uses a library from the toolchain for accessing the parser implementation from the Swift repo.

1 Like

This is a great hack. I've added this in my run script as a pre-build action.

Follow up question, is it possible to add custom run scripts to Swift Packages? In that case, copying over the dylib from the toolchain would become much easier

This can be also solved by copying/symlinking lib_InternalSwiftSyntaxParser.dylib to /usr/local/lib as an alternative that doesn't require a script to keep derived data up to date.

sudo mkdir -p /usr/local/lib
# sudo cp /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/lib_InternalSwiftSyntaxParser.dylib /usr/local/lib/

The issue itself is out of date now. SwiftSyntax 0.50600.1 includes the library as a binary target on macOS, which works just fine with Xcode.

1 Like

That's weird. I can see it's conditionally added for macOS, but I hit it yesterday using 0.50600.1 and Xcode 13.3.1, building for an M1. What could have caused that? I was writing a plugin (I doubt it matters tho) and added deps like so:

dependencies: [
    .product(name: "SwiftSyntax", package: "swift-syntax"),
    .product(name: "SwiftSyntaxParser", package: "swift-syntax"),
    .product(name: "SwiftSyntaxBuilder", package: "swift-syntax")
]

Strange. @ahoppen?

That’s interesting. It should just work on macOS. Asking the obvious question: Are you sure that you depend on SwiftSyntax 0.50600.1? If you changed the version recently could you check if it persists after running swift package update (if building from the command line) or File -> Packages -> Update to Latest Package version (if building from Xcode) to make sure the dependencies are updated?

If the issue persists, would it be possible to share the project so I can investigate what might be going on? If that’s not possible, could you share your Package.swift?

Sure, the project is public. Just try to build or run the tests while making sure the lib_InternalSwiftSyntaxParser.dylib is not in any of the external search paths. It should fail.

I didn’t manage to fully reproduce the issue you were describing (for some reason building the plugin just hang for me, filing a bug report for that separately) but I think you are missing a dependency from InjectableTests to InjectableTool. Because SwiftSyntax is a dependency of InjectableTool, I think it might not get built/installed when running InjectableTests. Could you try adding that and see if that fixes the issue?

Makes sense. I wonder how it resolved InjectableTool and didn't failed earlier. But unfortunately no change regarding the .dylib.
Could the crash you're experiencing be related? Could you try add lib_InternalSwiftSyntaxParser.dylib to /usr/local/lib?

I have made the following changes to Package.swift to fix compilation issues I was seeing

diff --git a/Package.swift b/Package.swift
index e87a2bb..5c5771c 100644
--- a/Package.swift
+++ b/Package.swift
@@ -5,6 +5,7 @@ import PackageDescription
 
 let package = Package(
     name: "Injectable",
+    platforms: [.macOS(.v11)],
     products: [
         // Products define the executables and libraries a package produces, and make them visible to other packages.
         .library(
@@ -31,7 +32,7 @@ let package = Package(
         ),
         .testTarget(
             name: "InjectableTests",
-            dependencies: ["Injectable"],
+            dependencies: ["Injectable", "InjectableTool"],
             plugins: [.plugin(name: "InjectablePlugin")]
         ),
         .executableTarget(
/Library/Developer/Toolchains/swift-5.6-RELEASE.xctoolchain/usr/bin/swift build

passed just fine for me (even if I remove /Library/Developer/Toolchains/swift-5.6-RELEASE.xctoolchain/usr/lib/swift/macosx/lib_InternalSwiftSyntaxParser.dylib).

Running the tests fails for me with the following log.

/Library/Developer/Toolchains/swift-5.6-RELEASE.xctoolchain/usr/bin/swift test
[1/1] Planning buildCompiling plugin InjectablePlugin...
Building for debugging...
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableTests+Dependencies.swift' (No such file or directory)
<unknown>:0: error: error opening input file '/private/tmp/Injectable/.build/plugins/outputs/injectable/InjectableTests/InjectablePlugin/InjectableToolTests+Dependencies.swift' (No such file or directory)

error: fatalError

Could you share which build command you are using to hit the issue and the log it produces?

This issue only manifests when opening the project in Xcode (via Package.swift). Sorry I haven't made that clear earlier. That's why people that commented above asked about a workaround without using generate-xcodeproj (which fixed a similar issue in older versions of SwiftSyntax).

In any case, I've PM'd you the full Xcode logs.

This is really weird, opening your project in Xcode results in a hang in “Resolving Package Graph” for me (https://github.com/apple/swift/issues/58415). Do you also see that behavior? I’m thinking that you might have gotten your Xcode into a situation that just doesn’t update the package properly anymore.

Could you try the following:

  • Build your project from the command line using /Applictions/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift build
  • Build your project using the swift-5.6.1-RELEASE-osx open source toolchain from the command line using /Library/Developer/Toolchains/swift-5.6-RELEASE.xctoolchain/usr/bin/swift build
  • Close Xcode, move ~/Library/Developer/Xcode/DerivedData, /path/to/Injectable/.swiftpm and /path/to/Injectable/.build aside and try rebuild the project in Xcode
  • Select the swift-5.6.1-RELEASE toolchain in Xcode -> Toolchains and try rebuilding the project using that toolchain.

Unfortunately (or fortunately) Xcode doesn't hang for me. I'm on 13.3.1.

Because of some other issues Xcode had with pulgins (it was trying to build the tool for iOS) I've moved the tool into its own package (and distribute it as a binary dependency as well).

Now everything works as expected and there's no need to have the lib_InternalSwiftSyntaxParser.dylib externally sourced. It seems having executable targets for the plugin tool is a bit buggy. I'll have a closer look into this a bit later. I'll try all the steps you outlined above and also try to provide a minimum reproducible example.

1 Like

A small update:

  1. Building/testing with Xcode's toolchain directly as you suggested works
  2. Building/testing with the latest Swift's toolchain works as well
  3. Clearing the derived data and build dir has no effect (still fails when building in Xcode)
  4. The Swift Open Source toolchain fails as well when building in Xcode

So, given the above, I'm thinking this is a Xcode exclusive issue. I've also noticed that it looks for the library in the wrong place /Users/valentinradu/Library/Developer/Xcode/DerivedData/Injectable-hckztmxeoriroqfkmeoyjwqydvoj/Build/Products/Debug/../lib/lib_InternalSwiftSyntaxParser.dylib (the lib is available in Debug , not sure why ../lib/lib_InternalSwiftSyntaxParser.dylib is appended - looks like default relative to the executable)

EDIT: Sill not sure what's causing this but for anyone encountering it, you can set the -rpath using linkedSettings on the executable to temp fix it:

linkerSettings: [
    .unsafeFlags([
        "-Xlinker", "-rpath", "-Xlinker", "@executable_path/"
    ])
]

OK, I finally managed to build everything locally without the hang (not sure what I did differently this time though, but :person_shrugging:).

I still needed to add

platforms: [.macOS(.v10_15)],

in Package.swift to stop Concurrency is only available in macOS 10.15.0 or newer from showing up.

I also just compared rpath entries and it indeed looks like your InjectableTool is missing the @executable_path rpath.

Could you try the following

  1. Try add the platform I mentioned above to Package.swift, just to make sure this is not triggering different behavior for you and me.
  2. With the platform requirement added to Package.swift, perform a clean build and send me the build log (either here or as a PM)
    • Product -> Clean Build Folder
    • Product -> Build
    • Wait for build to finish
    • Select the most recent build in the Log Navigator -> Export…
  3. Send me the output of otool -l /Users/<user>/Library/Developer/Xcode/DerivedData/Injectable-<some-hash>/Build/Products/Debug/InjectableTool. It should contain an entry like the following. If that doesn’t exist, I’d like to use the build log from step 2. to figure out why it might be missing.
Load command 28
          cmd LC_RPATH
      cmdsize 32
         path @executable_path/ (offset 12)

Indeed, the rpath is the problem (see above). Fixable by manually setting it in the target. Not sure why, but in my case the output was:

          cmd LC_RPATH
      cmdsize 32
         path /usr/lib/swift (offset 12)
--
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/../lib (offset 12)

Notice the extra ../lib (the dep is at @executable_path/)

In any case I'll follow the steps you've outlined a bit later today and come back to you.