Skip to content

Commit 87a555c

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 (the linker wilk know...), moving this into the LinkerSpec.LibrarySpecifier extension, this allows for proper searching of libraries, and linking of dynamic libraries (especially on Windows).
1 parent 469349f commit 87a555c

File tree

25 files changed

+159
-214
lines changed

25 files changed

+159
-214
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: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,41 +1289,12 @@ 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+
return (specifier.searchPathFlagsForLd(), [])
13251296
}
1326-
return (absPathArgs, [returnPath])
1297+
return (specifier.absolutePathFlagsForLd(), [specifier.path])
13271298
case .object:
13281299
// Object files are added to linker inputs in the sources task producer.
13291300
return ([], [])
@@ -1559,35 +1530,41 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
15591530

15601531
/// Extensions to `LinkerSpec.LibrarySpecifier` specific to the dynamic linker.
15611532
fileprivate extension LinkerSpec.LibrarySpecifier {
1562-
func searchPathFlagsForLd(_ name: String) -> [String] {
1533+
func searchPathFlagsForLd() -> [String] {
1534+
let strippedName: String
1535+
if let prefix = libPrefix, path.basename.hasPrefix(prefix) {
1536+
strippedName = Path(path.basename).withoutSuffix.withoutPrefix(prefix)
1537+
} else {
1538+
strippedName = Path(path.basename).withoutSuffix
1539+
}
15631540
switch (kind, mode) {
15641541
case (.dynamic, .normal):
1565-
return ["-l" + name]
1542+
return ["-l" + strippedName]
15661543
case (.dynamic, .reexport):
1567-
return ["-Xlinker", "-reexport-l" + name]
1544+
return ["-Xlinker", "-reexport-l" + strippedName]
15681545
case (.dynamic, .merge):
1569-
return ["-Xlinker", "-merge-l" + name]
1546+
return ["-Xlinker", "-merge-l" + strippedName]
15701547
case (.dynamic, .reexport_merge):
1571-
return ["-Xlinker", "-no_merge-l" + name]
1548+
return ["-Xlinker", "-no_merge-l" + strippedName]
15721549
case (.dynamic, .weak):
1573-
return ["-weak-l" + name]
1550+
return ["-weak-l" + strippedName]
15741551
case (.static, .weak),
15751552
(.textBased, .weak):
1576-
return ["-weak-l" + name]
1553+
return ["-weak-l" + strippedName]
15771554
case (.static, _),
15781555
(.textBased, _):
15791556
// Other modes are not supported for these kinds.
1580-
return ["-l" + name]
1557+
return ["-l" + strippedName]
15811558
case (.framework, .normal):
1582-
return ["-framework", name]
1559+
return ["-framework", strippedName]
15831560
case (.framework, .reexport):
1584-
return ["-Xlinker", "-reexport_framework", "-Xlinker", name]
1561+
return ["-Xlinker", "-reexport_framework", "-Xlinker", strippedName]
15851562
case (.framework, .merge):
1586-
return ["-Xlinker", "-merge_framework", "-Xlinker", name]
1563+
return ["-Xlinker", "-merge_framework", "-Xlinker", strippedName]
15871564
case (.framework, .reexport_merge):
1588-
return ["-Xlinker", "-no_merge_framework", "-Xlinker", name]
1565+
return ["-Xlinker", "-no_merge_framework", "-Xlinker", strippedName]
15891566
case (.framework, .weak):
1590-
return ["-weak_framework", name]
1567+
return ["-weak_framework", strippedName]
15911568
case (.object, _):
15921569
// Object files are added to linker inputs in the sources task producer.
15931570
return []
@@ -1724,9 +1701,9 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
17241701
delegate.warning("Product \(cbc.output.basename) cannot weak-link \(specifier.kind) \(basename)")
17251702
}
17261703

1727-
if specifier.useSearchPaths, basename.hasPrefix("lib"), basename.hasSuffix(".a") {
1704+
if specifier.useSearchPaths {
17281705
// 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")]
1706+
return ["-l" + Path(specifier.path.basename).withoutSuffix.withoutPrefix(specifier.libPrefix ?? "lib")]
17301707
}
17311708
else {
17321709
// Locate using an absolute path: Add the path as an option and as an input to the task.

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
@@ -506,6 +506,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
506506
useSearchPaths: useSearchPaths,
507507
swiftModulePaths: swiftModulePaths,
508508
swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths,
509+
prefixes: fileType.prefixes,
509510
privacyFile: privacyFile
510511
)
511512
} else if fileType.conformsTo(context.lookupFileType(identifier: "compiled.mach-o.dylib")!) {
@@ -516,6 +517,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
516517
useSearchPaths: useSearchPaths,
517518
swiftModulePaths: [:],
518519
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
520+
prefixes: fileType.prefixes,
519521
privacyFile: privacyFile
520522
)
521523
} else if fileType.conformsTo(context.lookupFileType(identifier: "sourcecode.text-based-dylib-definition")!) {
@@ -526,17 +528,18 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
526528
useSearchPaths: useSearchPaths,
527529
swiftModulePaths: [:],
528530
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
531+
prefixes: fileType.prefixes,
529532
privacyFile: privacyFile
530533
)
531534
} else if fileType.conformsTo(context.lookupFileType(identifier: "wrapper.framework")!) {
532-
func kindFromSettings(_ settings: Settings) -> LinkerSpec.LibrarySpecifier.Kind? {
535+
func kindFromSettings(_ settings: Settings) -> (kind: LinkerSpec.LibrarySpecifier.Kind, prefixes: [String])? {
533536
switch settings.globalScope.evaluate(BuiltinMacros.MACH_O_TYPE) {
534537
case "staticlib":
535-
return .static
538+
return (.static, context.lookupFileType(identifier: "archive.ar")?.prefixes ?? [])
536539
case "mh_dylib":
537-
return .dynamic
540+
return (.dynamic, context.lookupFileType(identifier: "compiled.mach-o.dylib")?.prefixes ?? [])
538541
case "mh_object":
539-
return .object
542+
return (.object, [])
540543
default:
541544
return nil
542545
}
@@ -546,9 +549,11 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
546549
let path: Path
547550
let dsymPath: Path?
548551
let topLevelItemPath: Path?
552+
let prefixes: [String]
549553
if let settingsForRef, let presumedKind = kindFromSettings(settingsForRef), !useSearchPaths {
550554
// 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.
551-
kind = presumedKind
555+
kind = presumedKind.kind
556+
prefixes = presumedKind.prefixes
552557
path = settingsForRef.globalScope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(settingsForRef.globalScope.evaluate(BuiltinMacros.EXECUTABLE_PATH)).normalize()
553558
topLevelItemPath = absolutePath
554559
if shouldGenerateDSYM(settingsForRef.globalScope) {
@@ -562,6 +567,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
562567
path = absolutePath
563568
topLevelItemPath = nil
564569
dsymPath = nil
570+
prefixes = []
565571
}
566572

567573
return LinkerSpec.LibrarySpecifier(
@@ -571,6 +577,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
571577
useSearchPaths: useSearchPaths,
572578
swiftModulePaths: [:],
573579
swiftModuleAdditionalLinkerArgResponseFilePaths: [:],
580+
prefixes: prefixes,
574581
topLevelItemPath: topLevelItemPath,
575582
dsymPath: dsymPath,
576583
privacyFile: privacyFile
@@ -580,7 +587,7 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
580587
kind: .object,
581588
path: absolutePath,
582589
mode: buildFile.shouldLinkWeakly ? .weak : .normal,
583-
useSearchPaths: useSearchPaths,
590+
useSearchPaths: false,
584591
swiftModulePaths: swiftModulePaths,
585592
swiftModuleAdditionalLinkerArgResponseFilePaths: swiftModuleAdditionalLinkerArgResponseFilePaths,
586593
privacyFile: privacyFile
@@ -620,10 +627,16 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
620627
}
621628

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

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;

Sources/SWBWindowsPlatform/Plugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ struct WindowsSDKRegistryExtension: SDKRegistryExtension {
170170
"GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO",
171171

172172
"LIBRARY_SEARCH_PATHS": "$(inherited) $(SDKROOT)/usr/lib/swift/windows/$(CURRENT_ARCH)",
173-
"TEST_LIBRARY_SEARCH_PATHS": .plString("\(testingLibraryPath.strWithPosixSlashes)/Testing-$(SWIFT_TESTING_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH) \(testingLibraryPath.strWithPosixSlashes)/XCTest-$(XCTEST_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH)"),
173+
"TEST_LIBRARY_SEARCH_PATHS": .plString("\(testingLibraryPath.strWithPosixSlashes)/Testing-$(SWIFT_TESTING_VERSION)/usr/lib/swift/windows/ \(testingLibraryPath.strWithPosixSlashes)/Testing-$(SWIFT_TESTING_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH) \(testingLibraryPath.strWithPosixSlashes)/XCTest-$(XCTEST_VERSION)/usr/lib/swift/windows/$(CURRENT_ARCH) \(testingLibraryPath.strWithPosixSlashes)/XCTest-$(XCTEST_VERSION)/usr/lib/swift/windows"),
174174
"OTHER_SWIFT_FLAGS": "$(inherited) -libc $(DEFAULT_USE_RUNTIME)",
175175

176176
"DEFAULT_USE_RUNTIME": "MD",

Sources/SWBWindowsPlatform/Specs/Windows.xcspec

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
BasedOn = default:com.apple.product-type.library.dynamic;
8888
HasInfoPlist = NO;
8989
DefaultBuildProperties = {
90+
EXECUTABLE_PREFIX = "";
9091
PUBLIC_HEADERS_FOLDER_PATH = "";
9192
PRIVATE_HEADERS_FOLDER_PATH = "";
9293
};
@@ -111,4 +112,27 @@
111112
Identifier = org.swift.product-type.common.object;
112113
BasedOn = com.apple.product-type.library.static;
113114
},
115+
116+
{
117+
Domain = windows;
118+
Type = FileType;
119+
Identifier = archive.ar;
120+
BasedOn = archive;
121+
Extensions = (lib);
122+
Prefix = ();
123+
IsLibrary = YES;
124+
IsStaticLibrary = YES;
125+
ContainsNativeCode = YES;
126+
},
127+
128+
{
129+
Domain = windows;
130+
Type = FileType;
131+
Identifier = compiled.mach-o.dylib;
132+
BasedOn = compiled.mach-o;
133+
Prefix = ();
134+
Extensions = (dll);
135+
IsLibrary = YES;
136+
IsDynamicLibrary = YES;
137+
}
114138
)

Tests/SWBAndroidPlatformTests/SWBAndroidPlatformTests.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ fileprivate struct AndroidBuildOperationTests: CoreBasedTests {
7070
buildConfigurations: [
7171
TestBuildConfiguration("Debug", buildSettings: [
7272
"DYLIB_INSTALL_NAME_BASE": "$ORIGIN",
73-
74-
// FIXME: Find a way to make these default
75-
"EXECUTABLE_PREFIX": "lib",
7673
])
7774
],
7875
buildPhases: [
@@ -83,12 +80,6 @@ fileprivate struct AndroidBuildOperationTests: CoreBasedTests {
8380
TestStandardTarget(
8481
"staticlib",
8582
type: .staticLibrary,
86-
buildConfigurations: [
87-
TestBuildConfiguration("Debug", buildSettings: [
88-
// FIXME: Find a way to make these default
89-
"EXECUTABLE_PREFIX": "lib",
90-
])
91-
],
9283
buildPhases: [
9384
TestSourcesBuildPhase(["static.c"]),
9485
]

0 commit comments

Comments
 (0)