Skip to content

Commit

Permalink
[cbuild] Add list environment command
Browse files Browse the repository at this point in the history
Related to Open-CMSIS-Pack/devtools#783

Extended `cbuild` with `list environment` command
for e.g
```
$ cbuild list environment
CMSIS_PACK_ROOT=C:/Users/Test/AppData/Local/Arm/Packs
CMSIS_COMPILER_ROOT=C:/Test/cmsis-toolbox/etc
cmake=C:/tools/bin/cmake.exe, version 3.26.0
ninja=C:/Users/Test/AppData/Local/Programs/Python/Python310/Scripts/ninja.exe, version 1.10.2
```
or 
```
$ cbuild list environment
CMSIS_PACK_ROOT=C:/Users/Test/AppData/Local/Arm/Packs
CMSIS_COMPILER_ROOT=C:/Test/cmsis-toolbox/etc
cmake=<Not Found>
ninja=<Not Found>
```
or
```
$ cbuild list environment -h
Print list of environment configurations

Usage:
  cbuild list environment [flags]

Flags:
  -h, --help   help for environment

Global Flags:
  -l, --log string   Save output messages in a log file

```
  • Loading branch information
soumeh01 authored May 11, 2023
1 parent 75fbf9f commit 1ea30b2
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 8 deletions.
7 changes: 6 additions & 1 deletion cmd/cbuild/commands/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
34 changes: 34 additions & 0 deletions cmd/cbuild/commands/list/list_environment.go
Original file line number Diff line number Diff line change
@@ -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()
},
}
39 changes: 39 additions & 0 deletions cmd/cbuild/commands/list/list_environment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2023 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/

package list_test

import (
"cbuild/cmd/cbuild/commands"
"testing"

"github.com/stretchr/testify/assert"
)

func TestListEnvironmentCommand(t *testing.T) {
assert := assert.New(t)

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", "environment", "-h"})
err := cmd.Execute()
assert.Nil(err)
})
}
65 changes: 65 additions & 0 deletions pkg/builder/csolution/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -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 installed exe path and version number
getInstalledExeInfo := func(name string) string {
path, err := utils.GetInstalledExePath(name)
if err != nil || path == "" {
return "<Not Found>"
}

// run "exe --version" command
versionStr, err := b.Runner.ExecuteCommand(path, true, "--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="+getInstalledExeInfo("cmake"))
envConfigs = append(envConfigs, "ninja="+getInstalledExeInfo("ninja"))

return envConfigs, nil
}

func (b CSolutionBuilder) ListConfigurations() error {
configurations, err := b.listConfigurations()
if err != nil {
Expand All @@ -312,6 +366,17 @@ func (b CSolutionBuilder) ListToolchains() error {
return err
}

func (b CSolutionBuilder) ListEnvironment() error {
envConfigs, err := b.listEnvironment(true)
if err != nil {
return err
}
for _, config := range envConfigs {
fmt.Println(config)
}
return nil
}

func (b CSolutionBuilder) Build() (err error) {
_ = utils.UpdateEnvVars(b.InstallConfigs.BinPath, b.InstallConfigs.EtcPath)

Expand Down
41 changes: 41 additions & 0 deletions pkg/builder/csolution/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
12 changes: 5 additions & 7 deletions pkg/utils/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
package utils

import (
"errors"
"os"
"path/filepath"
"runtime"

log "github.com/sirupsen/logrus"
)

type Configurations struct {
Expand All @@ -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 != "" {
Expand All @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package utils
import (
"errors"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -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
}
9 changes: 9 additions & 0 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}

0 comments on commit 1ea30b2

Please sign in to comment.