Skip to content

Commit 7bfde0f

Browse files
authored
Merge pull request swiftlang#132 from benlangmuir/boo-symlinks
[swiftpm] Handle symlinks in the workspace path
2 parents 4dcc4e7 + 3c04356 commit 7bfde0f

File tree

2 files changed

+141
-15
lines changed

2 files changed

+141
-15
lines changed

Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public final class SwiftPMWorkspace {
6767
throw Error.noManifest(workspacePath: workspacePath)
6868
}
6969

70-
self.packageRoot = packageRoot
70+
self.packageRoot = resolveSymlinks(packageRoot)
7171

7272
guard let destinationToolchainBinDir = toolchainRegistry.default?.path?.appending(components: "usr", "bin") else {
7373
throw Error.cannotDetermineHostToolchain
@@ -199,7 +199,7 @@ extension SwiftPMWorkspace: BuildSystem {
199199
return nil
200200
}
201201

202-
if let td = self.fileToTarget[path] {
202+
if let td = targetDescription(for: path) {
203203
return settings(for: path, language, td)
204204
}
205205

@@ -213,6 +213,21 @@ extension SwiftPMWorkspace: BuildSystem {
213213

214214
return nil
215215
}
216+
217+
/// Returns the resolved target description for the given file, if one is known.
218+
func targetDescription(for file: AbsolutePath) -> TargetBuildDescription? {
219+
if let td = fileToTarget[file] {
220+
return td
221+
}
222+
223+
let realpath = resolveSymlinks(file)
224+
if realpath != file, let td = fileToTarget[realpath] {
225+
fileToTarget[file] = td
226+
return td
227+
}
228+
229+
return nil
230+
}
216231
}
217232

218233
extension SwiftPMWorkspace {
@@ -239,26 +254,44 @@ extension SwiftPMWorkspace {
239254

240255
/// Retrieve settings for a package manifest (Package.swift).
241256
func settings(forPackageManifest path: AbsolutePath) -> FileBuildSettings? {
242-
for package in packageGraph.packages where path == package.manifest.path {
257+
func impl(_ path: AbsolutePath) -> FileBuildSettings? {
258+
for package in packageGraph.packages where path == package.manifest.path {
243259
let compilerArgs = workspace.interpreterFlags(for: package.path) + [path.pathString]
244260
return FileBuildSettings(
245261
preferredToolchain: nil,
246262
compilerArguments: compilerArgs
247263
)
264+
}
265+
return nil
248266
}
249-
return nil
267+
268+
if let result = impl(path) {
269+
return result
270+
}
271+
272+
let canonicalPath = resolveSymlinks(path)
273+
return canonicalPath == path ? nil : impl(canonicalPath)
250274
}
251275

252276
/// Retrieve settings for a given header file.
253277
public func settings(forHeader path: AbsolutePath, _ language: Language) -> FileBuildSettings? {
254-
var dir = path.parentDirectory
255-
while !dir.isRoot {
256-
if let td = sourceDirToTarget[dir] {
257-
return settings(for: path, language, td)
278+
func impl(_ path: AbsolutePath) -> FileBuildSettings? {
279+
var dir = path.parentDirectory
280+
while !dir.isRoot {
281+
if let td = sourceDirToTarget[dir] {
282+
return settings(for: path, language, td)
283+
}
284+
dir = dir.parentDirectory
258285
}
259-
dir = dir.parentDirectory
286+
return nil
260287
}
261-
return nil
288+
289+
if let result = impl(path) {
290+
return result
291+
}
292+
293+
let canonicalPath = resolveSymlinks(path)
294+
return canonicalPath == path ? nil : impl(canonicalPath)
262295
}
263296

264297
/// Retrieve settings for the given swift file, which is part of a known target build description.

Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ final class SwiftPMWorkspaceTests: XCTestCase {
9090
targets: [.target(name: "lib", dependencies: [])])
9191
"""
9292
])
93-
let packageRoot = tempDir.path.appending(component: "pkg")
93+
let packageRoot = resolveSymlinks(tempDir.path.appending(component: "pkg"))
9494
let tr = ToolchainRegistry.shared
9595
let ws = try! SwiftPMWorkspace(
9696
workspacePath: packageRoot,
@@ -183,7 +183,7 @@ final class SwiftPMWorkspaceTests: XCTestCase {
183183
fileSystem: fs,
184184
buildSetup: TestSourceKitServer.buildSetup)
185185

186-
let source = packageRoot.appending(component: "Package.swift")
186+
let source = resolveSymlinks(packageRoot.appending(component: "Package.swift"))
187187
let arguments = ws.settings(for: source.asURL, .swift)!.compilerArguments
188188

189189
check("-swift-version", "4.2", arguments: arguments)
@@ -204,7 +204,7 @@ final class SwiftPMWorkspaceTests: XCTestCase {
204204
targets: [.target(name: "lib", dependencies: [])])
205205
"""
206206
])
207-
let packageRoot = tempDir.path.appending(component: "pkg")
207+
let packageRoot = resolveSymlinks(tempDir.path.appending(component: "pkg"))
208208
let tr = ToolchainRegistry.shared
209209
let ws = try! SwiftPMWorkspace(
210210
workspacePath: packageRoot,
@@ -243,7 +243,7 @@ final class SwiftPMWorkspaceTests: XCTestCase {
243243
])
244244
"""
245245
])
246-
let packageRoot = tempDir.path.appending(component: "pkg")
246+
let packageRoot = resolveSymlinks(tempDir.path.appending(component: "pkg"))
247247
let tr = ToolchainRegistry.shared
248248
let ws = try! SwiftPMWorkspace(
249249
workspacePath: packageRoot,
@@ -314,7 +314,7 @@ final class SwiftPMWorkspaceTests: XCTestCase {
314314
cxxLanguageStandard: .cxx14)
315315
"""
316316
])
317-
let packageRoot = tempDir.path.appending(component: "pkg")
317+
let packageRoot = resolveSymlinks(tempDir.path.appending(component: "pkg"))
318318
let tr = ToolchainRegistry.shared
319319
let ws = try! SwiftPMWorkspace(
320320
workspacePath: packageRoot,
@@ -393,6 +393,99 @@ final class SwiftPMWorkspaceTests: XCTestCase {
393393
check("-target", Triple.hostTriple.tripleString, arguments: arguments)
394394
#endif
395395
}
396+
397+
func testSymlinkInWorkspaceSwift() {
398+
// FIXME: should be possible to use InMemoryFileSystem.
399+
let fs = localFileSystem
400+
let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true)
401+
402+
try! fs.createFiles(root: tempDir.path, files: [
403+
"pkg_real/Sources/lib/a.swift": "",
404+
"pkg_real/Package.swift": """
405+
// swift-tools-version:4.2
406+
import PackageDescription
407+
let package = Package(name: "a", products: [], dependencies: [],
408+
targets: [.target(name: "lib", dependencies: [])])
409+
"""
410+
])
411+
let packageRoot = tempDir.path.appending(component: "pkg")
412+
413+
try! FileManager.default.createSymbolicLink(
414+
atPath: packageRoot.pathString,
415+
withDestinationPath: "pkg_real")
416+
417+
let tr = ToolchainRegistry.shared
418+
let ws = try! SwiftPMWorkspace(
419+
workspacePath: packageRoot,
420+
toolchainRegistry: tr,
421+
fileSystem: fs,
422+
buildSetup: TestSourceKitServer.buildSetup)
423+
424+
let aswift1 = packageRoot.appending(components: "Sources", "lib", "a.swift")
425+
let aswift2 = tempDir.path
426+
.appending(component: "pkg_real")
427+
.appending(components: "Sources", "lib", "a.swift")
428+
let manifest = packageRoot.appending(components: "Package.swift")
429+
430+
let arguments1 = ws.settings(for: aswift1.asURL, .swift)?.compilerArguments
431+
let arguments2 = ws.settings(for: aswift2.asURL, .swift)?.compilerArguments
432+
XCTAssertNotNil(arguments1)
433+
XCTAssertNotNil(arguments2)
434+
XCTAssertEqual(arguments1, arguments2)
435+
436+
checkNot(aswift1.pathString, arguments: arguments1 ?? [])
437+
check(resolveSymlinks(aswift1).pathString, arguments: arguments1 ?? [])
438+
439+
let argsManifest = ws.settings(for: manifest.asURL, .swift)?.compilerArguments
440+
XCTAssertNotNil(argsManifest)
441+
442+
checkNot(manifest.pathString, arguments: argsManifest ?? [])
443+
check(resolveSymlinks(manifest).pathString, arguments: argsManifest ?? [])
444+
}
445+
446+
func testSymlinkInWorkspaceCXX() {
447+
// FIXME: should be possible to use InMemoryFileSystem.
448+
let fs = localFileSystem
449+
let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true)
450+
try! fs.createFiles(root: tempDir.path, files: [
451+
"pkg_real/Sources/lib/a.cpp": "",
452+
"pkg_real/Sources/lib/b.cpp": "",
453+
"pkg_real/Sources/lib/include/a.h": "",
454+
"pkg_real/Package.swift": """
455+
// swift-tools-version:4.2
456+
import PackageDescription
457+
let package = Package(name: "a", products: [], dependencies: [],
458+
targets: [.target(name: "lib", dependencies: [])],
459+
cxxLanguageStandard: .cxx14)
460+
"""
461+
])
462+
463+
let packageRoot = tempDir.path.appending(component: "pkg")
464+
465+
try! FileManager.default.createSymbolicLink(
466+
atPath: packageRoot.pathString,
467+
withDestinationPath: "pkg_real")
468+
469+
let tr = ToolchainRegistry.shared
470+
let ws = try! SwiftPMWorkspace(
471+
workspacePath: packageRoot,
472+
toolchainRegistry: tr,
473+
fileSystem: fs,
474+
buildSetup: TestSourceKitServer.buildSetup)
475+
476+
let acxx = packageRoot.appending(components: "Sources", "lib", "a.cpp")
477+
let ah = packageRoot.appending(components: "Sources", "lib", "include", "a.h")
478+
479+
let argsCxx = ws.settings(for: acxx.asURL, .cpp)?.compilerArguments
480+
XCTAssertNotNil(argsCxx)
481+
check(acxx.pathString, arguments: argsCxx ?? [])
482+
checkNot(resolveSymlinks(acxx).pathString, arguments: argsCxx ?? [])
483+
484+
let argsH = ws.settings(for: ah.asURL, .cpp)?.compilerArguments
485+
XCTAssertNotNil(argsH)
486+
checkNot(ah.pathString, arguments: argsH ?? [])
487+
check(resolveSymlinks(ah).pathString, arguments: argsH ?? [])
488+
}
396489
}
397490

398491
private func checkNot(

0 commit comments

Comments
 (0)