@@ -259,7 +259,8 @@ public final class ClangTargetBuildDescription {
259
259
self . derivedSources = Sources ( paths: [ ] , root: tempsPath. appending ( component: " DerivedSources " ) )
260
260
261
261
// Try computing modulemap path for a C library. This also creates the file in the file system, if needed.
262
- if target. type == . library {
262
+ // FIXME: Adding .executable here is probably not right, but is needed in order to test Clang executables.
263
+ if target. type == . library || target. type == . executable {
263
264
// If there's a custom module map, use it as given.
264
265
if case . custom( let path) = clangTarget. moduleMapType {
265
266
self . moduleMap = path
@@ -503,8 +504,7 @@ public final class SwiftTargetBuildDescription {
503
504
504
505
/// The path to the swiftmodule file after compilation.
505
506
var moduleOutputPath : AbsolutePath {
506
- let dirPath = ( target. type == . executable) ? tempsPath : buildParameters. buildPath
507
- return dirPath. appending ( component: target. c99name + " .swiftmodule " )
507
+ return buildParameters. buildPath. appending ( component: target. c99name + " .swiftmodule " )
508
508
}
509
509
510
510
/// The path to the wrapped swift module which is created using the modulewrap tool. This is required
@@ -1003,7 +1003,7 @@ public final class ProductBuildDescription {
1003
1003
return buildParameters. binaryPath ( for: product)
1004
1004
}
1005
1005
1006
- /// The objects in this product.
1006
+ /// All object files to link into this product.
1007
1007
///
1008
1008
// Computed during build planning.
1009
1009
public fileprivate( set) var objects = SortedArray < AbsolutePath > ( )
@@ -1021,6 +1021,10 @@ public final class ProductBuildDescription {
1021
1021
/// The list of Swift modules that should be passed to the linker. This is required for debugging to work.
1022
1022
fileprivate var swiftASTs : SortedArray < AbsolutePath > = . init( )
1023
1023
1024
+ /// Ordered mapping of any mainless object files used by the product to the originals in the executable
1025
+ /// targets that produce them.
1026
+ public fileprivate( set) var executableObjects : OrderedDictionary < AbsolutePath , AbsolutePath > = . init( )
1027
+
1024
1028
/// Paths to the binary libraries the product depends on.
1025
1029
fileprivate var libraryBinaryPaths : Set < AbsolutePath > = [ ]
1026
1030
@@ -1034,6 +1038,11 @@ public final class ProductBuildDescription {
1034
1038
return tempsPath. appending ( component: " Objects.LinkFileList " )
1035
1039
}
1036
1040
1041
+ /// Path to the symbol removal list file (list of symbols to remove from any objects linked into this product).
1042
+ var mainSymbolRemovalListFilePath : AbsolutePath {
1043
+ return tempsPath. appending ( component: " MainSymbol.SymbolList " )
1044
+ }
1045
+
1037
1046
/// Diagnostics Engine for emitting diagnostics.
1038
1047
let diagnostics : DiagnosticsEngine
1039
1048
@@ -1191,6 +1200,16 @@ public final class ProductBuildDescription {
1191
1200
try fs. writeFileContents ( linkFileListPath, bytes: stream. bytes)
1192
1201
}
1193
1202
1203
+ /// Writes symbol removal list file to the filesystem.
1204
+ func writeMainSymbolRemovalListFile( _ fs: FileSystem ) throws {
1205
+ let stream = BufferedOutputByteStream ( )
1206
+
1207
+ stream <<< " _main \n "
1208
+
1209
+ try fs. createDirectory ( mainSymbolRemovalListFilePath. parentDirectory, recursive: true )
1210
+ try fs. writeFileContents ( mainSymbolRemovalListFilePath, bytes: stream. bytes)
1211
+ }
1212
+
1194
1213
/// Returns the build flags from the declared build settings.
1195
1214
private func buildSettingsFlags( ) -> [ String ] {
1196
1215
var flags : [ String ] = [ ]
@@ -1482,14 +1501,14 @@ public class BuildPlan {
1482
1501
1483
1502
// Link C++ if needed.
1484
1503
// Note: This will come from build settings in future.
1485
- for target in dependencies. staticTargets {
1504
+ for target in dependencies. staticTargets + dependencies . executableTargets {
1486
1505
if case let target as ClangTarget = target. underlyingTarget, target. isCXX {
1487
1506
buildProduct. additionalFlags += self . buildParameters. toolchain. extraCPPFlags
1488
1507
break
1489
1508
}
1490
1509
}
1491
1510
1492
- for target in dependencies. staticTargets {
1511
+ for target in dependencies. staticTargets + dependencies . executableTargets {
1493
1512
switch target. underlyingTarget {
1494
1513
case is SwiftTarget :
1495
1514
// Swift targets are guaranteed to have a corresponding Swift description.
@@ -1513,7 +1532,7 @@ public class BuildPlan {
1513
1532
}
1514
1533
}
1515
1534
1516
- buildProduct. staticTargets = dependencies. staticTargets
1535
+ buildProduct. staticTargets = dependencies. staticTargets + dependencies . executableTargets
1517
1536
buildProduct. dylibs = try dependencies. dylibs. map {
1518
1537
guard let product = productMap [ $0] else {
1519
1538
throw InternalError ( " unknown product \( $0) " )
@@ -1527,6 +1546,25 @@ public class BuildPlan {
1527
1546
return target. objects
1528
1547
}
1529
1548
buildProduct. libraryBinaryPaths = dependencies. libraryBinaryPaths
1549
+
1550
+ // If we're linking against any executable targets, we need to create versions of the .o files from those
1551
+ // targets that elide the `_main` symbol. We should look into whether linker options can be added to specify
1552
+ // this on the command line.
1553
+ if !dependencies. executableTargets. isEmpty {
1554
+ // Creating a mapping from each .o file in each executable target to a corresponding modified .o file in
1555
+ // our product directory. This duplicates work if an executable is tested by more than one test product
1556
+ // but has the advantage of keeping the executable target clean unless it's being used by a test target.
1557
+ for target in dependencies. executableTargets. map ( { targetMap [ $0] ! } ) {
1558
+ for object in target. objects {
1559
+ // FIXME: Plenty of opportunity for collisions here — how is this handled for regular object files?
1560
+ let mainlessObject = buildProduct. tempsPath. appending ( components: " LinkedExecutableObjects " , " \( target. target. c99name) _ \( object. basename) " )
1561
+ buildProduct. executableObjects [ mainlessObject] = object
1562
+ buildProduct. objects. insert ( mainlessObject)
1563
+ }
1564
+ }
1565
+ // The symbol removal tool on some platforms requires a separate file list in the file system.
1566
+ try buildProduct. writeMainSymbolRemovalListFile ( fileSystem)
1567
+ }
1530
1568
1531
1569
// Write the link filelist file.
1532
1570
//
@@ -1541,6 +1579,7 @@ public class BuildPlan {
1541
1579
) throws -> (
1542
1580
dylibs: [ ResolvedProduct ] ,
1543
1581
staticTargets: [ ResolvedTarget ] ,
1582
+ executableTargets: [ ResolvedTarget ] ,
1544
1583
systemModules: [ ResolvedTarget ] ,
1545
1584
libraryBinaryPaths: Set < AbsolutePath >
1546
1585
) {
@@ -1568,6 +1607,7 @@ public class BuildPlan {
1568
1607
// Create empty arrays to collect our results.
1569
1608
var linkLibraries = [ ResolvedProduct] ( )
1570
1609
var staticTargets = [ ResolvedTarget] ( )
1610
+ var executableTargets = [ ResolvedTarget] ( )
1571
1611
var systemModules = [ ResolvedTarget] ( )
1572
1612
var libraryBinaryPaths : Set < AbsolutePath > = [ ]
1573
1613
@@ -1577,7 +1617,14 @@ public class BuildPlan {
1577
1617
switch target. type {
1578
1618
// Include executable and tests only if they're top level contents
1579
1619
// of the product. Otherwise they are just build time dependency.
1580
- case . executable, . test:
1620
+ case . executable:
1621
+ if product. targets. contains ( target) {
1622
+ staticTargets. append ( target)
1623
+ }
1624
+ else {
1625
+ executableTargets. append ( target)
1626
+ }
1627
+ case . test:
1581
1628
if product. targets. contains ( target) {
1582
1629
staticTargets. append ( target)
1583
1630
}
@@ -1612,7 +1659,7 @@ public class BuildPlan {
1612
1659
}
1613
1660
}
1614
1661
1615
- return ( linkLibraries, staticTargets, systemModules, libraryBinaryPaths)
1662
+ return ( linkLibraries, staticTargets, executableTargets , systemModules, libraryBinaryPaths)
1616
1663
}
1617
1664
1618
1665
/// Plan a Clang target.
@@ -1657,7 +1704,8 @@ public class BuildPlan {
1657
1704
// depends on.
1658
1705
for case . target( let dependency, _) in try swiftTarget. target. recursiveDependencies ( satisfying: buildEnvironment) {
1659
1706
switch dependency. underlyingTarget {
1660
- case let underlyingTarget as ClangTarget where underlyingTarget. type == . library:
1707
+ // FIXME: Adding .executable here is probably not right, but is needed in order to test Clang executables.
1708
+ case let underlyingTarget as ClangTarget where underlyingTarget. type == . library || underlyingTarget. type == . executable:
1661
1709
guard case let . clang( target) ? = targetMap [ dependency] else {
1662
1710
fatalError ( " unexpected clang target \( underlyingTarget) " )
1663
1711
}
0 commit comments