@@ -121,6 +121,18 @@ extension BuildParameters {
121
121
return args
122
122
}
123
123
124
+ /// Computes the linker flags to use in order to rename a module-named main function to 'main' for the target platform, or nil if the linker doesn't support it for the platform.
125
+ fileprivate func linkerFlagsForRenamingMainFunction( of target: ResolvedTarget ) -> [ String ] ? {
126
+ var args : [ String ] = [ ]
127
+ if self . triple. isDarwin ( ) {
128
+ args = [ " -alias " , " _ \( target. c99name) _main " , " _main " ]
129
+ }
130
+ else if self . triple. isLinux ( ) {
131
+ args = [ " --defsym " , " main= \( target. c99name) _main " ]
132
+ }
133
+ return args. flatMap { [ " -Xlinker " , $0] }
134
+ }
135
+
124
136
/// Returns the scoped view of build settings for a given target.
125
137
fileprivate func createScope( for target: ResolvedTarget ) -> BuildSettings . Scope {
126
138
return BuildSettings . Scope ( target. underlyingTarget. buildSettings, environment: buildEnvironment)
@@ -195,6 +207,11 @@ public final class ClangTargetBuildDescription {
195
207
public var clangTarget : ClangTarget {
196
208
return target. underlyingTarget as! ClangTarget
197
209
}
210
+
211
+ /// The tools version of the package that declared the target. This can
212
+ /// can be used to conditionalize semantically significant changes in how
213
+ /// a target is built.
214
+ public let toolsVersion : ToolsVersion
198
215
199
216
/// The build parameters.
200
217
let buildParameters : BuildParameters
@@ -249,11 +266,12 @@ public final class ClangTargetBuildDescription {
249
266
}
250
267
251
268
/// Create a new target description with target and build parameters.
252
- init ( target: ResolvedTarget , buildParameters: BuildParameters , fileSystem: FileSystem = localFileSystem, diagnostics: DiagnosticsEngine ) throws {
269
+ init ( target: ResolvedTarget , toolsVersion : ToolsVersion , buildParameters: BuildParameters , fileSystem: FileSystem = localFileSystem, diagnostics: DiagnosticsEngine ) throws {
253
270
assert ( target. underlyingTarget is ClangTarget , " underlying target type mismatch \( target) " )
254
271
self . fileSystem = fileSystem
255
272
self . diagnostics = diagnostics
256
273
self . target = target
274
+ self . toolsVersion = toolsVersion
257
275
self . buildParameters = buildParameters
258
276
self . tempsPath = buildParameters. buildPath. appending ( component: target. c99name + " .build " )
259
277
self . derivedSources = Sources ( paths: [ ] , root: tempsPath. appending ( component: " DerivedSources " ) )
@@ -472,6 +490,11 @@ public final class SwiftTargetBuildDescription {
472
490
/// The target described by this target.
473
491
public let target : ResolvedTarget
474
492
493
+ /// The tools version of the package that declared the target. This can
494
+ /// can be used to conditionalize semantically significant changes in how
495
+ /// a target is built.
496
+ public let toolsVersion : ToolsVersion
497
+
475
498
/// The build parameters.
476
499
let buildParameters : BuildParameters
477
500
@@ -504,7 +527,9 @@ public final class SwiftTargetBuildDescription {
504
527
505
528
/// The path to the swiftmodule file after compilation.
506
529
var moduleOutputPath : AbsolutePath {
507
- let dirPath = ( target. type == . executable) ? tempsPath : buildParameters. buildPath
530
+ // If we're an executable and we're not allowing test targets to link against us, we hide the module.
531
+ let allowLinkingAgainstExecutables = ( buildParameters. triple. isDarwin ( ) || buildParameters. triple. isLinux ( ) ) && toolsVersion >= . vNext
532
+ let dirPath = ( target. type == . executable && !allowLinkingAgainstExecutables) ? tempsPath : buildParameters. buildPath
508
533
return dirPath. appending ( component: target. c99name + " .swiftmodule " )
509
534
}
510
535
@@ -555,6 +580,7 @@ public final class SwiftTargetBuildDescription {
555
580
/// Create a new target description with target and build parameters.
556
581
init (
557
582
target: ResolvedTarget ,
583
+ toolsVersion: ToolsVersion ,
558
584
buildParameters: BuildParameters ,
559
585
pluginInvocationResults: [ PluginInvocationResult ] = [ ] ,
560
586
prebuildCommandResults: [ PrebuildCommandResult ] = [ ] ,
@@ -564,6 +590,7 @@ public final class SwiftTargetBuildDescription {
564
590
) throws {
565
591
assert ( target. underlyingTarget is SwiftTarget , " underlying target type mismatch \( target) " )
566
592
self . target = target
593
+ self . toolsVersion = toolsVersion
567
594
self . buildParameters = buildParameters
568
595
// Unless mentioned explicitly, use the target type to determine if this is a test target.
569
596
self . isTestTarget = isTestTarget ?? ( target. type == . test)
@@ -677,6 +704,24 @@ public final class SwiftTargetBuildDescription {
677
704
args += buildParameters. sanitizers. compileSwiftFlags ( )
678
705
args += [ " -parseable-output " ]
679
706
707
+ // If we're compiling the main module of an executable other than the one that
708
+ // implements a test suite, and if the package tools version indicates that we
709
+ // should, we rename the `_main` entry point to `_<modulename>_main`.
710
+ //
711
+ // This will allow tests to link against the module without any conflicts. And
712
+ // when we link the executable, we will ask the linker to rename the entry point
713
+ // symbol to just `_main` again (or if the linker doesn't support it, we'll
714
+ // generate a source containing a redirect).
715
+ if target. type == . executable && !isTestTarget && toolsVersion >= . vNext {
716
+ // We only do this if the linker supports it, as indicated by whether we
717
+ // can construct the linker flags. In the future we will use a generated
718
+ // code stub for the cases in which the linker doesn't support it, so that
719
+ // we can rename the symbol unconditionally.
720
+ if buildParameters. linkerFlagsForRenamingMainFunction ( of: target) != nil {
721
+ args += [ " -Xfrontend " , " -entry-point-function-name " , " -Xfrontend " , " \( target. c99name) _main " ]
722
+ }
723
+ }
724
+
680
725
// Only add the build path to the framework search path if there are binary frameworks to link against.
681
726
if !libraryBinaryPaths. isEmpty {
682
727
args += [ " -F " , buildParameters. buildPath. pathString]
@@ -1018,6 +1063,11 @@ public final class ProductBuildDescription {
1018
1063
/// The reference to the product.
1019
1064
public let product : ResolvedProduct
1020
1065
1066
+ /// The tools version of the package that declared the product. This can
1067
+ /// can be used to conditionalize semantically significant changes in how
1068
+ /// a target is built.
1069
+ public let toolsVersion : ToolsVersion
1070
+
1021
1071
/// The build parameters.
1022
1072
let buildParameters : BuildParameters
1023
1073
@@ -1029,7 +1079,7 @@ public final class ProductBuildDescription {
1029
1079
return buildParameters. binaryPath ( for: product)
1030
1080
}
1031
1081
1032
- /// The objects in this product.
1082
+ /// All object files to link into this product.
1033
1083
///
1034
1084
// Computed during build planning.
1035
1085
public fileprivate( set) var objects = SortedArray < AbsolutePath > ( )
@@ -1067,9 +1117,10 @@ public final class ProductBuildDescription {
1067
1117
let diagnostics : DiagnosticsEngine
1068
1118
1069
1119
/// Create a build description for a product.
1070
- init ( product: ResolvedProduct , buildParameters: BuildParameters , fs: FileSystem , diagnostics: DiagnosticsEngine ) {
1120
+ init ( product: ResolvedProduct , toolsVersion : ToolsVersion , buildParameters: BuildParameters , fs: FileSystem , diagnostics: DiagnosticsEngine ) {
1071
1121
assert ( product. type != . library( . automatic) , " Automatic type libraries should not be described. " )
1072
1122
self . product = product
1123
+ self . toolsVersion = toolsVersion
1073
1124
self . buildParameters = buildParameters
1074
1125
self . fs = fs
1075
1126
self . diagnostics = diagnostics
@@ -1148,6 +1199,20 @@ public final class ProductBuildDescription {
1148
1199
}
1149
1200
}
1150
1201
args += [ " -emit-executable " ]
1202
+
1203
+ // If we're linking an executable whose main module is implemented in Swift,
1204
+ // we rename the `_<modulename>_main` entry point symbol to `_main` again.
1205
+ // This is because executable modules implemented in Swift are compiled with
1206
+ // a main symbol named that way to allow tests to link against it without
1207
+ // conflicts. If we're using a linker that doesn't support symbol renaming,
1208
+ // we will instead have generated a source file containing the redirect.
1209
+ // Support for linking tests againsts executables is conditional on the tools
1210
+ // version of the package that defines the executable product.
1211
+ if product. executableModule. underlyingTarget is SwiftTarget , toolsVersion >= . vNext {
1212
+ if let flags = buildParameters. linkerFlagsForRenamingMainFunction ( of: product. executableModule) {
1213
+ args += flags
1214
+ }
1215
+ }
1151
1216
case . plugin:
1152
1217
throw InternalError ( " unexpectedly asked to generate linker arguments for a plugin product " )
1153
1218
}
@@ -1327,9 +1392,11 @@ public class BuildPlan {
1327
1392
// if test manifest exists, prefer that over test detection,
1328
1393
// this is designed as an escape hatch when test discovery is not appropriate
1329
1394
// and for backwards compatibility for projects that have existing test manifests (LinuxMain.swift)
1395
+ let toolsVersion = graph. package ( for: testProduct) ? . manifest. toolsVersion ?? . vNext
1330
1396
if let testManifestTarget = testProduct. testManifestTarget, !generate {
1331
1397
let desc = try SwiftTargetBuildDescription (
1332
1398
target: testManifestTarget,
1399
+ toolsVersion: toolsVersion,
1333
1400
buildParameters: buildParameters,
1334
1401
isTestTarget: true
1335
1402
)
@@ -1361,6 +1428,7 @@ public class BuildPlan {
1361
1428
1362
1429
let target = try SwiftTargetBuildDescription (
1363
1430
target: testManifestTarget,
1431
+ toolsVersion: toolsVersion,
1364
1432
buildParameters: buildParameters,
1365
1433
isTestTarget: true ,
1366
1434
testDiscoveryTarget: true
@@ -1403,18 +1471,24 @@ public class BuildPlan {
1403
1471
}
1404
1472
}
1405
1473
}
1474
+
1475
+ // Determine the appropriate tools version to use for the target.
1476
+ // This can affect what flags to pass and other semantics.
1477
+ let toolsVersion = graph. package ( for: target) ? . manifest. toolsVersion ?? . vNext
1406
1478
1407
1479
switch target. underlyingTarget {
1408
1480
case is SwiftTarget :
1409
1481
targetMap [ target] = try . swift( SwiftTargetBuildDescription (
1410
1482
target: target,
1483
+ toolsVersion: toolsVersion,
1411
1484
buildParameters: buildParameters,
1412
1485
pluginInvocationResults: pluginInvocationResults [ target] ?? [ ] ,
1413
1486
prebuildCommandResults: prebuildCommandResults [ target] ?? [ ] ,
1414
1487
fs: fileSystem) )
1415
1488
case is ClangTarget :
1416
1489
targetMap [ target] = try . clang( ClangTargetBuildDescription (
1417
1490
target: target,
1491
+ toolsVersion: toolsVersion,
1418
1492
buildParameters: buildParameters,
1419
1493
fileSystem: fileSystem,
1420
1494
diagnostics: diagnostics) )
@@ -1448,8 +1522,14 @@ public class BuildPlan {
1448
1522
// Create product description for each product we have in the package graph except
1449
1523
// for automatic libraries and plugins, because they don't produce any output.
1450
1524
for product in graph. allProducts where product. type != . library( . automatic) && product. type != . plugin {
1525
+
1526
+ // Determine the appropriate tools version to use for the product.
1527
+ // This can affect what flags to pass and other semantics.
1528
+ let toolsVersion = graph. package ( for: product) ? . manifest. toolsVersion ?? . vNext
1451
1529
productMap [ product] = ProductBuildDescription (
1452
- product: product, buildParameters: buildParameters,
1530
+ product: product,
1531
+ toolsVersion: toolsVersion,
1532
+ buildParameters: buildParameters,
1453
1533
fs: fileSystem,
1454
1534
diagnostics: diagnostics
1455
1535
)
@@ -1635,9 +1715,21 @@ public class BuildPlan {
1635
1715
switch dependency {
1636
1716
case . target( let target, _) :
1637
1717
switch target. type {
1638
- // Include executable and tests only if they're top level contents
1639
- // of the product. Otherwise they are just build time dependency.
1640
- case . executable, . test:
1718
+ // Executable target have historically only been included if they are directly in the product's
1719
+ // target list. Otherwise they have always been just build-time dependencies.
1720
+ // In tool version .vNext or greater, we also include executable modules implemented in Swift in
1721
+ // any test products... this is to allow testing of executables. Note that they are also still
1722
+ // built as separate products that the test can invoke as subprocesses.
1723
+ case . executable:
1724
+ if product. targets. contains ( target) {
1725
+ staticTargets. append ( target)
1726
+ } else if product. type == . test && target. underlyingTarget is SwiftTarget {
1727
+ if let toolsVersion = graph. package ( for: product) ? . manifest. toolsVersion, toolsVersion >= . vNext {
1728
+ staticTargets. append ( target)
1729
+ }
1730
+ }
1731
+ // Test targets should be included only if they are directly in the product's target list.
1732
+ case . test:
1641
1733
if product. targets. contains ( target) {
1642
1734
staticTargets. append ( target)
1643
1735
}
0 commit comments