You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2019/10/10 17:15:10 UTC

[mynewt-newt] 06/08: Pass environment variables as map[string]string

This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git

commit ec5657770d612abaab60d5d61250a87f0927abb6
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Fri Oct 4 17:52:55 2019 -0700

    Pass environment variables as map[string]string
    
    Prior to this commit, the util shell functions accepted env vars as
    string slices (`k=v`).  The problem with this approach was that env vars
    already defined in the process would not get overwritten by new ones
    (the new ones would be appended to the list; if they conflicted with old
    ones, they were ignored).
    
    Now environment variables are passed in maps.  Newt manually overwrites
    old env vars with new ones.
---
 newt/builder/buildutil.go | 29 ++++++----------
 newt/builder/extcmd.go    | 13 +++++--
 newt/builder/load.go      | 30 +++++++---------
 util/util.go              | 87 ++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 107 insertions(+), 52 deletions(-)

diff --git a/newt/builder/buildutil.go b/newt/builder/buildutil.go
index b71fa94..65a24ad 100644
--- a/newt/builder/buildutil.go
+++ b/newt/builder/buildutil.go
@@ -21,7 +21,6 @@ package builder
 
 import (
 	"bytes"
-	"fmt"
 	"sort"
 	"strconv"
 	"strings"
@@ -33,6 +32,7 @@ import (
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/resolve"
+	"mynewt.apache.org/newt/newt/toolchain"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -173,6 +173,15 @@ func BasicEnvVars(binBase string, bspPkg *pkg.BspPackage) map[string]string {
 	return m
 }
 
+func ToolchainEnvVars(c *toolchain.Compiler) map[string]string {
+	return map[string]string{
+		"MYNEWT_CC_PATH":  c.GetCcPath(),
+		"MYNEWT_CPP_PATH": c.GetCppPath(),
+		"MYNEWT_AS_PATH":  c.GetAsPath(),
+		"MYNEWT_AR_PATH":  c.GetArPath(),
+	}
+}
+
 // SettingsEnvVars calculates the syscfg set of environment variables required
 // by image loading scripts.
 func SettingsEnvVars(settings map[string]string) map[string]string {
@@ -299,21 +308,3 @@ func (b *Builder) EnvVars(imageSlot int) (map[string]string, error) {
 
 	return env, nil
 }
-
-// EnvVarsToSlice converts an environment variable map into a slice of strings
-// suitable for "shell command" functions defined in `util` (e.g.,
-// util.ShellCommand).
-func EnvVarsToSlice(env map[string]string) []string {
-	keys := make([]string, 0, len(env))
-	for k, _ := range env {
-		keys = append(keys, k)
-	}
-	sort.Strings(keys)
-
-	slice := make([]string, 0, len(env))
-	for _, key := range keys {
-		slice = append(slice, fmt.Sprintf("%s=%s", key, env[key]))
-	}
-
-	return slice
-}
diff --git a/newt/builder/extcmd.go b/newt/builder/extcmd.go
index ba64280..cddea76 100644
--- a/newt/builder/extcmd.go
+++ b/newt/builder/extcmd.go
@@ -102,11 +102,19 @@ func (t *TargetBuilder) envVarsForCmd(sf stage.StageFunc, userSrcDir string,
 		WorkDir:    workDir,
 	}
 	uenv := UserEnvVars(p)
-
 	for k, v := range uenv {
 		env[k] = v
 	}
 
+	c, err := t.NewCompiler("", "")
+	if err != nil {
+		return nil, err
+	}
+	tenv := ToolchainEnvVars(c)
+	for k, v := range tenv {
+		env[k] = v
+	}
+
 	return env, nil
 }
 
@@ -147,8 +155,7 @@ func (t *TargetBuilder) execExtCmds(sf stage.StageFunc, userSrcDir string,
 	defer os.Chdir(pwd)
 
 	util.StatusMessage(util.VERBOSITY_DEFAULT, "Executing %s\n", sf.Name)
-	envs := EnvVarsToSlice(env)
-	if err := util.ShellInteractiveCommand(toks, envs, true); err != nil {
+	if err := util.ShellInteractiveCommand(toks, env, true); err != nil {
 		return err
 	}
 
diff --git a/newt/builder/load.go b/newt/builder/load.go
index a166b8c..5f8421f 100644
--- a/newt/builder/load.go
+++ b/newt/builder/load.go
@@ -85,7 +85,7 @@ func (t *TargetBuilder) Load(extraJtagCmd string) error {
 	return err
 }
 
-func RunOptionalCheck(checkScript string, env []string) error {
+func RunOptionalCheck(checkScript string, env map[string]string) error {
 	if checkScript == "" {
 		return nil
 	}
@@ -125,9 +125,8 @@ func Load(binBasePath string, bspPkg *pkg.BspPackage,
 	for k, v := range extraEnvSettings {
 		env[k] = v
 	}
-	envSlice := EnvVarsToSlice(env)
 
-	RunOptionalCheck(bspPkg.OptChkScript, envSlice)
+	RunOptionalCheck(bspPkg.OptChkScript, env)
 	// bspPath, binBasePath are passed in command line for backwards
 	// compatibility
 	cmd := []string{
@@ -142,7 +141,7 @@ func Load(binBasePath string, bspPkg *pkg.BspPackage,
 	for _, v := range env {
 		util.StatusMessage(util.VERBOSITY_VERBOSE, "* %s\n", v)
 	}
-	if _, err := util.ShellCommand(cmd, envSlice); err != nil {
+	if _, err := util.ShellCommand(cmd, env); err != nil {
 		return err
 	}
 	util.StatusMessage(util.VERBOSITY_VERBOSE, "Successfully loaded image.\n")
@@ -211,31 +210,26 @@ func (b *Builder) debugBin(binPath string, extraJtagCmd string, reset bool,
 
 	bspPath := b.bspPkg.rpkg.Lpkg.BasePath()
 	binBasePath := binPath
-	featureString := FeatureString(b.cfg.SettingValues())
 	bspPkg := b.targetBuilder.bspPkg
 
-	coreRepo := project.GetProject().FindRepo("apache-mynewt-core")
-	envSettings := []string{
-		fmt.Sprintf("CORE_PATH=%s", coreRepo.Path()),
-		fmt.Sprintf("BSP_PATH=%s", bspPath),
-		fmt.Sprintf("BIN_BASENAME=%s", binBasePath),
-		fmt.Sprintf("FEATURES=%s", featureString),
-		fmt.Sprintf("MYNEWT_PROJECT_ROOT=%s", ProjectRoot()),
+	env, err := b.EnvVars(0)
+	if err != nil {
+		return err
 	}
+
 	if extraJtagCmd != "" {
-		envSettings = append(envSettings,
-			fmt.Sprintf("EXTRA_JTAG_CMD=%s", extraJtagCmd))
+		env["EXTRA_JTAG_CMD"] = extraJtagCmd
 	}
 	if reset == true {
-		envSettings = append(envSettings, fmt.Sprintf("RESET=true"))
+		env["RESET"] = "true"
 	}
 	if noGDB == true {
-		envSettings = append(envSettings, fmt.Sprintf("NO_GDB=1"))
+		env["NO_GDB"] = "1"
 	}
 
 	os.Chdir(project.GetProject().Path())
 
-	RunOptionalCheck(bspPkg.OptChkScript, envSettings)
+	RunOptionalCheck(bspPkg.OptChkScript, env)
 	// bspPath, binBasePath are passed in command line for backwards
 	// compatibility
 	cmdLine := []string{
@@ -243,7 +237,7 @@ func (b *Builder) debugBin(binPath string, extraJtagCmd string, reset bool,
 	}
 
 	fmt.Printf("%s\n", cmdLine)
-	return util.ShellInteractiveCommand(cmdLine, envSettings, false)
+	return util.ShellInteractiveCommand(cmdLine, env, false)
 }
 
 func (b *Builder) Debug(extraJtagCmd string, reset bool, noGDB bool) error {
diff --git a/util/util.go b/util/util.go
index aa46495..9800ea8 100644
--- a/util/util.go
+++ b/util/util.go
@@ -273,10 +273,11 @@ func fixupCmdArgs(args []string) {
 	}
 }
 
-func LogShellCmd(cmdStrs []string, env []string) {
+func LogShellCmd(cmdStrs []string, env map[string]string) {
 	envLogStr := ""
 	if len(env) > 0 {
-		envLogStr = strings.Join(env, " ") + " "
+		s := EnvVarsToSlice(env)
+		envLogStr = strings.Join(s, " ") + " "
 	}
 	log.Debugf("%s%s", envLogStr, strings.Join(cmdStrs, " "))
 
@@ -285,12 +286,58 @@ func LogShellCmd(cmdStrs []string, env []string) {
 	}
 }
 
+// EnvVarsToSlice converts an environment variable map into a slice of `k=v`
+// strings.
+func EnvVarsToSlice(env map[string]string) []string {
+	keys := make([]string, 0, len(env))
+	for k, _ := range env {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	slice := make([]string, 0, len(env))
+	for _, key := range keys {
+		slice = append(slice, fmt.Sprintf("%s=%s", key, env[key]))
+	}
+
+	return slice
+}
+
+// SliceToEnvVars converts a slice of `k=v` strings into an environment
+// variable map.
+func SliceToEnvVars(slc []string) (map[string]string, error) {
+	m := make(map[string]string, len(slc))
+	for _, s := range slc {
+		parts := strings.SplitN(s, "=", 2)
+		if len(parts) != 2 {
+			return nil, FmtNewtError("invalid env var string: \"%s\"", s)
+		}
+
+		m[parts[0]] = parts[1]
+	}
+
+	return m, nil
+}
+
+// EnvironAsMap gathers the current process's set of environment variables and
+// returns them as a map.
+func EnvironAsMap() (map[string]string, error) {
+	slc := os.Environ()
+
+	m, err := SliceToEnvVars(slc)
+	if err != nil {
+		return nil, err
+	}
+
+	return m, nil
+}
+
 // Execute the specified process and block until it completes.  Additionally,
 // the amount of combined stdout+stderr output to be logged to the debug log
 // can be restricted to a maximum number of characters.
 //
 // @param cmdStrs               The "argv" strings of the command to execute.
-// @param env                   Additional key=value pairs to inject into the
+// @param env                   Additional key,value pairs to inject into the
 //                                  child process's environment.  Specify null
 //                                  to just inherit the parent environment.
 // @param logCmd                Whether to log the command being executed.
@@ -304,8 +351,8 @@ func LogShellCmd(cmdStrs []string, env []string) {
 //                                  or if it just returned a non-zero exit
 //                                  status.
 func ShellCommandLimitDbgOutput(
-	cmdStrs []string, env []string, logCmd bool, maxDbgOutputChrs int) (
-	[]byte, error) {
+	cmdStrs []string, env map[string]string, logCmd bool,
+	maxDbgOutputChrs int) ([]byte, error) {
 
 	var name string
 	var args []string
@@ -328,7 +375,15 @@ func ShellCommandLimitDbgOutput(
 	cmd := exec.Command(name, args...)
 
 	if env != nil {
-		cmd.Env = append(env, os.Environ()...)
+		m, err := EnvironAsMap()
+		if err != nil {
+			return nil, err
+		}
+
+		for k, v := range env {
+			m[k] = v
+		}
+		cmd.Env = EnvVarsToSlice(m)
 	}
 
 	o, err := cmd.CombinedOutput()
@@ -356,18 +411,20 @@ func ShellCommandLimitDbgOutput(
 // Execute the specified process and block until it completes.
 //
 // @param cmdStrs               The "argv" strings of the command to execute.
-// @param env                   Additional key=value pairs to inject into the
+// @param env                   Additional key,value pairs to inject into the
 //                                  child process's environment.  Specify null
 //                                  to just inherit the parent environment.
 //
 // @return []byte               Combined stdout and stderr output of process.
 // @return error                NewtError on failure.
-func ShellCommand(cmdStrs []string, env []string) ([]byte, error) {
+func ShellCommand(cmdStrs []string, env map[string]string) ([]byte, error) {
 	return ShellCommandLimitDbgOutput(cmdStrs, env, true, -1)
 }
 
 // Run interactive shell command
-func ShellInteractiveCommand(cmdStr []string, env []string, flagBlock bool) error {
+func ShellInteractiveCommand(cmdStr []string, env map[string]string,
+	flagBlock bool) error {
+
 	// Escape special characters for Windows.
 	fixupCmdArgs(cmdStr)
 
@@ -385,15 +442,21 @@ func ShellInteractiveCommand(cmdStr []string, env []string, flagBlock bool) erro
 		}()
 	}
 
-	if env != nil {
-		env = append(env, os.Environ()...)
+	m, err := EnvironAsMap()
+	if err != nil {
+		return err
+	}
+
+	for k, v := range env {
+		m[k] = v
 	}
+	envSlice := EnvVarsToSlice(m)
 
 	// Transfer stdin, stdout, and stderr to the new process
 	// and also set target directory for the shell to start in.
 	// and set the additional environment variables
 	pa := os.ProcAttr{
-		Env:   env,
+		Env:   envSlice,
 		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
 	}