@@ -340,8 +340,17 @@ func dirsToModuleRootAbs(maindir, modroot string) []string {
340340 return dirs
341341}
342342
343+ // validateOutputFormat checks if the output file extension matches the expected format
344+ func validateOutputFormat (outpath , expectedExt string ) error {
345+ actualExt := filepath .Ext (outpath )
346+ if actualExt != expectedExt {
347+ return fmt .Errorf ("output format %s does not match target format %s" , actualExt , expectedExt )
348+ }
349+ return nil
350+ }
351+
343352// Flash builds and flashes the built binary to the given serial port.
344- func Flash (pkgName , port string , options * compileopts.Options ) error {
353+ func Flash (pkgName , port , outpath string , options * compileopts.Options ) error {
345354 config , err := builder .NewConfig (options )
346355 if err != nil {
347356 return err
@@ -390,13 +399,24 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
390399 if ! options .Work {
391400 defer os .RemoveAll (tmpdir )
392401 }
393-
402+ // Validate output format before building
403+ if outpath != "" {
404+ if err := validateOutputFormat (outpath , fileExt ); err != nil {
405+ return err
406+ }
407+ }
394408 // Build the binary.
395409 result , err := builder .Build (pkgName , fileExt , tmpdir , config )
396410 if err != nil {
397411 return err
398412 }
399413
414+ // Save output file if specified (after build, before flashing)
415+ if outpath != "" {
416+ if err := copyFile (result .Binary , outpath ); err != nil {
417+ return fmt .Errorf ("failed to save output file: %v" , err )
418+ }
419+ }
400420 // do we need port reset to put MCU into bootloader mode?
401421 if config .Target .PortReset == "true" && flashMethod != "openocd" {
402422 port , err := getDefaultPort (port , config .Target .SerialPort )
@@ -1298,6 +1318,11 @@ extension at all.`
12981318 (https://tinygo.org/docs/reference/microcontrollers/).
12991319 Examples: "arduino-nano", "d1mini", "xiao".
13001320
1321+ -o={filename}:
1322+ Save the built binary to the specified output file. The file
1323+ format must match the target's expected format (e.g., .hex,
1324+ .uf2). Both flashing and saving will be performed.
1325+
13011326 -monitor:
13021327 Start the serial monitor (see below) immediately after
13031328 flashing. However, some microcontrollers need a split second
@@ -1628,7 +1653,7 @@ func main() {
16281653 flag .BoolVar (& flagTest , "test" , false , "supply -test flag to go list" )
16291654 }
16301655 var outpath string
1631- if command == "help" || command == "build" || command == "test" {
1656+ if command == "help" || command == "build" || command == "test" || command == "flash" {
16321657 flag .StringVar (& outpath , "o" , "" , "output filename" )
16331658 }
16341659
@@ -1779,7 +1804,7 @@ func main() {
17791804 case "flash" , "gdb" , "lldb" :
17801805 pkgName := filepath .ToSlash (flag .Arg (0 ))
17811806 if command == "flash" {
1782- err := Flash (pkgName , * port , options )
1807+ err := Flash (pkgName , * port , outpath , options )
17831808 printBuildOutput (err , * flagJSON )
17841809 } else {
17851810 if ! options .Debug {
0 commit comments