From 8ea0f3e106c2dfc2a9127d9771ac8ae10d88be57 Mon Sep 17 00:00:00 2001 From: Sourabh Mehta Date: Fri, 5 May 2023 16:01:48 +0200 Subject: [PATCH 1/4] Add list environment command --- cmd/cbuild/commands/list/list.go | 7 +- cmd/cbuild/commands/list/list_environment.go | 34 ++++++++++ .../commands/list/list_environment_test.go | 41 ++++++++++++ pkg/builder/csolution/builder.go | 64 +++++++++++++++++++ pkg/utils/utils.go | 9 +++ pkg/utils/utils_test.go | 9 +++ 6 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 cmd/cbuild/commands/list/list_environment.go create mode 100644 cmd/cbuild/commands/list/list_environment_test.go diff --git a/cmd/cbuild/commands/list/list.go b/cmd/cbuild/commands/list/list.go index 46c1017e..71c4b2cb 100644 --- a/cmd/cbuild/commands/list/list.go +++ b/cmd/cbuild/commands/list/list.go @@ -30,5 +30,10 @@ func init() { _ = command.Flags().MarkHidden("toolchain") command.Parent().HelpFunc()(command, strings) }) - ListCmd.AddCommand(ListConfigurationsCmd, ListContextsCmd, ListToolchainsCmd) + ListEnvironmentCmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + _ = command.Flags().MarkHidden("schema") + _ = command.Flags().MarkHidden("toolchain") + command.Parent().HelpFunc()(command, strings) + }) + ListCmd.AddCommand(ListConfigurationsCmd, ListContextsCmd, ListToolchainsCmd, ListEnvironmentCmd) } diff --git a/cmd/cbuild/commands/list/list_environment.go b/cmd/cbuild/commands/list/list_environment.go new file mode 100644 index 00000000..1174ad97 --- /dev/null +++ b/cmd/cbuild/commands/list/list_environment.go @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package list + +import ( + "cbuild/pkg/builder" + "cbuild/pkg/builder/csolution" + "cbuild/pkg/utils" + + "github.com/spf13/cobra" +) + +var ListEnvironmentCmd = &cobra.Command{ + Use: "environment", + Short: "Print list of environment configurations", + Args: cobra.MaximumNArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + configs, err := utils.GetInstallConfigs() + if err != nil { + return err + } + + p := csolution.CSolutionBuilder{ + BuilderParams: builder.BuilderParams{ + Runner: utils.Runner{}, + InstallConfigs: configs, + }, + } + return p.ListEnvironment() + }, +} diff --git a/cmd/cbuild/commands/list/list_environment_test.go b/cmd/cbuild/commands/list/list_environment_test.go new file mode 100644 index 00000000..3160c2f3 --- /dev/null +++ b/cmd/cbuild/commands/list/list_environment_test.go @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package list_test + +import ( + "cbuild/cmd/cbuild/commands" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListEnvironmentCommand(t *testing.T) { + assert := assert.New(t) + os.Setenv("CMSIS_BUILD_ROOT", testRoot+"/run/bin") + + t.Run("invalid args", func(t *testing.T) { + cmd := commands.NewRootCmd() + cmd.SetArgs([]string{"list", "environment", "--invalid"}) + err := cmd.Execute() + assert.Error(err) + }) + + t.Run("test list environment", func(t *testing.T) { + cmd := commands.NewRootCmd() + cmd.SetArgs([]string{"list", "environment"}) + err := cmd.Execute() + assert.Error(err) + }) + + t.Run("test help", func(t *testing.T) { + cmd := commands.NewRootCmd() + cmd.SetArgs([]string{"list", "toolchains", "-h"}) + err := cmd.Execute() + assert.Nil(err) + }) +} diff --git a/pkg/builder/csolution/builder.go b/pkg/builder/csolution/builder.go index e7df790b..00713662 100644 --- a/pkg/builder/csolution/builder.go +++ b/pkg/builder/csolution/builder.go @@ -14,6 +14,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "sort" "strconv" "strings" @@ -293,6 +294,59 @@ func (b CSolutionBuilder) listToolchains(quiet bool) (toolchains []string, err e return toolchains, nil } +func (b CSolutionBuilder) listEnvironment(quiet bool) (envConfigs []string, err error) { + // get installer path and version number + getInstallerInfo := func(name string) string { + path, err := utils.GetInstalledExePath(name) + if err != nil || path == "" { + return "" + } + + // run "exe --version" command + versionStr, err := b.Runner.ExecuteCommand(path, false, "--version") + if err != nil { + versionStr = "" + } + + // get version + var version string + if name == "cmake" { + regex := "version\\s(.*?)\\s" + re, err := regexp.Compile(regex) + if err == nil { + match := re.FindAllStringSubmatch(versionStr, 1) + for index := range match { + version = match[index][1] + break + } + } + } else { + version = versionStr + } + info := path + if version != "" { + info += ", version " + version + } + return info + } + + // step1: call csolution list environment + args := []string{"list", "environment"} + output, err := b.runCSolution(args, quiet) + if err != nil { + return + } + if output != "" { + envConfigs = strings.Split(strings.ReplaceAll(strings.TrimSpace(output), "\r\n", "\n"), "\n") + } + + // step2: add other environment info + envConfigs = append(envConfigs, "cmake="+getInstallerInfo("cmake")) + envConfigs = append(envConfigs, "ninja="+getInstallerInfo("ninja")) + + return envConfigs, nil +} + func (b CSolutionBuilder) ListConfigurations() error { configurations, err := b.listConfigurations() if err != nil { @@ -312,6 +366,16 @@ func (b CSolutionBuilder) ListToolchains() error { return err } +func (b CSolutionBuilder) ListEnvironment() error { + envConfigs, err := b.listEnvironment(true) + if err == nil { + for _, config := range envConfigs { + fmt.Println(config) + } + } + return err +} + func (b CSolutionBuilder) Build() (err error) { _ = utils.UpdateEnvVars(b.InstallConfigs.BinPath, b.InstallConfigs.EtcPath) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index d876279e..da11af7d 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -9,6 +9,7 @@ package utils import ( "errors" "os" + "os/exec" "path/filepath" "runtime" "strings" @@ -336,3 +337,11 @@ func Contains[T comparable](slice []T, elem T) bool { } return false } + +func GetInstalledExePath(exeName string) (path string, err error) { + path, err = exec.LookPath(exeName) + if strings.Contains(path, "\\") { + path = strings.ReplaceAll(path, "\\", "/") + } + return +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 6134e5bb..0f840143 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -302,3 +302,12 @@ func TestContains(t *testing.T) { assert.Equal(output, test.expectedResult) } } + +func TestGetInstalledExePath(t *testing.T) { + assert := assert.New(t) + t.Run("test to get invalid executable path", func(t *testing.T) { + path, err := GetInstalledExePath("testunknown") + assert.Equal(path, "") + assert.Error(err) + }) +} From d3480520510d3d5ba0037b79c7534ab45e0dd139 Mon Sep 17 00:00:00 2001 From: Sourabh Mehta Date: Tue, 9 May 2023 15:23:52 +0200 Subject: [PATCH 2/4] Cleanup --- cmd/cbuild/commands/list/list_environment_test.go | 4 +--- pkg/builder/csolution/builder.go | 13 +++++++------ pkg/utils/configs.go | 12 +++++------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/cmd/cbuild/commands/list/list_environment_test.go b/cmd/cbuild/commands/list/list_environment_test.go index 3160c2f3..428283cc 100644 --- a/cmd/cbuild/commands/list/list_environment_test.go +++ b/cmd/cbuild/commands/list/list_environment_test.go @@ -8,7 +8,6 @@ package list_test import ( "cbuild/cmd/cbuild/commands" - "os" "testing" "github.com/stretchr/testify/assert" @@ -16,7 +15,6 @@ import ( func TestListEnvironmentCommand(t *testing.T) { assert := assert.New(t) - os.Setenv("CMSIS_BUILD_ROOT", testRoot+"/run/bin") t.Run("invalid args", func(t *testing.T) { cmd := commands.NewRootCmd() @@ -34,7 +32,7 @@ func TestListEnvironmentCommand(t *testing.T) { t.Run("test help", func(t *testing.T) { cmd := commands.NewRootCmd() - cmd.SetArgs([]string{"list", "toolchains", "-h"}) + cmd.SetArgs([]string{"list", "environment", "-h"}) err := cmd.Execute() assert.Nil(err) }) diff --git a/pkg/builder/csolution/builder.go b/pkg/builder/csolution/builder.go index 00713662..475c7c91 100644 --- a/pkg/builder/csolution/builder.go +++ b/pkg/builder/csolution/builder.go @@ -303,7 +303,7 @@ func (b CSolutionBuilder) listEnvironment(quiet bool) (envConfigs []string, err } // run "exe --version" command - versionStr, err := b.Runner.ExecuteCommand(path, false, "--version") + versionStr, err := b.Runner.ExecuteCommand(path, true, "--version") if err != nil { versionStr = "" } @@ -368,12 +368,13 @@ func (b CSolutionBuilder) ListToolchains() error { func (b CSolutionBuilder) ListEnvironment() error { envConfigs, err := b.listEnvironment(true) - if err == nil { - for _, config := range envConfigs { - fmt.Println(config) - } + if err != nil { + return err } - return err + for _, config := range envConfigs { + fmt.Println(config) + } + return nil } func (b CSolutionBuilder) Build() (err error) { diff --git a/pkg/utils/configs.go b/pkg/utils/configs.go index 3fefd2d4..124ddc80 100644 --- a/pkg/utils/configs.go +++ b/pkg/utils/configs.go @@ -7,11 +7,10 @@ package utils import ( + "errors" "os" "path/filepath" "runtime" - - log "github.com/sirupsen/logrus" ) type Configurations struct { @@ -28,8 +27,7 @@ func GetInstallConfigs() (configs Configurations, err error) { if binPath == "" { binPath, err = GetExecutablePath() if err != nil { - log.Error("executable path was not found") - return configs, err + return Configurations{}, err } } if binPath != "" { @@ -38,9 +36,9 @@ func GetInstallConfigs() (configs Configurations, err error) { configs.BinPath = binPath etcPath := filepath.Clean(binPath + "/../etc") - if _, err := os.Stat(etcPath); os.IsNotExist(err) { - log.Error("etc directory was not found") - return configs, err + if _, err = os.Stat(etcPath); os.IsNotExist(err) { + err = errors.New(etcPath + " path was not found") + return Configurations{}, err } if etcPath != "" { etcPath, _ = filepath.Abs(etcPath) From 183db637e7d6c9e4158bf67cb009bfc4171c9a8f Mon Sep 17 00:00:00 2001 From: Sourabh Mehta Date: Wed, 10 May 2023 16:29:11 +0200 Subject: [PATCH 3/4] Addressed review comments --- pkg/builder/csolution/builder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/builder/csolution/builder.go b/pkg/builder/csolution/builder.go index 475c7c91..038a98f6 100644 --- a/pkg/builder/csolution/builder.go +++ b/pkg/builder/csolution/builder.go @@ -295,8 +295,8 @@ func (b CSolutionBuilder) listToolchains(quiet bool) (toolchains []string, err e } func (b CSolutionBuilder) listEnvironment(quiet bool) (envConfigs []string, err error) { - // get installer path and version number - getInstallerInfo := func(name string) string { + // get installed exe path and version number + getInstalledExeInfo := func(name string) string { path, err := utils.GetInstalledExePath(name) if err != nil || path == "" { return "" @@ -341,8 +341,8 @@ func (b CSolutionBuilder) listEnvironment(quiet bool) (envConfigs []string, err } // step2: add other environment info - envConfigs = append(envConfigs, "cmake="+getInstallerInfo("cmake")) - envConfigs = append(envConfigs, "ninja="+getInstallerInfo("ninja")) + envConfigs = append(envConfigs, "cmake="+getInstalledExeInfo("cmake")) + envConfigs = append(envConfigs, "ninja="+getInstalledExeInfo("ninja")) return envConfigs, nil } From e9d11b1a5e4e8b6a6fd41c733f26b961b573c1d9 Mon Sep 17 00:00:00 2001 From: Sourabh Mehta Date: Thu, 11 May 2023 09:23:03 +0200 Subject: [PATCH 4/4] Added tests --- pkg/builder/csolution/builder_test.go | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/pkg/builder/csolution/builder_test.go b/pkg/builder/csolution/builder_test.go index ecebc1fb..1ea21bd2 100644 --- a/pkg/builder/csolution/builder_test.go +++ b/pkg/builder/csolution/builder_test.go @@ -36,6 +36,8 @@ func (r RunnerMock) ExecuteCommand(program string, quiet bool, args ...string) ( return "AC5@5.6.7\nAC6@6.18.0\nGCC@11.2.1\nIAR@8.50.6\n", nil } else if args[1] == "packs" { return "ARM::test:0.0.1\r\nARM::test2:0.0.2", nil + } else if args[1] == "environment" { + return "CMSIS_PACK_ROOT=C:/Path/Packs\nCMSIS_COMPILER_ROOT=C:/Test/etc\n", nil } } else if args[0] == "convert" { return "", nil @@ -271,6 +273,45 @@ func TestListToolchians(t *testing.T) { }) } +func TestListEnvironment(t *testing.T) { + assert := assert.New(t) + os.Setenv("CMSIS_BUILD_ROOT", testRoot+"/run/bin") + configs, err := utils.GetInstallConfigs() + assert.Nil(err) + b := CSolutionBuilder{ + BuilderParams: builder.BuilderParams{ + Runner: RunnerMock{}, + InstallConfigs: configs, + }, + } + + t.Run("test list environment", func(t *testing.T) { + envConfigs, err := b.listEnvironment(true) + assert.Nil(err) + assert.Equal(len(envConfigs), 4) + assert.Equal("CMSIS_PACK_ROOT=C:/Path/Packs", envConfigs[0]) + assert.Equal("CMSIS_COMPILER_ROOT=C:/Test/etc", envConfigs[1]) + assert.Regexp(`^cmake=([^\s]+)`, envConfigs[2]) + assert.Regexp(`^ninja=([^\s]+)`, envConfigs[3]) + }) + + t.Run("test list environment fails to detect", func(t *testing.T) { + // set empty install config, when cbuild is run standalone (without installation env) + b.InstallConfigs = utils.Configurations{} + envConfigs, err := b.listEnvironment(true) + assert.Error(err) + assert.Equal(len(envConfigs), 0) + // restore install config + b.InstallConfigs = configs + }) + + t.Run("test list environment", func(t *testing.T) { + err := b.ListEnvironment() + assert.Nil(err) + }) + +} + func TestBuild(t *testing.T) { assert := assert.New(t) os.Setenv("CMSIS_BUILD_ROOT", testRoot+"/run/bin")