@@ -8,6 +8,7 @@ public class Script {
88 let input : Input
99 let deps : [ ImportSpecification ]
1010 let args : [ String ]
11+ let mainStyle : ExecutableTargetMainStyle
1112
1213 private let inputPathHash : String ?
1314
@@ -30,18 +31,22 @@ public class Script {
3031 }
3132
3233 public var mainSwift : Path {
33- return buildDirectory/ " main.swift "
34+ switch mainStyle {
35+ case . mainAttribute: return buildDirectory/ " Root.swift "
36+ case . topLevelCode: return buildDirectory/ " main.swift "
37+ }
3438 }
3539
3640 public enum Input {
3741 case path( Path )
3842 case string( name: String , content: String )
3943 }
4044
41- public init ( for: Input , dependencies: [ ImportSpecification ] , arguments: [ String ] = [ ] ) {
45+ public init ( for: Input , style : ExecutableTargetMainStyle , dependencies: [ ImportSpecification ] , arguments: [ String ] = [ ] ) {
4246 input = `for`
4347 deps = dependencies
4448 args = arguments
49+ mainStyle = style
4550
4651 // cache hash if appropriate since accessed often and involves work
4752 if case let Input . path( path) = input {
@@ -68,20 +73,30 @@ public class Script {
6873 public func write( ) throws {
6974 //NOTE we only support Swift >= 4.2 basically
7075 //TODO dependency module names might not correspond the products that packages export, must parse `swift package dump-package` output
71-
7276 if depsCache != deps {
7377 // this check because SwiftPM has to reparse the manifest if we rewrite it
7478 // this is noticably slow, so avoid it if possible
75-
7679 var macOS : String {
7780 let version = ProcessInfo . processInfo. operatingSystemVersion
7881 return " .macOS( \" \( version. majorVersion) . \( version. minorVersion) \" ) "
7982 }
80-
8183 try buildDirectory. mkdir ( . p)
84+ let targetDefinition : String
85+ let swiftToolsVersion : String
86+ let sourceFile : String
87+ switch mainStyle {
88+ case . mainAttribute:
89+ targetDefinition = " executableTarget "
90+ swiftToolsVersion = " 5.5 "
91+ sourceFile = " Root.swift "
92+ case . topLevelCode:
93+ targetDefinition = " target "
94+ swiftToolsVersion = " 5.1 "
95+ sourceFile = " main.swift "
96+ }
8297 // we are using tools version 5.1 while we still can as >= 5.3 makes specifying deps significantly more complex
8398 try """
84- // swift-tools-version:5.1
99+ // swift-tools-version: \( swiftToolsVersion )
85100 import PackageDescription
86101
87102 let pkg = Package(name: " \( name) " )
@@ -93,12 +108,12 @@ public class Script {
93108 \( deps. packageLines)
94109 ]
95110 pkg.targets = [
96- .target (
111+ . \( targetDefinition ) (
97112 name: " \( name) " ,
98113 dependencies: [ \( deps. mainTargetDependencies) ],
99114 path: " . " ,
100115 exclude: [ " deps.json " ],
101- sources: [ " main.swift " ]
116+ sources: [ " \( sourceFile ) " ]
102117 )
103118 ]
104119
@@ -109,19 +124,26 @@ public class Script {
109124 #endif
110125
111126 """ . write ( to: manifestPath)
112-
113127 try JSONEncoder ( ) . encode ( deps) . write ( to: depsCachePath)
114128 }
115-
116129 switch input {
117130 case . path( let userPath) :
118131 func mklink( ) throws { try userPath. symlink ( as: mainSwift) }
119-
120- if let linkdst = try ? mainSwift. readlink ( ) , linkdst != userPath {
121- try mainSwift. delete ( )
122- try mklink ( )
123- } else if !mainSwift. exists {
124- try mklink ( )
132+ switch mainStyle {
133+ case . mainAttribute:
134+ try mainSwift. delete ( )
135+ let reader = try StreamReader ( path: userPath)
136+ let source = reader. compactMap { line in
137+ line. contains ( " #! " ) ? . none : line
138+ } . joined ( separator: " \n " )
139+ try source. write ( to: mainSwift)
140+ case . topLevelCode:
141+ if let linkdst = try ? mainSwift. readlink ( ) , linkdst != userPath {
142+ try mainSwift. delete ( )
143+ try mklink ( )
144+ } else if !mainSwift. exists {
145+ try mklink ( )
146+ }
125147 }
126148 case . string( _, let contents) :
127149 if let currentContents = try ? String ( contentsOf: mainSwift) , currentContents == contents { break }
@@ -178,12 +200,12 @@ public class Script {
178200 public func run( ) throws -> Never {
179201 if scriptChanged {
180202 try write ( )
181-
182203 // first arg has to be same as executable path
183204 let task = Process ( )
184205 task. launchPath = Path . swift. string
185206 task. arguments = [ " build " , " -Xswiftc " , " -suppress-warnings " ]
186207 task. currentDirectoryPath = buildDirectory. string
208+
187209 #if !os(Linux)
188210 task. standardOutput = task. standardError
189211 #else
0 commit comments