@@ -2,19 +2,19 @@ import Foundation
2
2
import PackagePlugin
3
3
4
4
@main
5
- struct Link : CommandPlugin {
5
+ struct LinkCommand : CommandPlugin {
6
6
static let pluginName : String = " link "
7
7
8
+ static var logPrefix : String {
9
+ " [ \( pluginName) ] "
10
+ }
11
+
8
12
func performCommand(
9
13
context: PluginContext ,
10
14
arguments: [ String ]
11
15
) async throws {
12
16
let clang = try context. tool ( named: " clang " )
13
- let clangURL = URL (
14
- fileURLWithPath: clang. path. string,
15
- isDirectory: false
16
- )
17
- Diagnostics . remark ( " [ \( Self . pluginName) ] clang: \( clang. path. string) " )
17
+ Diagnostics . remark ( " \( Self . logPrefix) clang: \( clang. path. string) " )
18
18
let commonClangArgs = [
19
19
" --target=armv6m-none-eabi " ,
20
20
" -mfloat-abi=soft " ,
@@ -23,21 +23,22 @@ struct Link: CommandPlugin {
23
23
" -nostdlib " ,
24
24
]
25
25
26
+ // TODO: Can we use the default build parameters the user is specifying on the command line (`-c release`, `--verbose`)?
26
27
let buildParams = PackageManager . BuildParameters (
27
28
configuration: . release,
28
29
logging: . concise
29
30
)
30
31
31
32
// Build RP2040 second-stage bootloader (boot2)
32
33
let boot2Product = try context. package . products ( named: [ " RP2040Boot2 " ] ) [ 0 ]
33
- Diagnostics . remark ( " [ \( Self . pluginName ) ] Building product '\( boot2Product. name) ' " )
34
+ Diagnostics . remark ( " \( Self . logPrefix ) Building product ' \( boot2Product. name) ' " )
34
35
let boot2BuildResult = try packageManager. build (
35
36
. product( boot2Product. name) ,
36
37
parameters: buildParams
37
38
)
38
39
guard boot2BuildResult. succeeded else {
39
40
print ( boot2BuildResult. logText)
40
- Diagnostics . error ( " [ \( Self . pluginName ) ] Building product '\( boot2Product. name) ' failed " )
41
+ Diagnostics . error ( " \( Self . logPrefix ) Building product ' \( boot2Product. name) ' failed " )
41
42
// TODO: Exit with error code
42
43
return
43
44
}
@@ -52,8 +53,8 @@ struct Link: CommandPlugin {
52
53
)
53
54
54
55
// Postprocess boot2
55
- Diagnostics . remark ( " [ \( Self . pluginName ) ] Boot2 processing" )
56
- //
56
+ Diagnostics . remark ( " \( Self . logPrefix ) Boot2 processing " )
57
+
57
58
// 1. Extract .o file from static library build product (.a)
58
59
// For some reason, if I try to link the .a file with Clang, it doesn't work.
59
60
// I need to pass in the .o file. What's the difference?
@@ -91,7 +92,6 @@ struct Link: CommandPlugin {
91
92
// 3. Convert boot2.elf to boot2.bin
92
93
let boot2Bin = intermediatesDir. appending ( subpath: " bs2_default.bin " )
93
94
let objcopy = try context. tool ( named: " objcopy " )
94
- let objcopyURL = URL ( fileURLWithPath: objcopy. path. string, isDirectory: false )
95
95
let objcopyArgs = [
96
96
" -Obinary " ,
97
97
boot2ELF. string,
@@ -122,14 +122,15 @@ struct Link: CommandPlugin {
122
122
try runProgram ( clang. path, arguments: boot2ObjClangArgs)
123
123
124
124
// Build the app
125
+ Diagnostics . remark ( " \( Self . logPrefix) Creating app executable " )
125
126
let appProduct = try context. package . products ( named: [ " App " ] ) [ 0 ]
126
127
let appBuildResult = try packageManager. build (
127
128
. product( appProduct. name) ,
128
129
parameters: buildParams
129
130
)
130
131
guard appBuildResult. succeeded else {
131
132
print ( appBuildResult. logText)
132
- Diagnostics . error ( " [ \( Self . pluginName ) ] Building product '\( appProduct. name) ' failed " )
133
+ Diagnostics . error ( " \( Self . logPrefix ) Building product ' \( appProduct. name) ' failed " )
133
134
// TODO: Exit with error code
134
135
return
135
136
}
@@ -161,52 +162,52 @@ struct Link: CommandPlugin {
161
162
162
163
print ( " Executable: \( linkedExecutable) " )
163
164
}
165
+ }
164
166
165
- /// Runs an external program and waits for it to finish.
166
- ///
167
- /// Emits SwiftPM diagnostics:
168
- /// - `remark` with the invocation (exectuable + arguments)
169
- /// - `error` on non-zero exit code
170
- ///
171
- /// - Throws:
172
- /// - When the program cannot be launched.
173
- /// - Throws `ExitCode` when the program completes with a non-zero status.
174
- private func runProgram(
175
- _ executable: Path ,
176
- arguments: [ String ] ,
177
- workingDirectory: Path ? = nil
178
- ) throws {
179
- // If the command is longer than approx. one line, format it neatly
180
- // on multiple lines for logging.
181
- let fullCommand = " \( executable. string) \( arguments. joined ( separator: " " ) ) "
182
- let logMessage = if fullCommand. count < 70 {
183
- fullCommand
184
- } else {
185
- """
186
- \( executable. string) \\
187
- \( arguments. joined ( separator: " \\ \n " ) )
188
- """
189
- }
190
- Diagnostics . remark ( " [ \( Self . pluginName) ] \( logMessage) " )
191
-
192
- let process = Process ( )
193
- process. executableURL = URL (
194
- fileURLWithPath: executable. string,
195
- isDirectory: false
167
+ /// Runs an external program and waits for it to finish.
168
+ ///
169
+ /// Emits SwiftPM diagnostics:
170
+ /// - `remark` with the invocation (exectuable + arguments)
171
+ /// - `error` on non-zero exit code
172
+ ///
173
+ /// - Throws:
174
+ /// - When the program cannot be launched.
175
+ /// - Throws `ExitCode` when the program completes with a non-zero status.
176
+ private func runProgram(
177
+ _ executable: Path ,
178
+ arguments: [ String ] ,
179
+ workingDirectory: Path ? = nil
180
+ ) throws {
181
+ // If the command is longer than approx. one line, format it neatly
182
+ // on multiple lines for logging.
183
+ let fullCommand = " \( executable. string) \( arguments. joined ( separator: " " ) ) "
184
+ let logMessage = if fullCommand. count < 70 {
185
+ fullCommand
186
+ } else {
187
+ """
188
+ \( executable. string) \\
189
+ \( arguments. joined ( separator: " \\ \n " ) )
190
+ """
191
+ }
192
+ Diagnostics . remark ( " \( LinkCommand . logPrefix) \( logMessage) " )
193
+
194
+ let process = Process ( )
195
+ process. executableURL = URL (
196
+ fileURLWithPath: executable. string,
197
+ isDirectory: false
198
+ )
199
+ process. arguments = arguments
200
+ if let workingDirectory {
201
+ process. currentDirectoryURL = URL (
202
+ fileURLWithPath: workingDirectory. string,
203
+ isDirectory: true
196
204
)
197
- process. arguments = arguments
198
- if let workingDirectory {
199
- process. currentDirectoryURL = URL (
200
- fileURLWithPath: workingDirectory. string,
201
- isDirectory: true
202
- )
203
- }
204
- try process. run ( )
205
- process. waitUntilExit ( )
206
- guard process. terminationStatus == 0 else {
207
- Diagnostics . error ( " [ \( Self . pluginName) ] \( executable. lastComponent) exited with code \( process. terminationStatus) " )
208
- throw ExitCode ( process. terminationStatus)
209
- }
205
+ }
206
+ try process. run ( )
207
+ process. waitUntilExit ( )
208
+ guard process. terminationStatus == 0 else {
209
+ Diagnostics . error ( " \( LinkCommand . logPrefix) \( executable. lastComponent) exited with code \( process. terminationStatus) " )
210
+ throw ExitCode ( process. terminationStatus)
210
211
}
211
212
}
212
213
0 commit comments