Improve handling of local_repositories
- Create the tulsi-workspace symlink during project generation in order
to allow Xcode to resolve local_repository files without needing to
build.
- External and local_repositories should no longer show up under
@repository_name in the source filter UI. Instead, they will show up
under external/repository_name to match Bazel.
PiperOrigin-RevId: 189064998
diff --git a/src/TulsiGenerator/Base.lproj/Localizable.strings b/src/TulsiGenerator/Base.lproj/Localizable.strings
index b4a2b98..9e4113d 100644
--- a/src/TulsiGenerator/Base.lproj/Localizable.strings
+++ b/src/TulsiGenerator/Base.lproj/Localizable.strings
@@ -112,3 +112,6 @@
/* Warning when updating the user's DBGShellCommands script in ~/Library/Application Support/Tulsi failed. */
"UpdatingDBGShellCommandsFailed" = "Failed to update the DBGShellCommands script used to locate dSYM bundles: received error '%1$@'.";
+
+/* Warning shown when failing to update the tulsi-workspace symlink to the Bazel execution root. tulsi-workspace path is in %1$@, additional error context in %2$@. */
+"UpdatingTulsiWorkspaceSymlinkFailed" = "Failed to update the %1$@ symlink to the Bazel execution root. Additional context: %2$@";
diff --git a/src/TulsiGenerator/BuildLabel.swift b/src/TulsiGenerator/BuildLabel.swift
index df5b6de..1f8e74d 100644
--- a/src/TulsiGenerator/BuildLabel.swift
+++ b/src/TulsiGenerator/BuildLabel.swift
@@ -46,9 +46,14 @@
}()
public lazy var asFileName: String? = { [unowned self] in
- guard let package = self.packageName, let target = self.targetName else {
+ guard var package = self.packageName, let target = self.targetName else {
return nil
}
+ // Fix for external and local_repository, which may be referenced by Bazel via
+ // @repository//subpath while we internally refer to them via external/repository/subpath.
+ if package.starts(with: "@") {
+ package = "external/" + package.suffix(from: package.index(package.startIndex, offsetBy: 1))
+ }
return "\(package)/\(target)"
}()
diff --git a/src/TulsiGenerator/XcodeProjectGenerator.swift b/src/TulsiGenerator/XcodeProjectGenerator.swift
index 48e67fe..eab1042 100644
--- a/src/TulsiGenerator/XcodeProjectGenerator.swift
+++ b/src/TulsiGenerator/XcodeProjectGenerator.swift
@@ -237,6 +237,7 @@
installStubExtensionPlistFiles(projectURL,
rules: projectInfo.buildRuleEntries.filter { $0.pbxTargetType?.isiOSAppExtension ?? false },
plistPaths: plistPaths)
+ linkTulsiWorkspace()
return projectURL
}
@@ -600,6 +601,57 @@
}
}
+ // Links tulsi-workspace to the current Bazel execution root. This may be overwritten during
+ // builds, but is useful to include in project generation for users who have local_repository
+ // references.
+ private func linkTulsiWorkspace() {
+ // Don't create the tulsi-workspace symlink for tests.
+ guard !self.redactWorkspaceSymlink else { return }
+
+ let path = workspaceRootURL.appendingPathComponent(PBXTargetGenerator.TulsiWorkspacePath,
+ isDirectory: false).path
+ let bazelExecRoot = self.workspaceInfoExtractor.bazelExecutionRoot;
+
+ // See if tulsi-includes is already present.
+ if let attributes = try? fileManager.attributesOfItem(atPath: path) {
+ // If tulsi-includes is already a symlink, we only need to change it if it points to the wrong
+ // Bazel exec root.
+ if attributes[FileAttributeKey.type] as? FileAttributeType == FileAttributeType.typeSymbolicLink {
+ do {
+ let oldBazelExecRoot = try self.fileManager.destinationOfSymbolicLink(atPath: path)
+ guard oldBazelExecRoot != bazelExecRoot else { return }
+ } catch {
+ self.localizedMessageLogger.warning("UpdatingTulsiWorkspaceSymlinkFailed",
+ comment: "Warning shown when failing to update the tulsi-workspace symlink in %1$@ to the Bazel execution root, additional context %2$@.",
+ context: config.projectName,
+ values: path, "Unable to read old symlink. Was it modified?")
+ return
+ }
+ }
+
+ // The symlink exists but points to the wrong path or is a different file type. Remove it.
+ do {
+ try fileManager.removeItem(atPath: path)
+ } catch {
+ self.localizedMessageLogger.warning("UpdatingTulsiWorkspaceSymlinkFailed",
+ comment: "Warning shown when failing to update the tulsi-workspace symlink in %1$@ to the Bazel execution root, additional context %2$@.",
+ context: config.projectName,
+ values: path, "Unable to remove the old tulsi-workspace symlink. Trying removing it and try again.")
+ return
+ }
+ }
+
+ // Symlink tulsi-workspace -> Bazel exec root.
+ do {
+ try self.fileManager.createSymbolicLink(atPath: path, withDestinationPath: bazelExecRoot)
+ } catch {
+ self.localizedMessageLogger.warning("UpdatingTulsiWorkspaceSymlinkFailed",
+ comment: "Warning shown when failing to update the tulsi-workspace symlink in %1$@ to the Bazel execution root, additional context %2$@.",
+ context: config.projectName,
+ values: path, "Creating symlink failed. Is it already present?")
+ }
+ }
+
// Writes Xcode schemes for non-indexer targets if they don't already exist.
private func installXcodeSchemesForProjectInfo(_ info: GeneratedProjectInfo,
projectURL: URL,
diff --git a/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift b/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift
index 071ec75..20fbf3b 100644
--- a/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift
+++ b/src/TulsiGeneratorTests/XcodeProjectGeneratorTests.swift
@@ -345,6 +345,7 @@
tulsiVersion: testTulsiVersion,
fileManager: mockFileManager,
pbxTargetGeneratorType: MockPBXTargetGenerator.self)
+ generator.redactWorkspaceSymlink = true
generator.writeDataHandler = { (url, _) in
self.writtenFiles.insert(url.path)
}