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 <ccoll...@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}, }