|
16 | 16 | package commands
|
17 | 17 |
|
18 | 18 | import (
|
| 19 | + "archive/zip" |
19 | 20 | "context"
|
20 | 21 | "errors"
|
21 | 22 | "fmt"
|
| 23 | + "io" |
22 | 24 | "io/ioutil"
|
23 | 25 | "net/url"
|
| 26 | + "os" |
24 | 27 | "path"
|
| 28 | + "strings" |
25 | 29 |
|
26 | 30 | "github.com/arduino/arduino-cli/arduino/builder"
|
27 | 31 | "github.com/arduino/arduino-cli/arduino/cores"
|
@@ -702,3 +706,151 @@ func LoadSketch(ctx context.Context, req *rpc.LoadSketchReq) (*rpc.LoadSketchRes
|
702 | 706 | AdditionalFiles: additionalFiles,
|
703 | 707 | }, nil
|
704 | 708 | }
|
| 709 | + |
| 710 | +// ArchiveSketch FIXMEDOC |
| 711 | +func ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchReq) (*rpc.ArchiveSketchResp, error) { |
| 712 | + // sketchName is the name of the sketch without extension, for example "MySketch" |
| 713 | + var sketchName string |
| 714 | + |
| 715 | + sketchPath := paths.New(req.SketchPath) |
| 716 | + if sketchPath == nil { |
| 717 | + sketchPath = paths.New(".") |
| 718 | + } |
| 719 | + |
| 720 | + sketchPath, err := sketchPath.Clean().Abs() |
| 721 | + if err != nil { |
| 722 | + return nil, fmt.Errorf("Error getting absolute sketch path %v", err) |
| 723 | + } |
| 724 | + |
| 725 | + // Get the sketch name and make sketchPath point to the ino file |
| 726 | + if sketchPath.IsDir() { |
| 727 | + sketchName = sketchPath.Base() |
| 728 | + sketchPath = sketchPath.Join(sketchName + ".ino") |
| 729 | + } else if sketchPath.Ext() == ".ino" { |
| 730 | + sketchName = strings.TrimSuffix(sketchPath.Base(), ".ino") |
| 731 | + } |
| 732 | + |
| 733 | + // Checks if it's really a sketch |
| 734 | + if sketchPath.NotExist() { |
| 735 | + return nil, fmt.Errorf("specified path is not a sketch: %v", sketchPath.String()) |
| 736 | + } |
| 737 | + |
| 738 | + archivePath := paths.New(req.ArchivePath) |
| 739 | + if archivePath == nil { |
| 740 | + archivePath = sketchPath.Parent().Parent() |
| 741 | + } |
| 742 | + |
| 743 | + archivePath, err = archivePath.Clean().Abs() |
| 744 | + if err != nil { |
| 745 | + return nil, fmt.Errorf("Error getting absolute archive path %v", err) |
| 746 | + } |
| 747 | + |
| 748 | + // Makes archivePath point to a zip file |
| 749 | + if archivePath.IsDir() { |
| 750 | + archivePath = archivePath.Join(sketchName + ".zip") |
| 751 | + } else if archivePath.Ext() == "" { |
| 752 | + archivePath = paths.New(archivePath.String() + ".zip") |
| 753 | + } |
| 754 | + |
| 755 | + if archivePath.Exist() { |
| 756 | + return nil, fmt.Errorf("archive already exists") |
| 757 | + } |
| 758 | + |
| 759 | + archive, err := os.Create(archivePath.Clean().String()) |
| 760 | + if err != nil { |
| 761 | + return nil, fmt.Errorf("Error creating archive: %v", err) |
| 762 | + } |
| 763 | + defer archive.Close() |
| 764 | + |
| 765 | + zipWriter := zip.NewWriter(archive) |
| 766 | + defer zipWriter.Close() |
| 767 | + |
| 768 | + filesToZip, err := getSketchContent(sketchPath.Parent()) |
| 769 | + if err != nil { |
| 770 | + return nil, fmt.Errorf("Error retrieving sketch files: %v", err) |
| 771 | + } |
| 772 | + |
| 773 | + for _, f := range filesToZip { |
| 774 | + |
| 775 | + if !req.IncludeBuildDir { |
| 776 | + filePath, err := sketchPath.Parent().Parent().RelTo(f) |
| 777 | + if err != nil { |
| 778 | + return nil, fmt.Errorf("Error calculating relative file path: %v", err) |
| 779 | + } |
| 780 | + |
| 781 | + // Skips build folder |
| 782 | + if strings.HasPrefix(filePath.String(), sketchName+"/build") { |
| 783 | + continue |
| 784 | + } |
| 785 | + } |
| 786 | + |
| 787 | + // We get the parent path since we want the archive to unpack as a folder. |
| 788 | + // If we don't do this the archive would contain all the sketch files as top level. |
| 789 | + err = addFileToSketchArchive(zipWriter, f, sketchPath.Parent().Parent()) |
| 790 | + if err != nil { |
| 791 | + return nil, fmt.Errorf("Error adding file to archive: %v", err) |
| 792 | + } |
| 793 | + } |
| 794 | + |
| 795 | + return &rpc.ArchiveSketchResp{}, nil |
| 796 | +} |
| 797 | + |
| 798 | +// Recursively retrieves all files in the sketch folder |
| 799 | +func getSketchContent(sketchFolder *paths.Path) (paths.PathList, error) { |
| 800 | + sketchFiles, err := sketchFolder.ReadDir() |
| 801 | + if err != nil { |
| 802 | + return nil, err |
| 803 | + } |
| 804 | + for _, f := range sketchFiles { |
| 805 | + if f.IsDir() { |
| 806 | + files, err := getSketchContent(f) |
| 807 | + if err != nil { |
| 808 | + return nil, err |
| 809 | + } |
| 810 | + |
| 811 | + sketchFiles = append(sketchFiles, files...) |
| 812 | + } |
| 813 | + } |
| 814 | + finalFiles := paths.PathList{} |
| 815 | + for _, f := range sketchFiles { |
| 816 | + if f.IsNotDir() { |
| 817 | + finalFiles = append(finalFiles, f) |
| 818 | + } |
| 819 | + } |
| 820 | + return finalFiles, nil |
| 821 | +} |
| 822 | + |
| 823 | +// Adds a single file to an existing zip file |
| 824 | +func addFileToSketchArchive(zipWriter *zip.Writer, filePath, sketchPath *paths.Path) error { |
| 825 | + f, err := filePath.Open() |
| 826 | + if err != nil { |
| 827 | + return err |
| 828 | + } |
| 829 | + defer f.Close() |
| 830 | + |
| 831 | + info, err := f.Stat() |
| 832 | + if err != nil { |
| 833 | + return err |
| 834 | + } |
| 835 | + |
| 836 | + header, err := zip.FileInfoHeader(info) |
| 837 | + if err != nil { |
| 838 | + return err |
| 839 | + } |
| 840 | + |
| 841 | + filePath, err = sketchPath.RelTo(filePath) |
| 842 | + if err != nil { |
| 843 | + return err |
| 844 | + } |
| 845 | + |
| 846 | + header.Name = filePath.String() |
| 847 | + header.Method = zip.Deflate |
| 848 | + |
| 849 | + writer, err := zipWriter.CreateHeader(header) |
| 850 | + if err != nil { |
| 851 | + return err |
| 852 | + } |
| 853 | + |
| 854 | + _, err = io.Copy(writer, f) |
| 855 | + return err |
| 856 | +} |
0 commit comments