@@ -18,7 +18,10 @@ package builder
18
18
import (
19
19
"bytes"
20
20
"fmt"
21
+ "math"
21
22
"regexp"
23
+ "strconv"
24
+ "strings"
22
25
23
26
"github.com/arduino/arduino-cli/arduino/builder/compilation"
24
27
"github.com/arduino/arduino-cli/arduino/builder/cpp"
@@ -29,6 +32,7 @@ import (
29
32
f "github.com/arduino/arduino-cli/internal/algorithms"
30
33
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
31
34
"github.com/arduino/go-paths-helper"
35
+ "github.com/marcinbor85/gohex"
32
36
33
37
"github.com/pkg/errors"
34
38
)
@@ -221,3 +225,128 @@ func (b *Builder) BuildSketch(
221
225
222
226
return sketchObjectFiles , nil
223
227
}
228
+
229
+ // MergeSketchWithBootloader fixdoc
230
+ func (b * Builder ) MergeSketchWithBootloader (onlyUpdateCompilationDatabase bool ) error {
231
+ if onlyUpdateCompilationDatabase {
232
+ return nil
233
+ }
234
+
235
+ if ! b .buildProperties .ContainsKey ("bootloader.noblink" ) && ! b .buildProperties .ContainsKey ("bootloader.file" ) {
236
+ return nil
237
+ }
238
+
239
+ sketchFileName := b .sketch .MainFile .Base ()
240
+ sketchInBuildPath := b .buildPath .Join (sketchFileName + ".hex" )
241
+ sketchInSubfolder := b .buildPath .Join ("sketch" , sketchFileName + ".hex" )
242
+
243
+ var builtSketchPath * paths.Path
244
+ if sketchInBuildPath .Exist () {
245
+ builtSketchPath = sketchInBuildPath
246
+ } else if sketchInSubfolder .Exist () {
247
+ builtSketchPath = sketchInSubfolder
248
+ } else {
249
+ return nil
250
+ }
251
+
252
+ bootloader := ""
253
+ if bootloaderNoBlink , ok := b .buildProperties .GetOk ("bootloader.noblink" ); ok {
254
+ bootloader = bootloaderNoBlink
255
+ } else {
256
+ bootloader = b .buildProperties .Get ("bootloader.file" )
257
+ }
258
+ bootloader = b .buildProperties .ExpandPropsInString (bootloader )
259
+
260
+ bootloaderPath := b .buildProperties .GetPath ("runtime.platform.path" ).Join ("bootloaders" , bootloader )
261
+ if bootloaderPath .NotExist () {
262
+ if b .logger .Verbose () {
263
+ b .logger .Warn (tr ("Bootloader file specified but missing: %[1]s" , bootloaderPath ))
264
+ }
265
+ return nil
266
+ }
267
+
268
+ mergedSketchPath := builtSketchPath .Parent ().Join (sketchFileName + ".with_bootloader.hex" )
269
+
270
+ // Ignore merger errors for the first iteration
271
+ maximumBinSize := 16000000
272
+ if uploadMaxSize , ok := b .buildProperties .GetOk ("upload.maximum_size" ); ok {
273
+ maximumBinSize , _ = strconv .Atoi (uploadMaxSize )
274
+ maximumBinSize *= 2
275
+ }
276
+ err := merge (builtSketchPath , bootloaderPath , mergedSketchPath , maximumBinSize )
277
+ if err != nil && b .logger .Verbose () {
278
+ b .logger .Info (err .Error ())
279
+ }
280
+
281
+ return nil
282
+ }
283
+
284
+ func merge (builtSketchPath , bootloaderPath , mergedSketchPath * paths.Path , maximumBinSize int ) error {
285
+ if bootloaderPath .Ext () == ".bin" {
286
+ bootloaderPath = paths .New (strings .TrimSuffix (bootloaderPath .String (), ".bin" ) + ".hex" )
287
+ }
288
+
289
+ memBoot := gohex .NewMemory ()
290
+ if bootFile , err := bootloaderPath .Open (); err == nil {
291
+ defer bootFile .Close ()
292
+ if err := memBoot .ParseIntelHex (bootFile ); err != nil {
293
+ return errors .New (bootFile .Name () + " " + err .Error ())
294
+ }
295
+ } else {
296
+ return err
297
+ }
298
+
299
+ memSketch := gohex .NewMemory ()
300
+ if buildFile , err := builtSketchPath .Open (); err == nil {
301
+ defer buildFile .Close ()
302
+ if err := memSketch .ParseIntelHex (buildFile ); err != nil {
303
+ return errors .New (buildFile .Name () + " " + err .Error ())
304
+ }
305
+ } else {
306
+ return err
307
+ }
308
+
309
+ memMerged := gohex .NewMemory ()
310
+ initialAddress := uint32 (math .MaxUint32 )
311
+ lastAddress := uint32 (0 )
312
+
313
+ for _ , segment := range memBoot .GetDataSegments () {
314
+ if err := memMerged .AddBinary (segment .Address , segment .Data ); err != nil {
315
+ continue
316
+ }
317
+ if segment .Address < initialAddress {
318
+ initialAddress = segment .Address
319
+ }
320
+ if segment .Address + uint32 (len (segment .Data )) > lastAddress {
321
+ lastAddress = segment .Address + uint32 (len (segment .Data ))
322
+ }
323
+ }
324
+ for _ , segment := range memSketch .GetDataSegments () {
325
+ if err := memMerged .AddBinary (segment .Address , segment .Data ); err != nil {
326
+ continue
327
+ }
328
+ if segment .Address < initialAddress {
329
+ initialAddress = segment .Address
330
+ }
331
+ if segment .Address + uint32 (len (segment .Data )) > lastAddress {
332
+ lastAddress = segment .Address + uint32 (len (segment .Data ))
333
+ }
334
+ }
335
+
336
+ if mergeFile , err := mergedSketchPath .Create (); err == nil {
337
+ defer mergeFile .Close ()
338
+ memMerged .DumpIntelHex (mergeFile , 16 )
339
+ } else {
340
+ return err
341
+ }
342
+
343
+ // Write out a .bin if the addresses doesn't go too far away from origin
344
+ // (and consequently produce a very large bin)
345
+ size := lastAddress - initialAddress
346
+ if size > uint32 (maximumBinSize ) {
347
+ return nil
348
+ }
349
+ mergedSketchPathBin := paths .New (strings .TrimSuffix (mergedSketchPath .String (), ".hex" ) + ".bin" )
350
+ data := memMerged .ToBinary (initialAddress , size , 0xFF )
351
+ return mergedSketchPathBin .WriteFile (data )
352
+ }
0 commit comments