diff --git a/arduino/cores/packagemanager/install_uninstall.go b/arduino/cores/packagemanager/install_uninstall.go index cfcc0a21e46..112faea8340 100644 --- a/arduino/cores/packagemanager/install_uninstall.go +++ b/arduino/cores/packagemanager/install_uninstall.go @@ -16,6 +16,7 @@ package packagemanager import ( + "bytes" "encoding/json" "fmt" "runtime" @@ -174,9 +175,13 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools( if !platformRelease.IsInstalled() { return errors.New(tr("platform not installed")) } - if err := pme.RunPostInstallScript(platformRelease.InstallDir); err != nil { - taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot configure platform: %s", err)}) + stdout, stderr, err := pme.RunPostInstallScript(platformRelease.InstallDir) + skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true}) + skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true}) + if err != nil { + taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot configure platform: %s", err), Completed: true}) } + } else { log.Info("Skipping platform configuration.") taskCB(&rpc.TaskProgress{Message: tr("Skipping platform configuration.")}) @@ -226,7 +231,7 @@ func (pme *Explorer) cacheInstalledJSON(platformRelease *cores.PlatformRelease) // RunPostInstallScript runs the post_install.sh (or post_install.bat) script for the // specified platformRelease or toolRelease. -func (pme *Explorer) RunPostInstallScript(installDir *paths.Path) error { +func (pme *Explorer) RunPostInstallScript(installDir *paths.Path) ([]byte, []byte, error) { postInstallFilename := "post_install.sh" if runtime.GOOS == "windows" { postInstallFilename = "post_install.bat" @@ -235,14 +240,16 @@ func (pme *Explorer) RunPostInstallScript(installDir *paths.Path) error { if postInstall.Exist() && postInstall.IsNotDir() { cmd, err := executils.NewProcessFromPath(pme.GetEnvVarsForSpawnedProcess(), postInstall) if err != nil { - return err + return []byte{}, []byte{}, err } + cmdStdout, cmdStderr := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}) + cmd.RedirectStdoutTo(cmdStdout) + cmd.RedirectStderrTo(cmdStderr) cmd.SetDirFromPath(installDir) - if err := cmd.Run(); err != nil { - return err - } + err = cmd.Run() + return cmdStdout.Bytes(), cmdStderr.Bytes(), err } - return nil + return []byte{}, []byte{}, nil } // IsManagedPlatformRelease returns true if the PlatforRelease is managed by the PackageManager @@ -335,7 +342,10 @@ func (pme *Explorer) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.Task if !skipPostInstall { log.Info("Running tool post_install script") taskCB(&rpc.TaskProgress{Message: tr("Configuring tool.")}) - if err := pme.RunPostInstallScript(toolRelease.InstallDir); err != nil { + stdout, stderr, err := pme.RunPostInstallScript(toolRelease.InstallDir) + skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout)}) + skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr)}) + if err != nil { taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot configure tool: %s", err)}) } } else { @@ -412,3 +422,12 @@ func (pme *Explorer) IsToolRequired(toolRelease *cores.ToolRelease) bool { } return false } + +func skipEmptyMessageTaskProgressCB(taskCB rpc.TaskProgressCB) rpc.TaskProgressCB { + return func(msg *rpc.TaskProgress) { + if msg != nil && len(msg.Message) == 0 { + return + } + taskCB(msg) + } +} diff --git a/arduino/cores/packagemanager/package_manager_test.go b/arduino/cores/packagemanager/package_manager_test.go index 32dc4d3a23c..adecae80f28 100644 --- a/arduino/cores/packagemanager/package_manager_test.go +++ b/arduino/cores/packagemanager/package_manager_test.go @@ -19,6 +19,8 @@ import ( "fmt" "net/url" "os" + "runtime" + "strings" "testing" "github.com/arduino/arduino-cli/arduino/cores" @@ -639,3 +641,39 @@ func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { require.Equal(t, `"{network_cmd}" -address {upload.port.address} -port {upload.port.properties.port} -sketch "{build.path}/{build.project_name}.hex" -upload {upload.port.properties.endpoint_upload} -sync {upload.port.properties.endpoint_sync} -reset {upload.port.properties.endpoint_reset} -sync_exp {upload.port.properties.sync_return}`, platformProps.Get("tools.avrdude__pluggable_network.upload.pattern")) } } + +func TestRunPostInstall(t *testing.T) { + pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test") + pm := pmb.Build() + pme, release := pm.NewExplorer() + defer release() + + // prepare dummy post install script + dir := paths.New(t.TempDir()) + + var scriptPath *paths.Path + var err error + if runtime.GOOS == "windows" { + scriptPath = dir.Join("post_install.bat") + + err = scriptPath.WriteFile([]byte( + `@echo off + echo sent in stdout + echo sent in stderr 1>&2`)) + } else { + scriptPath = dir.Join("post_install.sh") + err = scriptPath.WriteFile([]byte( + `#!/bin/sh + echo "sent in stdout" + echo "sent in stderr" 1>&2`)) + } + require.NoError(t, err) + err = os.Chmod(scriptPath.String(), 0777) + require.NoError(t, err) + stdout, stderr, err := pme.RunPostInstallScript(dir) + require.NoError(t, err) + + // `HasPrefix` because windows seem to add a trailing space at the end + require.Equal(t, "sent in stdout", strings.Trim(string(stdout), "\n\r ")) + require.Equal(t, "sent in stderr", strings.Trim(string(stderr), "\n\r ")) +}