@@ -1001,7 +1001,7 @@ public final class ProductBuildDescription {
1001
1001
return buildParameters. binaryPath ( for: product)
1002
1002
}
1003
1003
1004
- /// The objects in this product.
1004
+ /// All object files to link into this product.
1005
1005
///
1006
1006
// Computed during build planning.
1007
1007
public fileprivate( set) var objects = SortedArray < AbsolutePath > ( )
@@ -1019,6 +1019,10 @@ public final class ProductBuildDescription {
1019
1019
/// The list of Swift modules that should be passed to the linker. This is required for debugging to work.
1020
1020
fileprivate var swiftASTs : SortedArray < AbsolutePath > = . init( )
1021
1021
1022
+ /// Ordered mapping of any mainless object files used by the product to the originals in the executable
1023
+ /// targets that produce them.
1024
+ public fileprivate( set) var executableObjects : OrderedDictionary < AbsolutePath , AbsolutePath > = . init( )
1025
+
1022
1026
/// Paths to the binary libraries the product depends on.
1023
1027
fileprivate var libraryBinaryPaths : Set < AbsolutePath > = [ ]
1024
1028
@@ -1032,6 +1036,11 @@ public final class ProductBuildDescription {
1032
1036
return tempsPath. appending ( component: " Objects.LinkFileList " )
1033
1037
}
1034
1038
1039
+ /// Path to the symbol removal list file (list of symbols to remove from any objects linked into this product).
1040
+ var mainSymbolRemovalListFilePath : AbsolutePath {
1041
+ return tempsPath. appending ( component: " MainSymbol.SymbolList " )
1042
+ }
1043
+
1035
1044
/// Diagnostics Engine for emitting diagnostics.
1036
1045
let diagnostics : DiagnosticsEngine
1037
1046
@@ -1189,6 +1198,16 @@ public final class ProductBuildDescription {
1189
1198
try fs. writeFileContents ( linkFileListPath, bytes: stream. bytes)
1190
1199
}
1191
1200
1201
+ /// Writes symbol removal list file to the filesystem.
1202
+ func writeMainSymbolRemovalListFile( _ fs: FileSystem ) throws {
1203
+ let stream = BufferedOutputByteStream ( )
1204
+
1205
+ stream <<< " _main \n "
1206
+
1207
+ try fs. createDirectory ( mainSymbolRemovalListFilePath. parentDirectory, recursive: true )
1208
+ try fs. writeFileContents ( mainSymbolRemovalListFilePath, bytes: stream. bytes)
1209
+ }
1210
+
1192
1211
/// Returns the build flags from the declared build settings.
1193
1212
private func buildSettingsFlags( ) -> [ String ] {
1194
1213
var flags : [ String ] = [ ]
@@ -1480,14 +1499,14 @@ public class BuildPlan {
1480
1499
1481
1500
// Link C++ if needed.
1482
1501
// Note: This will come from build settings in future.
1483
- for target in dependencies. staticTargets {
1502
+ for target in dependencies. staticTargets + dependencies . executableTargets {
1484
1503
if case let target as ClangTarget = target. underlyingTarget, target. isCXX {
1485
1504
buildProduct. additionalFlags += self . buildParameters. toolchain. extraCPPFlags
1486
1505
break
1487
1506
}
1488
1507
}
1489
1508
1490
- for target in dependencies. staticTargets {
1509
+ for target in dependencies. staticTargets + dependencies . executableTargets {
1491
1510
switch target. underlyingTarget {
1492
1511
case is SwiftTarget :
1493
1512
// Swift targets are guaranteed to have a corresponding Swift description.
@@ -1509,10 +1528,29 @@ public class BuildPlan {
1509
1528
}
1510
1529
}
1511
1530
1512
- buildProduct. staticTargets = dependencies. staticTargets
1531
+ buildProduct. staticTargets = dependencies. staticTargets + dependencies . executableTargets
1513
1532
buildProduct. dylibs = dependencies. dylibs. map ( { productMap [ $0] ! } )
1514
1533
buildProduct. objects += dependencies. staticTargets. flatMap ( { targetMap [ $0] !. objects } )
1515
1534
buildProduct. libraryBinaryPaths = dependencies. libraryBinaryPaths
1535
+
1536
+ // If we're linking against any executable targets, we need to create versions of the .o files from those
1537
+ // targets that elide the `_main` symbol. We should look into whether linker options can be added to specify
1538
+ // this on the command line.
1539
+ if !dependencies. executableTargets. isEmpty {
1540
+ // Creating a mapping from each .o file in each executable target to a corresponding modified .o file in
1541
+ // our product directory. This duplicates work if an executable is tested by more than one test product
1542
+ // but has the advantage of keeping the executable target clean unless it's being used by a test target.
1543
+ for target in dependencies. executableTargets. map ( { targetMap [ $0] ! } ) {
1544
+ for object in target. objects {
1545
+ // FIXME: Plenty of opportunity for collisions here — how is this handled for regular object files?
1546
+ let mainlessObject = buildProduct. tempsPath. appending ( components: " LinkedExecutableObjects " , " \( target. target. c99name) _ \( object. basename) " )
1547
+ buildProduct. executableObjects [ mainlessObject] = object
1548
+ buildProduct. objects. insert ( mainlessObject)
1549
+ }
1550
+ }
1551
+ // The symbol removal tool on some platforms requires a separate file list in the file system.
1552
+ try buildProduct. writeMainSymbolRemovalListFile ( fileSystem)
1553
+ }
1516
1554
1517
1555
// Write the link filelist file.
1518
1556
//
@@ -1527,6 +1565,7 @@ public class BuildPlan {
1527
1565
) -> (
1528
1566
dylibs: [ ResolvedProduct ] ,
1529
1567
staticTargets: [ ResolvedTarget ] ,
1568
+ executableTargets: [ ResolvedTarget ] ,
1530
1569
systemModules: [ ResolvedTarget ] ,
1531
1570
libraryBinaryPaths: Set < AbsolutePath >
1532
1571
) {
@@ -1554,6 +1593,7 @@ public class BuildPlan {
1554
1593
// Create empty arrays to collect our results.
1555
1594
var linkLibraries = [ ResolvedProduct] ( )
1556
1595
var staticTargets = [ ResolvedTarget] ( )
1596
+ var executableTargets = [ ResolvedTarget] ( )
1557
1597
var systemModules = [ ResolvedTarget] ( )
1558
1598
var libraryBinaryPaths : Set < AbsolutePath > = [ ]
1559
1599
@@ -1563,7 +1603,14 @@ public class BuildPlan {
1563
1603
switch target. type {
1564
1604
// Include executable and tests only if they're top level contents
1565
1605
// of the product. Otherwise they are just build time dependency.
1566
- case . executable, . test:
1606
+ case . executable:
1607
+ if product. targets. contains ( target) {
1608
+ staticTargets. append ( target)
1609
+ }
1610
+ else {
1611
+ executableTargets. append ( target)
1612
+ }
1613
+ case . test:
1567
1614
if product. targets. contains ( target) {
1568
1615
staticTargets. append ( target)
1569
1616
}
@@ -1598,7 +1645,7 @@ public class BuildPlan {
1598
1645
}
1599
1646
}
1600
1647
1601
- return ( linkLibraries, staticTargets, systemModules, libraryBinaryPaths)
1648
+ return ( linkLibraries, staticTargets, executableTargets , systemModules, libraryBinaryPaths)
1602
1649
}
1603
1650
1604
1651
/// Plan a Clang target.
0 commit comments