Skip to content

Conversation

mishabunte
Copy link

Fix: SwiftPM/CocoaPods source files in
SourcePackages/checkouts/* were indexed with MacOSX.sdk because the
upward search never found a .compile-* file.

Change:

findSwiftModuleRoot() accepts an optional start directory.

On a miss, InferFlagsForSwift() retries from os.getcwd() (workspace
root), where the real .compile-* lives.

Result: package sources now receive the correct iOS/iOS-sim flags, so
SourceKit-LSP regains hover and go-to-definition for library code.

If a checkout lacks a local `.compile-*`, _InferFlagsForSwift_ now
re-searches from cwd. This picks up the workspace-level `.compile` written
for SourcePackages and stops library files from falling back to macOS SDK.
@SolaWing
Copy link
Owner

Normally, xcode-build-server will find and use the corresponding compile file through the configuration in buildServer.json to provide compilation information. Infer Flags indicates that the corresponding compilation information is not found under the current buildServer.json configuration. This is just a fallback configuration and cannot guarantee accuracy. The .compile file information in the current directory usually is the same as the buildServer.json generated by xcode-build-server parse. Therefore, if the information in buildServer.json was not found before, there should be no corresponding information in the .compile file in the current directory.

On the other hand, SourcePackages/checkouts/* is outside the current directory. Most editors will generate a new root directory. There is no buildServer.json in this root directory, and sourcekit-lsp will not get flags through BSP.

So I am curious about your Integrated environment and configuration. I will try to reproduce the usage scenario and see if there is a better solution.

@mishabunte
Copy link
Author

I’m working on this project: https://github.com/mishabunte/unstoppable-wallet-ios

  1. I cloned the repository to ~/prj/unstoppable-wallet-ios and built it.
  2. I generated buildServer.json in the project root.
  3. After building, a .compile file appeared in the project root (~/prj/unstoppable-wallet-ios).
  4. I opened a source file from the project, e.g. Modules/Wallet/Views/BalanceButtonsView.swift.
  5. xcode-build-server (findSwiftModuleRoot) finds the .compile file, so LspDefinition works—because it can resolve the correct compilation flags.
  6. From that file, I use LspDefinition to jump to the PrimaryButton class.
  7. PrimaryButton.swift isn’t part of the app’s source; it’s from a dependency located at: ~/Library/Developer/Xcode/DerivedData/UnstoppableWallet-adhiqabesrsupjdlzfgzqspnbqlj/SourcePackages/checkouts/ComponentKit.Swift/Sources/ComponentKit/Classes/Buttons/PrimaryButton.swift
  8. When findSwiftModuleRoot walks up the path starting in ~/Library/Developer/Xcode/DerivedData/.../ComponentKit.Swift/Sources/ComponentKit/Classes/Buttons/, it can’t find a .compile file. As a result, SourceKit-LSP features fail because xcode-build-server doesn’t know the compilation flags for that library file.

Fix: I added a fallback in InferFlagsForSwift and findSwiftModuleRoot that checks the current working directory if no .compile file is found. With this change, I can navigate both the project’s source code and the dependency sources.

@SolaWing
Copy link
Owner

SolaWing commented Jul 18, 2025

see https://github.com/SolaWing/xcode-build-server/blob/5d1012ce5d008c77cec8be963d4c525ddb683635/server.py#L121C1-L121C73
flags = GetFlags(file_path, self.compile_file, store=self.store)

BSP server already pass current compile_file to get flags, if it contains flags for file, there shouldn't enter the infer branch. Except you start another BSP server instance for different root.

Did you have any buildServer.json in parent directory of ~/Library/Developer/Xcode/DerivedData/.../ComponentKit.Swift/Sources/ComponentKit/Classes/Buttons/

Or Can upload your logs? it can be export to a file by start LSP with environment XBS_LOGPATH=/tmp/xbs.log

@mishabunte
Copy link
Author

Did you have any buildServer.json in parent directory of ~/Library/Developer/Xcode/DerivedData/.../ComponentKit.Swift/Sources/ComponentKit/Classes/Buttons/

No, I don't have buildServer.json in Xcode/DerivedData. Xcode creates this folder automatically for the third-party libraries

My project has buildServer.json. But my project is in my home folder - ~/prj/unstoppable-wallet-ios

The fix is propose is very simple: xcode-build-server will use project's buildServer.json for all third-party libraries for the specific project

(Xcode puts the source code for the third-party libraries into ~/Library/Developer/Xcode/DerivedData/ ~/Library/Developer/Xcode/DerivedData/UnstoppableWallet-adhiqabesrsupjdlzfgzqspnbqlj/SourcePackages/checkouts/ComponentKit.Swift/Sources/ComponentKit/Classes/Buttons/PrimaryButton.swift)

The easiest way is to use project's buildServer.json for third-party libraries and it works perfect - check the FilesChanged:

@mishabunte
Copy link
Author

I perform these steps

  1. Open BalanceButtonView.swift file from my project directory ~/prj/unstoppable-wallet-ios
    (I have buildServer.json for it)
  2. Navigate to PrimaryButton in BalanceButtonView and go to definition (LspDefinition)
  3. PrimaryButton.swift is from the third-party library and source code is in ~/Library/Developer/Xcode/DerivedData/UnstoppableWallet-adhiqabesrsupjdlzfgzqspnbqlj/SourcePackages/checkouts/ComponentKit.Swift/Sources/ComponentKit/Classes/Buttons/PrimaryButton.swift
  4. Without my patch xcode-build-server does not know compile options for PrimaryButton
  5. With the patch xcode-build-server uses buildServer.json from the project directory for the third-party source code as well - everything works perfect

Please find logs before and after the patch:

xbs_before_patch.log
xbs_after_patch.log

@SolaWing
Copy link
Owner

I noticed that BalanceButtonsView.swift in your project directory also retrieves flags via infer. If all your flags are retrieved from the .compile file of cwd , the kind property in your buildServer.json should be "manual" instead of "xcode." Also, .compile and buildServer.json should be in the same directory. See the corresponding logic for retrieval of the compile file:

def get_compile_file(self, config: ServerConfig):
# isolate xcode generate compile file and manual compile_file
if config.kind == "xcode":
hash = hashlib.md5(config.build_root.encode("utf-8")).hexdigest()
name = ["compile_file", config.scheme or "_last", hash]
if config.skip_validate_bin:
name[0] = "compile_file1"
return os.path.join(self.cache_path, "-".join(name))
# manual compile_file
return os.path.join(self.root_path, ".compile")
and the logic for using the compile file:
flags = GetFlags(file_path, self.compile_file, store=self.store)
You should get flags by binding .compile information correctly, not by inferring them through fallback unknown files.

If the kind field in your buildServer.json is indeed xcode, I am curious about how you generate the .compile file. Corresponding to the two methods of using flags in the Readme, when the kind is xcode, it should use xcode to compile the app, and automatically obtain the information of the corresponding log directory of xcode to create an isolated cache compile file. Another way, the manual parsing log .compile (including xcode post-build-action) will generated in the current directory by default unless the output path is explicitly specified, and will overwrite the kind of buildServer.json to manual

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants