@@ -121,6 +121,18 @@ extension BuildParameters {
121121 return args
122122 }
123123
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+
124136 /// Returns the scoped view of build settings for a given target.
125137 fileprivate func createScope( for target: ResolvedTarget ) -> BuildSettings . Scope {
126138 return BuildSettings . Scope ( target. underlyingTarget. buildSettings, environment: buildEnvironment)
@@ -195,6 +207,11 @@ public final class ClangTargetBuildDescription {
195207 public var clangTarget : ClangTarget {
196208 return target. underlyingTarget as! ClangTarget
197209 }
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
198215
199216 /// The build parameters.
200217 let buildParameters : BuildParameters
@@ -249,11 +266,12 @@ public final class ClangTargetBuildDescription {
249266 }
250267
251268 /// 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 {
253270 assert ( target. underlyingTarget is ClangTarget , " underlying target type mismatch \( target) " )
254271 self . fileSystem = fileSystem
255272 self . diagnostics = diagnostics
256273 self . target = target
274+ self . toolsVersion = toolsVersion
257275 self . buildParameters = buildParameters
258276 self . tempsPath = buildParameters. buildPath. appending ( component: target. c99name + " .build " )
259277 self . derivedSources = Sources ( paths: [ ] , root: tempsPath. appending ( component: " DerivedSources " ) )
@@ -472,6 +490,11 @@ public final class SwiftTargetBuildDescription {
472490 /// The target described by this target.
473491 public let target : ResolvedTarget
474492
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+
475498 /// The build parameters.
476499 let buildParameters : BuildParameters
477500
@@ -504,7 +527,9 @@ public final class SwiftTargetBuildDescription {
504527
505528 /// The path to the swiftmodule file after compilation.
506529 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
508533 return dirPath. appending ( component: target. c99name + " .swiftmodule " )
509534 }
510535
@@ -555,6 +580,7 @@ public final class SwiftTargetBuildDescription {
555580 /// Create a new target description with target and build parameters.
556581 init (
557582 target: ResolvedTarget ,
583+ toolsVersion: ToolsVersion ,
558584 buildParameters: BuildParameters ,
559585 pluginInvocationResults: [ PluginInvocationResult ] = [ ] ,
560586 prebuildCommandResults: [ PrebuildCommandResult ] = [ ] ,
@@ -564,6 +590,7 @@ public final class SwiftTargetBuildDescription {
564590 ) throws {
565591 assert ( target. underlyingTarget is SwiftTarget , " underlying target type mismatch \( target) " )
566592 self . target = target
593+ self . toolsVersion = toolsVersion
567594 self . buildParameters = buildParameters
568595 // Unless mentioned explicitly, use the target type to determine if this is a test target.
569596 self . isTestTarget = isTestTarget ?? ( target. type == . test)
@@ -677,6 +704,24 @@ public final class SwiftTargetBuildDescription {
677704 args += buildParameters. sanitizers. compileSwiftFlags ( )
678705 args += [ " -parseable-output " ]
679706
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+
680725 // Only add the build path to the framework search path if there are binary frameworks to link against.
681726 if !libraryBinaryPaths. isEmpty {
682727 args += [ " -F " , buildParameters. buildPath. pathString]
@@ -1018,6 +1063,11 @@ public final class ProductBuildDescription {
10181063 /// The reference to the product.
10191064 public let product : ResolvedProduct
10201065
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+
10211071 /// The build parameters.
10221072 let buildParameters : BuildParameters
10231073
@@ -1029,7 +1079,7 @@ public final class ProductBuildDescription {
10291079 return buildParameters. binaryPath ( for: product)
10301080 }
10311081
1032- /// The objects in this product.
1082+ /// All object files to link into this product.
10331083 ///
10341084 // Computed during build planning.
10351085 public fileprivate( set) var objects = SortedArray < AbsolutePath > ( )
@@ -1067,9 +1117,10 @@ public final class ProductBuildDescription {
10671117 let diagnostics : DiagnosticsEngine
10681118
10691119 /// 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 ) {
10711121 assert ( product. type != . library( . automatic) , " Automatic type libraries should not be described. " )
10721122 self . product = product
1123+ self . toolsVersion = toolsVersion
10731124 self . buildParameters = buildParameters
10741125 self . fs = fs
10751126 self . diagnostics = diagnostics
@@ -1148,6 +1199,20 @@ public final class ProductBuildDescription {
11481199 }
11491200 }
11501201 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+ }
11511216 case . plugin:
11521217 throw InternalError ( " unexpectedly asked to generate linker arguments for a plugin product " )
11531218 }
@@ -1327,9 +1392,11 @@ public class BuildPlan {
13271392 // if test manifest exists, prefer that over test detection,
13281393 // this is designed as an escape hatch when test discovery is not appropriate
13291394 // and for backwards compatibility for projects that have existing test manifests (LinuxMain.swift)
1395+ let toolsVersion = graph. package ( for: testProduct) ? . manifest. toolsVersion ?? . vNext
13301396 if let testManifestTarget = testProduct. testManifestTarget, !generate {
13311397 let desc = try SwiftTargetBuildDescription (
13321398 target: testManifestTarget,
1399+ toolsVersion: toolsVersion,
13331400 buildParameters: buildParameters,
13341401 isTestTarget: true
13351402 )
@@ -1361,6 +1428,7 @@ public class BuildPlan {
13611428
13621429 let target = try SwiftTargetBuildDescription (
13631430 target: testManifestTarget,
1431+ toolsVersion: toolsVersion,
13641432 buildParameters: buildParameters,
13651433 isTestTarget: true ,
13661434 testDiscoveryTarget: true
@@ -1403,18 +1471,24 @@ public class BuildPlan {
14031471 }
14041472 }
14051473 }
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
14061478
14071479 switch target. underlyingTarget {
14081480 case is SwiftTarget :
14091481 targetMap [ target] = try . swift( SwiftTargetBuildDescription (
14101482 target: target,
1483+ toolsVersion: toolsVersion,
14111484 buildParameters: buildParameters,
14121485 pluginInvocationResults: pluginInvocationResults [ target] ?? [ ] ,
14131486 prebuildCommandResults: prebuildCommandResults [ target] ?? [ ] ,
14141487 fs: fileSystem) )
14151488 case is ClangTarget :
14161489 targetMap [ target] = try . clang( ClangTargetBuildDescription (
14171490 target: target,
1491+ toolsVersion: toolsVersion,
14181492 buildParameters: buildParameters,
14191493 fileSystem: fileSystem,
14201494 diagnostics: diagnostics) )
@@ -1448,8 +1522,14 @@ public class BuildPlan {
14481522 // Create product description for each product we have in the package graph except
14491523 // for automatic libraries and plugins, because they don't produce any output.
14501524 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
14511529 productMap [ product] = ProductBuildDescription (
1452- product: product, buildParameters: buildParameters,
1530+ product: product,
1531+ toolsVersion: toolsVersion,
1532+ buildParameters: buildParameters,
14531533 fs: fileSystem,
14541534 diagnostics: diagnostics
14551535 )
@@ -1635,9 +1715,21 @@ public class BuildPlan {
16351715 switch dependency {
16361716 case . target( let target, _) :
16371717 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:
16411733 if product. targets. contains ( target) {
16421734 staticTargets. append ( target)
16431735 }
0 commit comments