From 8f0b394a3eaaba4ea8d26c2a87ff05827616518e Mon Sep 17 00:00:00 2001 From: Alessandro Sanino Date: Tue, 13 Jun 2017 11:45:23 +0200 Subject: [PATCH] lib install implemented, needs refactoring --- cmd/install.go | 4 +- common/common.go | 70 +++++++++++++++++-- libraries/install_uninstall.go | 120 ++++++++++++++++++++------------- 3 files changed, 143 insertions(+), 51 deletions(-) diff --git a/cmd/install.go b/cmd/install.go index 770394adebd..4e26dd9a98d 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -26,8 +26,8 @@ import ( // installCmd represents the lib install command. var installCmd = &cobra.Command{ Use: "install", - Short: "Installs a specified library into the system.", - Long: `Installs a specified library into the system.`, + Short: "Installs one of more specified libraries into the system.", + Long: `Installs one or more specified libraries into the system.`, RunE: executeInstallCommand, } diff --git a/common/common.go b/common/common.go index 2aa3d7227ef..86292e8f9fb 100644 --- a/common/common.go +++ b/common/common.go @@ -1,7 +1,10 @@ package common import ( + "archive/zip" "fmt" + "io/ioutil" + "os" "os/user" "path/filepath" "runtime" @@ -9,25 +12,84 @@ import ( // GetDefaultArduinoFolder returns the default data folder for Arduino platform func GetDefaultArduinoFolder() (string, error) { + var folder string + usr, err := user.Current() if err != nil { return "", err } + switch runtime.GOOS { case "linux": - return filepath.Join(usr.HomeDir, ".arduino15"), nil + folder = filepath.Join(usr.HomeDir, ".arduino15") case "darwin": - return filepath.Join(usr.HomeDir, "Library", "arduino15"), nil + folder = filepath.Join(usr.HomeDir, "Library", "arduino15") default: return "", fmt.Errorf("Unsupported OS: %s", runtime.GOOS) } + _, err = os.Stat(folder) + if os.IsNotExist(err) { + fmt.Print("Cannot find default arduino folder, attemping to create it ...") + err = os.MkdirAll(folder, 0755) + if err != nil { + fmt.Println("ERROR") + fmt.Println("Cannot create arduino folder") + return "", err + } + fmt.Println("OK") + } + return folder, nil } // GetDefaultLibFolder get the default folder of downloaded libraries. func GetDefaultLibFolder() (string, error) { - usr, err := user.Current() + baseFolder, err := GetDefaultArduinoFolder() if err != nil { return "", err } - return filepath.Join(usr.HomeDir, "Arduino", "libraries"), nil + + libFolder := filepath.Join(baseFolder, "libraries") + _, err = os.Stat(libFolder) + if os.IsNotExist(err) { + fmt.Print("Cannot find libraries folder, attempting to create it ... ") + err = os.MkdirAll(libFolder, 0755) + if err != nil { + fmt.Println("ERROR") + fmt.Println("Cannot create libraries folder") + return "", err + } + fmt.Println("OK") + } + return libFolder, nil +} + +func Unzip(archive *zip.Reader, destination string) error { + for _, file := range archive.File { + path := filepath.Join(destination, file.Name) + if file.FileInfo().IsDir() { + err := os.MkdirAll(path, 0755) + if err != nil { + return fmt.Errorf("Cannot create directory during extraction. Process has been aborted") + } + } else { + err := os.MkdirAll(filepath.Dir(path), 0755) + if err != nil { + return fmt.Errorf("Cannot create directory tree of file during extraction. Process has been aborted") + } + + fileOpened, err := file.Open() + if err != nil { + return fmt.Errorf("Cannot open archived file, process has been aborted") + } + content, err := ioutil.ReadAll(fileOpened) + if err != nil { + return fmt.Errorf("Cannot read archived file, process has been aborted") + } + err = ioutil.WriteFile(path, content, 0664) + if err != nil { + return fmt.Errorf("Cannot copy archived file, process has been aborted, %s", err) + } + } + } + return nil } diff --git a/libraries/install_uninstall.go b/libraries/install_uninstall.go index 9bca5d74d6e..bc3d0e3cc9e 100644 --- a/libraries/install_uninstall.go +++ b/libraries/install_uninstall.go @@ -12,45 +12,94 @@ import ( "github.com/bcmi-labs/arduino-cli/common" ) -// DownloadAndInstall downloads a library and installs it to its specified location. -func DownloadAndInstall(library *Library) error { +// download downloads a library from arduino repository. +func download(library *Library) ([]byte, error) { client := http.DefaultClient + + request, err := http.NewRequest("GET", library.Latest.URL, nil) + if err != nil { + return nil, fmt.Errorf("Cannot create HTTP request") + } + + response, err := client.Do(request) + if err != nil { + return nil, fmt.Errorf("Cannot fetch library. Response creation error") + } else if response.StatusCode != 200 { + response.Body.Close() + return nil, fmt.Errorf("Cannot fetch library. Source responded with a status %d code", response.StatusCode) + } + defer response.Body.Close() + + // Download completed, now move the archive to temp location and unpack it. + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("Cannot read response body") + } + return body, nil +} + +// getDownloadCacheFolder gets the folder where temp installs are stored until installation complete (libraries). +func getDownloadCacheFolder(library *Library) (string, error) { libFolder, err := common.GetDefaultLibFolder() if err != nil { - fmt.Print("Cannot find lib folder, trying to create it ... ") - baseFolder, err := common.GetDefaultArduinoFolder() + return "", err + } + + stagingFolder := filepath.Join(libFolder, "cache") + _, err = os.Stat(stagingFolder) + if err != nil { + fmt.Print("Cannot find cache folder of libraries, attempting to create it ... ") + err = os.MkdirAll(stagingFolder, 0755) if err != nil { fmt.Println("ERROR") - fmt.Println("Cannot create libraries folder, no current user defined") - return fmt.Errorf("Cannot create libraries folder") + fmt.Println("Cannot create cache folder of libraries") + return "", err } - libFolder = filepath.Join(baseFolder, "Arduino", "libraries") + fmt.Println("OK") + } + return stagingFolder, nil +} + +// getLibFolder returns the destination folder of the downloaded specified library. +// It creates the folder if does not find it. +func getLibFolder(library *Library) (string, error) { + baseFolder, err := common.GetDefaultLibFolder() + if err != nil { + return "", err + } + + libFolder := filepath.Join(baseFolder, fmt.Sprintf("%s-%s", library.Name, library.Latest.Version)) + + _, err = os.Stat(libFolder) + if os.IsNotExist(err) { + fmt.Print("Cannot find lib folder, trying to create it ... ") err = os.MkdirAll(libFolder, 0755) if err != nil { fmt.Println("ERROR") - fmt.Println("Cannot create libraries folder") - return fmt.Errorf("Cannot create libraries folder") + fmt.Println("Cannot create lib folder") + return "", err } fmt.Println("OK") } - request, err := http.NewRequest("GET", library.Latest.URL, nil) + return libFolder, nil +} + +// DownloadAndInstall downloads a library and installs it to its specified location. +func DownloadAndInstall(library *Library) error { + libFolder, err := common.GetDefaultLibFolder() if err != nil { - return fmt.Errorf("Cannot create HTTP request") + return fmt.Errorf("Cannot get Lib destination directory") } - response, err := client.Do(request) + + stagingFolder, err := getDownloadCacheFolder(library) if err != nil { - return fmt.Errorf("Cannot fetch library. Response creation error") - } else if response.StatusCode != 200 { - response.Body.Close() - return fmt.Errorf("Cannot fetch library. Source responded with a status %d code", response.StatusCode) + return fmt.Errorf("Cannot get staging installs folder") } - defer response.Body.Close() - // Download completed, now move the archive to temp location and unpack it. - body, err := ioutil.ReadAll(response.Body) + body, err := download(library) if err != nil { - return fmt.Errorf("Cannot read response body") + return err } reader := bytes.NewReader(body) @@ -60,34 +109,15 @@ func DownloadAndInstall(library *Library) error { return fmt.Errorf("Cannot read downloaded archive") } - zipExtractionPath := filepath.Join(libFolder, fmt.Sprintf("%s-%s", library.Name, library.Latest.Version)) - - err = os.MkdirAll(zipExtractionPath, 0755) + // if I can read the archive I save it to staging folder. + err = ioutil.WriteFile(filepath.Join(stagingFolder, fmt.Sprintf("%s-%s.zip", library.Name, library.Latest.Version)), body, 0666) if err != nil { - return fmt.Errorf("Cannot create library final destination directory") + return fmt.Errorf("Cannot write download to cache folder, %s", err.Error()) } - for _, file := range archive.File { - if file.FileInfo().IsDir() { - err = os.MkdirAll(filepath.Join(zipExtractionPath, file.Name), 0755) - if err != nil { - return fmt.Errorf("Cannot create directory during extraction. Process has been aborted") - } - } else { - - } - fileOpened, err := file.Open() - if err != nil { - return fmt.Errorf("Cannot open archived file, process has been aborted") - } - content, err := ioutil.ReadAll(fileOpened) - if err != nil { - return fmt.Errorf("Cannot read archived file, process has been aborted") - } - err = ioutil.WriteFile(filepath.Join(zipExtractionPath, file.Name), content, 0666) - if err != nil { - return fmt.Errorf("Cannot copy archived file, process has been aborted") - } + err = common.Unzip(archive, libFolder) + if err != nil { + return err } return nil