@@ -18,7 +18,10 @@ package builder
1818import (
1919 "bytes"
2020 "fmt"
21+ "math"
2122 "regexp"
23+ "strconv"
24+ "strings"
2225
2326 "github.com/arduino/arduino-cli/arduino/builder/compilation"
2427 "github.com/arduino/arduino-cli/arduino/builder/cpp"
@@ -29,6 +32,7 @@ import (
2932 f "github.com/arduino/arduino-cli/internal/algorithms"
3033 rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3134 "github.com/arduino/go-paths-helper"
35+ "github.com/marcinbor85/gohex"
3236
3337 "github.com/pkg/errors"
3438)
@@ -221,3 +225,128 @@ func (b *Builder) BuildSketch(
221225
222226 return sketchObjectFiles , nil
223227}
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