Skip to content

Commit cdcfe04

Browse files
committed
Change how libraries are specified to the linker when using searched libs
- remove the platform specifics from computeLibraryArgs (we cannot assume that all libraries have a lib prefix and what there suffix is.) So we now use the FileType prefix and remove any suffix when using searchPathFlagsForLD, moving this into the LinkerSpec.LibrarySpecifier extension, this allows for proper searching of libraries, and linking of dynamic libraries (especially on Windows).
1 parent 3a9a8f6 commit cdcfe04

28 files changed

+206
-232
lines changed

Sources/SWBCore/SpecImplementations/LinkerSpec.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ open class LinkerSpec : CommandLineToolSpec, @unchecked Sendable {
8989
/// The path to the privacy file, if one exists.
9090
public let privacyFile: Path?
9191

92-
public init(kind: Kind, path: Path, mode: Mode, useSearchPaths: Bool, swiftModulePaths: [String: Path], swiftModuleAdditionalLinkerArgResponseFilePaths: [String: Path], explicitDependencies: [Path] = [], topLevelItemPath: Path? = nil, dsymPath: Path? = nil, xcframeworkSourcePath: Path? = nil, privacyFile: Path? = nil) {
92+
public let libPrefix: String?
93+
94+
public init(kind: Kind, path: Path, mode: Mode, useSearchPaths: Bool, swiftModulePaths: [String: Path], swiftModuleAdditionalLinkerArgResponseFilePaths: [String: Path], prefixes: [String] = [], explicitDependencies: [Path] = [], topLevelItemPath: Path? = nil, dsymPath: Path? = nil, xcframeworkSourcePath: Path? = nil, privacyFile: Path? = nil) {
9395
self.kind = kind
9496
self.path = path
9597
self.mode = mode
@@ -101,6 +103,7 @@ open class LinkerSpec : CommandLineToolSpec, @unchecked Sendable {
101103
self.dsymPath = dsymPath
102104
self.xcframeworkSourcePath = xcframeworkSourcePath
103105
self.privacyFile = privacyFile
106+
self.libPrefix = prefixes.first
104107
}
105108
}
106109

Sources/SWBCore/SpecImplementations/Specs.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ public class FileTypeSpec : Spec, SpecType, @unchecked Sendable {
301301
/// Returns `true` if the `isWrapperFolder` value is set in the XCSpec for the file spec.
302302
public let isWrapper: Bool
303303

304+
/// Returns any common prefix this file may have (currently used when specifying searched libraries to linker)
305+
public let prefixes: [String]
306+
304307
required init(_ parser: SpecParser, _ basedOnSpec: Spec?) {
305308
let basedOnFileTypeSpec = basedOnSpec as? FileTypeSpec ?? nil
306309

@@ -318,8 +321,8 @@ public class FileTypeSpec : Spec, SpecType, @unchecked Sendable {
318321
self.isEmbeddableInProduct = parser.parseBool("IsEmbeddable") ?? false
319322
self.validateOnCopy = parser.parseBool("ValidateOnCopy") ?? false
320323
self.codeSignOnCopy = parser.parseBool("CodeSignOnCopy") ?? false
321-
322324
self.isWrapper = parser.parseBool("IsWrapperFolder") ?? false
325+
self.prefixes = parser.parseStringList("Prefix") ?? []
323326

324327
// Parse and ignore keys we have no use for.
325328
//
@@ -358,7 +361,6 @@ public class FileTypeSpec : Spec, SpecType, @unchecked Sendable {
358361
parser.parseStringList("MIMETypes")
359362
parser.parseString("Permissions")
360363
parser.parseString("PlistStructureDefinition")
361-
parser.parseStringList("Prefix")
362364
parser.parseBool("RemoveHeadersOnCopy")
363365
parser.parseBool("RequiresHardTabs")
364366
parser.parseString("UTI")

Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift

Lines changed: 37 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,41 +1289,15 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
12891289
private static func computeLibraryArgs(_ libraries: [LibrarySpecifier], scope: MacroEvaluationScope) -> (args: [String], inputs: [Path]) {
12901290
// Construct the library arguments.
12911291
return libraries.compactMap { specifier -> (args: [String], inputs: [Path]) in
1292-
let basename = specifier.path.basename
1293-
1294-
// FIXME: This isn't a good system, we need to redesign how we talk to the linker w.r.t. search paths and our notion of paths.
12951292
switch specifier.kind {
1296-
case .static:
1297-
if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".a") {
1298-
return (specifier.searchPathFlagsForLd(basename.withoutPrefix("lib").withoutSuffix(".a")), [])
1299-
}
1300-
return (specifier.absolutePathFlagsForLd(), [specifier.path])
1301-
case .dynamic:
1302-
let suffix = ".\(scope.evaluate(BuiltinMacros.DYNAMIC_LIBRARY_EXTENSION))"
1303-
if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(suffix) {
1304-
return (specifier.searchPathFlagsForLd(basename.withoutPrefix("lib").withoutSuffix(suffix)), [])
1305-
}
1306-
return (specifier.absolutePathFlagsForLd(), [specifier.path])
1307-
case .textBased:
1308-
if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".tbd") {
1309-
// .merge and .reexport are not supported for text-based libraries.
1310-
return (specifier.searchPathFlagsForLd(basename.withoutPrefix("lib").withoutSuffix(".tbd")), [])
1311-
}
1312-
return (specifier.absolutePathFlagsForLd(), [specifier.path])
1313-
case .framework:
1314-
let frameworkName = Path(basename).withoutSuffix
1293+
case .static, .dynamic, .textBased, .framework:
13151294
if specifier.useSearchPaths {
1316-
return (specifier.searchPathFlagsForLd(frameworkName), [])
1317-
}
1318-
let absPathArgs = specifier.absolutePathFlagsForLd()
1319-
let returnPath: Path
1320-
if let pathArg = absPathArgs.last, Path(pathArg).basename == frameworkName {
1321-
returnPath = Path(pathArg)
1322-
}
1323-
else {
1324-
returnPath = specifier.path
1295+
let args = specifier.searchPathFlagsForLd()
1296+
if !args.isEmpty { // no search args, fallback to absolute path to library
1297+
return (args, [])
1298+
}
13251299
}
1326-
return (absPathArgs, [returnPath])
1300+
return (specifier.absolutePathFlagsForLd(), [specifier.path])
13271301
case .object:
13281302
// Object files are added to linker inputs in the sources task producer.
13291303
return ([], [])
@@ -1559,35 +1533,44 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
15591533

15601534
/// Extensions to `LinkerSpec.LibrarySpecifier` specific to the dynamic linker.
15611535
fileprivate extension LinkerSpec.LibrarySpecifier {
1562-
func searchPathFlagsForLd(_ name: String) -> [String] {
1536+
func searchPathFlagsForLd() -> [String] {
1537+
let strippedName: String
1538+
if let prefix = libPrefix, path.basename.hasPrefix(prefix) {
1539+
strippedName = Path(path.basename).withoutSuffix.withoutPrefix(prefix)
1540+
} else {
1541+
if libPrefix != nil { // we need a prefix for linking with search paths
1542+
return [] // this will fallback to using absolute paths
1543+
}
1544+
strippedName = Path(path.basename).withoutSuffix
1545+
}
15631546
switch (kind, mode) {
15641547
case (.dynamic, .normal):
1565-
return ["-l" + name]
1548+
return ["-l" + strippedName]
15661549
case (.dynamic, .reexport):
1567-
return ["-Xlinker", "-reexport-l" + name]
1550+
return ["-Xlinker", "-reexport-l" + strippedName]
15681551
case (.dynamic, .merge):
1569-
return ["-Xlinker", "-merge-l" + name]
1552+
return ["-Xlinker", "-merge-l" + strippedName]
15701553
case (.dynamic, .reexport_merge):
1571-
return ["-Xlinker", "-no_merge-l" + name]
1554+
return ["-Xlinker", "-no_merge-l" + strippedName]
15721555
case (.dynamic, .weak):
1573-
return ["-weak-l" + name]
1556+
return ["-weak-l" + strippedName]
15741557
case (.static, .weak),
15751558
(.textBased, .weak):
1576-
return ["-weak-l" + name]
1559+
return ["-weak-l" + strippedName]
15771560
case (.static, _),
15781561
(.textBased, _):
15791562
// Other modes are not supported for these kinds.
1580-
return ["-l" + name]
1563+
return ["-l" + strippedName]
15811564
case (.framework, .normal):
1582-
return ["-framework", name]
1565+
return ["-framework", strippedName]
15831566
case (.framework, .reexport):
1584-
return ["-Xlinker", "-reexport_framework", "-Xlinker", name]
1567+
return ["-Xlinker", "-reexport_framework", "-Xlinker", strippedName]
15851568
case (.framework, .merge):
1586-
return ["-Xlinker", "-merge_framework", "-Xlinker", name]
1569+
return ["-Xlinker", "-merge_framework", "-Xlinker", strippedName]
15871570
case (.framework, .reexport_merge):
1588-
return ["-Xlinker", "-no_merge_framework", "-Xlinker", name]
1571+
return ["-Xlinker", "-no_merge_framework", "-Xlinker", strippedName]
15891572
case (.framework, .weak):
1590-
return ["-weak_framework", name]
1573+
return ["-weak_framework", strippedName]
15911574
case (.object, _):
15921575
// Object files are added to linker inputs in the sources task producer.
15931576
return []
@@ -1724,15 +1707,17 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
17241707
delegate.warning("Product \(cbc.output.basename) cannot weak-link \(specifier.kind) \(basename)")
17251708
}
17261709

1727-
if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".a") {
1710+
if specifier.useSearchPaths {
17281711
// Locate using search paths: Add a -l option and *don't* add the path to the library as an input to the task.
1729-
return ["-l" + basename.withoutPrefix("lib").withoutSuffix(".a")]
1730-
}
1731-
else {
1732-
// Locate using an absolute path: Add the path as an option and as an input to the task.
1733-
inputPaths.append(specifier.path)
1734-
return [specifier.path.str]
1712+
let basename = specifier.path.basename
1713+
let expectedPrefix = specifier.libPrefix ?? "lib"
1714+
if basename.hasPrefix(expectedPrefix) {
1715+
return ["-l" + Path(basename).withoutSuffix.withoutPrefix(expectedPrefix)]
1716+
}
17351717
}
1718+
// Locate using an absolute path: Add the path as an option and as an input to the task.
1719+
inputPaths.append(specifier.path)
1720+
return [specifier.path.str]
17361721

17371722
case .object:
17381723
// Object files are added to linker inputs in the sources task producer and so end up in the link-file-list.

Sources/SWBGenericUnixPlatform/Specs/Unix.xcspec

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,14 @@
9191
IconNamePrefix = "TargetPlugin";
9292
DefaultTargetName = "Object File";
9393
},
94+
{
95+
Domain = generic-unix;
96+
Type = FileType;
97+
Identifier = compiled.mach-o.dylib;
98+
BasedOn = compiled.mach-o;
99+
Prefix = (lib);
100+
Extensions = (so);
101+
IsLibrary = YES;
102+
IsDynamicLibrary = YES;
103+
}
94104
)

Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/SourcesTaskProducer.swift

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
507507
useSearchPaths: useSearchPaths,
508508
swiftModulePaths: swiftModulePaths,
509509
swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths,
510+
prefixes: fileType.prefixes,
510511
privacyFile: privacyFile
511512
)
512513
} else if fileType.conformsTo(context.lookupFileType(identifier: "compiled.mach-o.dylib")!) {
@@ -517,6 +518,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
517518
useSearchPaths: useSearchPaths,
518519
swiftModulePaths: [:],
519520
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
521+
prefixes: fileType.prefixes,
520522
privacyFile: privacyFile
521523
)
522524
} else if fileType.conformsTo(context.lookupFileType(identifier: "sourcecode.text-based-dylib-definition")!) {
@@ -527,17 +529,18 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
527529
useSearchPaths: useSearchPaths,
528530
swiftModulePaths: [:],
529531
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
532+
prefixes: fileType.prefixes,
530533
privacyFile: privacyFile
531534
)
532535
} else if fileType.conformsTo(context.lookupFileType(identifier: "wrapper.framework")!) {
533-
func kindFromSettings(_ settings: Settings) -> LinkerSpec.LibrarySpecifier.Kind? {
536+
func kindFromSettings(_ settings: Settings) -> (kind: LinkerSpec.LibrarySpecifier.Kind, prefixes: [String])? {
534537
switch settings.globalScope.evaluate(BuiltinMacros.MACH_O_TYPE) {
535538
case "staticlib":
536-
return .static
539+
return (.static, context.lookupFileType(identifier: "archive.ar")?.prefixes ?? [])
537540
case "mh_dylib":
538-
return .dynamic
541+
return (.dynamic, context.lookupFileType(identifier: "compiled.mach-o.dylib")?.prefixes ?? [])
539542
case "mh_object":
540-
return .object
543+
return (.object, [])
541544
default:
542545
return nil
543546
}
@@ -547,9 +550,11 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
547550
let path: Path
548551
let dsymPath: Path?
549552
let topLevelItemPath: Path?
553+
let prefixes: [String]
550554
if let settingsForRef, let presumedKind = kindFromSettings(settingsForRef), !useSearchPaths {
551555
// If we have a Settings from a cross-project reference, use the _actual_ library path. This prevents downstream code from reconstituting the framework path by joining the framework path with the basename of the framework, which won't be correct for deep frameworks which also need the Versions/A path component.
552-
kind = presumedKind
556+
kind = presumedKind.kind
557+
prefixes = presumedKind.prefixes
553558
path = settingsForRef.globalScope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(settingsForRef.globalScope.evaluate(BuiltinMacros.EXECUTABLE_PATH)).normalize()
554559
topLevelItemPath = absolutePath
555560
if shouldGenerateDSYM(settingsForRef.globalScope) {
@@ -563,6 +568,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
563568
path = absolutePath
564569
topLevelItemPath = nil
565570
dsymPath = nil
571+
prefixes = []
566572
}
567573

568574
return LinkerSpec.LibrarySpecifier(
@@ -572,6 +578,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
572578
useSearchPaths: useSearchPaths,
573579
swiftModulePaths: [:],
574580
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
581+
prefixes: prefixes,
575582
topLevelItemPath: topLevelItemPath,
576583
dsymPath: dsymPath,
577584
privacyFile: privacyFile
@@ -581,7 +588,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
581588
kind: .object,
582589
path: absolutePath,
583590
mode: buildFile.shouldLinkWeakly ? .weak : .normal,
584-
useSearchPaths: useSearchPaths,
591+
useSearchPaths: false,
585592
swiftModulePaths: swiftModulePaths,
586593
swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths,
587594
privacyFile: privacyFile
@@ -621,10 +628,16 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
621628
}
622629

623630
let libraryKind: LinkerSpec.LibrarySpecifier.Kind
631+
let prefixes: [String]
624632
switch library.libraryType {
625-
case .framework: libraryKind = .framework; break
626-
case .dynamicLibrary: libraryKind = .dynamic; break
627-
case .staticLibrary: libraryKind = .static; break
633+
case .framework: libraryKind = .framework; prefixes = []
634+
case .dynamicLibrary:
635+
libraryKind = .dynamic;
636+
prefixes = context.lookupFileType(identifier: "compiled.mach-o.dylib")?.prefixes ?? []
637+
case .staticLibrary:
638+
libraryKind = .static
639+
prefixes = context.lookupFileType(identifier: "archive.ar")?.prefixes ?? []
640+
break
628641
case let .unknown(fileExtension):
629642
// An error of type this type should have already been manifested.
630643
assertionFailure("unknown xcframework type: \(fileExtension)")
@@ -651,6 +664,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
651664
useSearchPaths: useSearchPaths,
652665
swiftModulePaths: [:],
653666
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
667+
prefixes: prefixes,
654668
explicitDependencies: outputFilePaths,
655669
xcframeworkSourcePath: xcframeworkPath,
656670
privacyFile: nil

Sources/SWBTestSupport/RunDestinationTestSupport.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ extension RunDestinationInfo {
320320
/// An `Environment` object with `PATH` or `LD_LIBRARY_PATH` set appropriately pointing into the toolchain to be able to run a built Swift binary in tests.
321321
///
322322
/// - note: On macOS, the OS provided Swift runtime is used, so `DYLD_LIBRARY_PATH` is never set for Mach-O destinations.
323-
package func hostRuntimeEnvironment(_ core: Core, initialEnvironment: Environment = Environment()) -> Environment {
323+
package func hostRuntimeEnvironment(_ core: Core, initialEnvironment: Environment = Environment()) throws -> Environment {
324324
var environment = initialEnvironment
325325
guard let toolchain = core.toolchainRegistry.defaultToolchain else {
326326
return environment
@@ -329,7 +329,10 @@ extension RunDestinationInfo {
329329
case .elf:
330330
environment.prependPath(key: "LD_LIBRARY_PATH", value: toolchain.path.join("usr/lib/swift/\(platform)").str)
331331
case .pe:
332-
environment.prependPath(key: .path, value: core.developerPath.path.join("Runtimes").join(toolchain.version.description).join("usr/bin").str)
332+
let currentPath = Environment.current[.path] ?? ""
333+
let runtimePath = core.developerPath.path.join("Runtimes").join(toolchain.version.description).join("usr/bin").str
334+
let newPath = currentPath.isEmpty ? "\(runtimePath)" : "\(runtimePath);\(currentPath)"
335+
environment[.path] = newPath
333336
case .macho:
334337
// Fall back to the OS provided Swift runtime
335338
break

Sources/SWBUniversalPlatform/Specs/ProductTypes.xcspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
8383
MACH_O_TYPE = "mh_dylib";
8484
REZ_EXECUTABLE = YES;
85+
EXECUTABLE_PREFIX = "lib";
8586
EXECUTABLE_SUFFIX = ".$(EXECUTABLE_EXTENSION)";
8687
EXECUTABLE_EXTENSION = "$(DYNAMIC_LIBRARY_EXTENSION:default=dylib)";
8788
PUBLIC_HEADERS_FOLDER_PATH = "/usr/local/include";

Sources/SWBUniversalPlatform/Specs/StandardFileTypes.xcspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@
903903
Class = PBXMachOFileType;
904904
BasedOn = compiled.mach-o;
905905
Extensions = (dylib);
906+
Prefix = (lib);
906907
IsLibrary = YES;
907908
IsDynamicLibrary = YES;
908909
CodeSignOnCopy = YES;
@@ -939,6 +940,7 @@
939940
Identifier = sourcecode.text-based-dylib-definition;
940941
BasedOn = sourcecode;
941942
Extensions = (tbd);
943+
Prefix = (lib);
942944
IsLibrary = YES;
943945
IsDynamicLibrary = YES;
944946
CodeSignOnCopy = YES;

0 commit comments

Comments
 (0)