You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2018/11/01 18:41:23 UTC

[GitHub] ccollins476ad closed pull request #221: logcfg: Allow logs to be defined in `syscfg.yml`

ccollins476ad closed pull request #221: logcfg: Allow logs to be defined in `syscfg.yml`
URL: https://github.com/apache/mynewt-newt/pull/221
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
index e4ccc218..519632c9 100644
--- a/newt/builder/targetbuild.go
+++ b/newt/builder/targetbuild.go
@@ -101,6 +101,9 @@ func NewTargetTester(target *target.Target,
 		injectedSettings: map[string]string{},
 	}
 
+	// Indicate that this version of newt supports the generated logcfg header.
+	t.InjectSetting("NEWT_FEATURE_LOGCFG", "1")
+
 	return t, nil
 }
 
@@ -219,6 +222,12 @@ func (t *TargetBuilder) validateAndWriteCfg() error {
 		return err
 	}
 
+	if err := t.res.LCfg.EnsureWritten(
+		GeneratedIncludeDir(t.target.Name())); err != nil {
+
+		return err
+	}
+
 	return nil
 }
 
diff --git a/newt/cli/target_cmds.go b/newt/cli/target_cmds.go
index bf7d6ddf..821865f1 100644
--- a/newt/cli/target_cmds.go
+++ b/newt/cli/target_cmds.go
@@ -31,6 +31,7 @@ import (
 	log "github.com/Sirupsen/logrus"
 	"github.com/spf13/cobra"
 	"mynewt.apache.org/newt/newt/builder"
+	"mynewt.apache.org/newt/newt/logcfg"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/resolve"
@@ -778,6 +779,145 @@ func targetConfigBriefCmd(cmd *cobra.Command, args []string) {
 	}
 }
 
+func logModuleString(ls logcfg.LogSetting) string {
+	intVal, _ := ls.IntVal()
+
+	s := fmt.Sprintf("%d", intVal)
+	if ls.RefName != "" {
+		s += fmt.Sprintf("%*s [%s]", 16-len(s), "", ls.RefName)
+	}
+
+	return s
+}
+
+func logLevelString(ls logcfg.LogSetting) string {
+	intVal, _ := ls.IntVal()
+
+	s := fmt.Sprintf("%d (%s)", intVal, logcfg.LogLevelString(intVal))
+	if ls.RefName != "" {
+		s += fmt.Sprintf("%*s [%s]", 16-len(s), "", ls.RefName)
+	}
+
+	return s
+}
+
+func printLogCfgOne(l logcfg.Log) {
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "%s:\n", l.Name)
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "    Package: %s\n",
+		l.Source.FullName())
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "    Module:  %s\n",
+		logModuleString(l.Module))
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "    Level:   %s\n",
+		logLevelString(l.Level))
+}
+
+func printLogCfg(targetName string, lcfg logcfg.LCfg) {
+	if errText := lcfg.ErrorText(); errText != "" {
+		util.StatusMessage(util.VERBOSITY_DEFAULT, "!!! %s\n\n", errText)
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Log config for %s:\n",
+		targetName)
+
+	logNames := make([]string, 0, len(lcfg.Logs))
+	for name, _ := range lcfg.Logs {
+		logNames = append(logNames, name)
+	}
+	sort.Strings(logNames)
+
+	for i, logName := range logNames {
+		if i > 0 {
+			util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
+		}
+		printLogCfgOne(lcfg.Logs[logName])
+	}
+}
+
+func targetLogShowCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		NewtUsage(cmd,
+			util.NewNewtError("Must specify target or unittest name"))
+	}
+
+	TryGetProject()
+
+	for i, arg := range args {
+		b, err := TargetBuilderForTargetOrUnittest(arg)
+		if err != nil {
+			NewtUsage(cmd, err)
+		}
+
+		res := targetBuilderConfigResolve(b)
+		printLogCfg(b.GetTarget().Name(), res.LCfg)
+
+		if i < len(args)-1 {
+			util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
+		}
+	}
+}
+
+func printLogCfgBriefOne(l logcfg.Log, colWidth int) {
+	intMod, _ := l.Module.IntVal()
+	intLevel, _ := l.Level.IntVal()
+
+	levelStr := fmt.Sprintf("%d (%s)", intLevel, logcfg.LogLevelString(intLevel))
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "%*s | %-8d | %-12s\n",
+		colWidth, l.Name, intMod, levelStr)
+}
+
+func printLogCfgBrief(targetName string, lcfg logcfg.LCfg) {
+	if errText := lcfg.ErrorText(); errText != "" {
+		util.StatusMessage(util.VERBOSITY_DEFAULT, "!!! %s\n\n", errText)
+	}
+
+	util.StatusMessage(util.VERBOSITY_DEFAULT, "Brief log config for %s:\n",
+		targetName)
+
+	longest := 0
+	logNames := make([]string, 0, len(lcfg.Logs))
+	for name, _ := range lcfg.Logs {
+		logNames = append(logNames, name)
+		if len(name) > longest {
+			longest = len(name)
+		}
+	}
+	sort.Strings(logNames)
+
+	colWidth := longest + 4
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"%*s | MODULE   | LEVEL\n", colWidth, "LOG")
+	util.StatusMessage(util.VERBOSITY_DEFAULT,
+		"%s-+----------+--------------\n",
+		strings.Repeat("-", colWidth))
+	for _, logName := range logNames {
+		printLogCfgBriefOne(lcfg.Logs[logName], colWidth)
+	}
+}
+
+func targetLogBriefCmd(cmd *cobra.Command, args []string) {
+	if len(args) < 1 {
+		NewtUsage(cmd,
+			util.NewNewtError("Must specify target or unittest name"))
+	}
+
+	TryGetProject()
+
+	for i, arg := range args {
+		b, err := TargetBuilderForTargetOrUnittest(arg)
+		if err != nil {
+			NewtUsage(cmd, err)
+		}
+
+		res := targetBuilderConfigResolve(b)
+		printLogCfgBrief(b.GetTarget().Name(), res.LCfg)
+
+		if i < len(args)-1 {
+			util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
+		}
+	}
+}
+
 func targetConfigInitCmd(cmd *cobra.Command, args []string) {
 	if len(args) < 1 {
 		NewtUsage(cmd,
@@ -1130,6 +1270,43 @@ func AddTargetCommands(cmd *cobra.Command) {
 		return append(targetList(), unittestList()...)
 	})
 
+	logHelpText := "View a target's log configuration"
+
+	logCmd := &cobra.Command{
+		Use:   "logcfg",
+		Short: logHelpText,
+		Long:  logHelpText,
+		Run: func(cmd *cobra.Command, args []string) {
+			cmd.Usage()
+		},
+	}
+
+	targetCmd.AddCommand(logCmd)
+
+	logShowCmd := &cobra.Command{
+		Use:   "show <target> [target...]",
+		Short: "View a target's log configuration",
+		Long:  "View a target's log configuration",
+		Run:   targetLogShowCmd,
+	}
+
+	logCmd.AddCommand(logShowCmd)
+	AddTabCompleteFn(logShowCmd, func() []string {
+		return append(targetList(), unittestList()...)
+	})
+
+	logBriefCmd := &cobra.Command{
+		Use:   "brief <target> [target...]",
+		Short: "View a summary of target's log configuration",
+		Long:  "View a summary of target's log configuration",
+		Run:   targetLogBriefCmd,
+	}
+
+	logCmd.AddCommand(logBriefCmd)
+	AddTabCompleteFn(logBriefCmd, func() []string {
+		return append(targetList(), unittestList()...)
+	})
+
 	depHelpText := "View a target's dependency graph."
 
 	depCmd := &cobra.Command{
diff --git a/newt/logcfg/logcfg.go b/newt/logcfg/logcfg.go
new file mode 100644
index 00000000..1cd5b27b
--- /dev/null
+++ b/newt/logcfg/logcfg.go
@@ -0,0 +1,363 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package logcfg
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cast"
+
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/pkg"
+	"mynewt.apache.org/newt/newt/syscfg"
+	"mynewt.apache.org/newt/util"
+)
+
+const HEADER_PATH = "logcfg/logcfg.h"
+
+type LogSetting struct {
+	// The exact text specified as the YAML map key.
+	Text string
+
+	// If this setting refers to a syscfg setting via the `MYNEWT_VAL(...)`
+	// notation, this contains the name of the setting.  Otherwise, "".
+	RefName string
+
+	// The setting value, after setting references are resolved.
+	Value string
+}
+
+type Log struct {
+	// Log name; equal to the name of the YAML map that defines the log.
+	Name string
+
+	// The package that defines the log.
+	Source *pkg.LocalPackage
+
+	// The log's numeric module ID.
+	Module LogSetting
+
+	// The level assigned to this log.
+	Level LogSetting
+}
+
+// Map of: [log-name] => log
+type LogMap map[string]Log
+
+// The log configuration of the target.
+type LCfg struct {
+	// [log-name] => log
+	Logs LogMap
+
+	// Strings describing errors encountered while parsing the log config.
+	InvalidSettings []string
+
+	// Contains sets of logs with conflicting module IDs.
+	//     [module-ID] => <slice-of-logs-with-module-id>
+	ModuleConflicts map[int][]Log
+}
+
+// Maps numeric log levels to their string representations.  Used when
+// generating the C log macros.
+var logLevelNames = []string{
+	0: "DEBUG",
+	1: "INFO",
+	2: "WARN",
+	3: "ERROR",
+	4: "CRITICAL",
+}
+
+func LogLevelString(level int) string {
+	if level < 0 || level >= len(logLevelNames) {
+		return "???"
+	}
+
+	return logLevelNames[level]
+}
+
+func NewLCfg() LCfg {
+	return LCfg{
+		Logs:            map[string]Log{},
+		ModuleConflicts: map[int][]Log{},
+	}
+}
+
+// IntVal Extracts a log setting's integer value.
+func (ls *LogSetting) IntVal() (int, error) {
+	iv, err := util.AtoiNoOct(ls.Value)
+	if err != nil {
+		return 0, util.ChildNewtError(err)
+	}
+
+	return iv, nil
+}
+
+// Constructs a log setting from a YAML string.
+func resolveLogVal(s string, cfg *syscfg.Cfg) (LogSetting, error) {
+	refName, val, err := cfg.ExpandRef(s)
+	if err != nil {
+		return LogSetting{},
+			util.FmtNewtError("value \"%s\" references undefined setting", s)
+	}
+
+	return LogSetting{
+		Text:    s,
+		RefName: refName,
+		Value:   val,
+	}, nil
+}
+
+// Parses a single log definition from a YAML map.  The `logMapItf` parameter
+// should be a map with the following elements:
+//     "module": <module-string>
+//     "level": <level-string>
+func parseOneLog(name string, lpkg *pkg.LocalPackage, logMapItf interface{},
+	cfg *syscfg.Cfg) (Log, error) {
+
+	cl := Log{
+		Name:   name,
+		Source: lpkg,
+	}
+
+	logMap := cast.ToStringMapString(logMapItf)
+	if logMap == nil {
+		return cl, util.FmtNewtError(
+			"\"%s\" missing required field \"module\"", name)
+	}
+
+	modStr := logMap["module"]
+	if modStr == "" {
+		return cl, util.FmtNewtError(
+			"\"%s\" missing required field \"module\"", name)
+	}
+	mod, err := resolveLogVal(modStr, cfg)
+	if err != nil {
+		return cl, util.FmtNewtError(
+			"\"%s\" contains invalid \"module\": %s",
+			name, err.Error())
+	}
+	if _, err := mod.IntVal(); err != nil {
+		return cl, util.FmtNewtError(
+			"\"%s\" contains invalid \"module\": %s", name, err.Error())
+	}
+
+	levelStr := logMap["level"]
+	if levelStr == "" {
+		return cl, util.FmtNewtError(
+			"\"%s\" missing required field \"level\"", name)
+	}
+	level, err := resolveLogVal(levelStr, cfg)
+	if err != nil {
+		return cl, util.FmtNewtError(
+			"\"%s\" contains invalid \"level\": %s",
+			name, err.Error())
+	}
+	if _, err := level.IntVal(); err != nil {
+		return cl, util.FmtNewtError(
+			"\"%s\" contains invalid \"level\": %s", name, err.Error())
+	}
+
+	cl.Module = mod
+	cl.Level = level
+
+	return cl, nil
+}
+
+// Reads all the logs defined by the specified package.  The log definitions
+// are read from the `syscfg.logs` map in the package's `syscfg.yml` file.
+func (lcfg *LCfg) readOnePkg(lpkg *pkg.LocalPackage, cfg *syscfg.Cfg) {
+	lsettings := cfg.AllSettingsForLpkg(lpkg)
+	logMaps := lpkg.SyscfgY.GetValStringMap("syscfg.logs", lsettings)
+	for name, logMapItf := range logMaps {
+		cl, err := parseOneLog(name, lpkg, logMapItf, cfg)
+		if err != nil {
+			lcfg.InvalidSettings =
+				append(lcfg.InvalidSettings, strings.TrimSpace(err.Error()))
+		} else {
+			lcfg.Logs[cl.Name] = cl
+		}
+	}
+}
+
+// Searches the log configuration for logs with identical module IDs.  The log
+// configuration object is populated with the results.
+func (lcfg *LCfg) detectModuleConflicts() {
+	m := map[int][]Log{}
+
+	for _, l := range lcfg.Logs {
+		intMod, _ := l.Module.IntVal()
+		m[intMod] = append(m[intMod], l)
+	}
+
+	for mod, logs := range m {
+		if len(logs) > 1 {
+			for _, l := range logs {
+				lcfg.ModuleConflicts[mod] =
+					append(lcfg.ModuleConflicts[mod], l)
+			}
+		}
+	}
+}
+
+// Reads all log definitions for each of the specified packages.  The
+// returned LCfg object is populated with the result of this operation.
+func Read(lpkgs []*pkg.LocalPackage, cfg *syscfg.Cfg) LCfg {
+	lcfg := NewLCfg()
+
+	for _, lpkg := range lpkgs {
+		lcfg.readOnePkg(lpkg, cfg)
+	}
+
+	lcfg.detectModuleConflicts()
+
+	return lcfg
+}
+
+// If any errors were encountered while parsing log definitions, this function
+// returns a string indicating the errors.  If no errors were encountered, ""
+// is returned.
+func (lcfg *LCfg) ErrorText() string {
+	str := ""
+
+	if len(lcfg.InvalidSettings) > 0 {
+		str += "Invalid log definitions detected:"
+		for _, e := range lcfg.InvalidSettings {
+			str += "\n    " + e
+		}
+	}
+
+	if len(lcfg.ModuleConflicts) > 0 {
+		str += "Log module conflicts detected:\n"
+		for mod, logs := range lcfg.ModuleConflicts {
+			for _, l := range logs {
+				str += fmt.Sprintf("    Module=%d Log=%s Package=%s\n",
+					mod, l.Name, l.Source.FullName())
+			}
+		}
+
+		str +=
+			"\nResolve the problem by assigning unique module IDs to each log."
+	}
+
+	return str
+}
+
+// Retrieves a sorted slice of logs from the receiving log configuration.
+func (lcfg *LCfg) sortedLogs() []Log {
+	names := make([]string, 0, len(lcfg.Logs))
+
+	for n, _ := range lcfg.Logs {
+		names = append(names, n)
+	}
+	sort.Strings(names)
+
+	logs := make([]Log, 0, len(names))
+	for _, n := range names {
+		logs = append(logs, lcfg.Logs[n])
+	}
+
+	return logs
+}
+
+// Writes a no-op stub log C macro definition.
+func writeLogStub(logName string, levelStr string, w io.Writer) {
+	fmt.Fprintf(w, "#define %s_%s(...) IGNORE(__VA_ARGS__)\n",
+		logName, levelStr)
+}
+
+// Writes a log C macro definition.
+func writeLogMacro(logName string, module int, levelStr string, w io.Writer) {
+	fmt.Fprintf(w,
+		"#define %s_%s(...) MODLOG_%s(%d, __VA_ARGS__)\n",
+		logName, levelStr, levelStr, module)
+}
+
+// Write log C macro definitions for each log in the log configuration.
+func (lcfg *LCfg) writeLogMacros(w io.Writer) {
+	logs := lcfg.sortedLogs()
+	for _, l := range logs {
+		fmt.Fprintf(w, "\n")
+
+		levelInt, _ := util.AtoiNoOct(l.Level.Value)
+		for i, levelStr := range logLevelNames {
+			if i < levelInt {
+				writeLogStub(l.Name, levelStr, w)
+			} else {
+				modInt, _ := l.Module.IntVal()
+				writeLogMacro(l.Name, modInt, levelStr, w)
+			}
+		}
+	}
+}
+
+// Writes a logcfg header file to the specified writer.
+func (lcfg *LCfg) write(w io.Writer) {
+	fmt.Fprintf(w, newtutil.GeneratedPreamble())
+
+	fmt.Fprintf(w, "#ifndef H_MYNEWT_LOGCFG_\n")
+	fmt.Fprintf(w, "#define H_MYNEWT_LOGCFG_\n\n")
+
+	if len(lcfg.Logs) > 0 {
+		fmt.Fprintf(w, "#include \"modlog/modlog.h\"\n")
+		fmt.Fprintf(w, "#include \"log_common/log_common.h\"\n")
+
+		lcfg.writeLogMacros(w)
+		fmt.Fprintf(w, "\n")
+	}
+
+	fmt.Fprintf(w, "#endif\n")
+}
+
+// Ensures an up-to-date logcfg header is written for the target.
+func (lcfg *LCfg) EnsureWritten(includeDir string) error {
+	buf := bytes.Buffer{}
+	lcfg.write(&buf)
+
+	path := includeDir + "/" + HEADER_PATH
+
+	writeReqd, err := util.FileContentsChanged(path, buf.Bytes())
+	if err != nil {
+		return err
+	}
+	if !writeReqd {
+		log.Debugf("logcfg unchanged; not writing header file (%s).", path)
+		return nil
+	}
+
+	log.Debugf("logcfg changed; writing header file (%s).", path)
+
+	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	return nil
+}
diff --git a/newt/resolve/resolve.go b/newt/resolve/resolve.go
index dcdb0a37..b5fa87a9 100644
--- a/newt/resolve/resolve.go
+++ b/newt/resolve/resolve.go
@@ -27,11 +27,12 @@ import (
 	log "github.com/Sirupsen/logrus"
 
 	"mynewt.apache.org/newt/newt/flash"
+	"mynewt.apache.org/newt/newt/logcfg"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/syscfg"
-	"mynewt.apache.org/newt/util"
 	"mynewt.apache.org/newt/newt/ycfg"
+	"mynewt.apache.org/newt/util"
 )
 
 // Represents a supplied API.
@@ -58,6 +59,7 @@ type Resolver struct {
 	injectedSettings map[string]string
 	flashMap         flash.FlashMap
 	cfg              syscfg.Cfg
+	lcfg             logcfg.LCfg
 
 	// [api-name][api-supplier]
 	apiConflicts map[string]map[*ResolvePackage]struct{}
@@ -104,6 +106,7 @@ type ApiConflict struct {
 // The result of resolving a target's configuration, APIs, and dependencies.
 type Resolution struct {
 	Cfg             syscfg.Cfg
+	LCfg            logcfg.LCfg
 	ApiMap          map[string]*ResolvePackage
 	UnsatisfiedApis map[string][]*ResolvePackage
 	ApiConflicts    []ApiConflict
@@ -540,6 +543,9 @@ func (r *Resolver) resolveDepsAndCfg() error {
 		return err
 	}
 
+	lpkgs := RpkgSliceToLpkgSlice(r.rpkgSlice())
+	r.lcfg = logcfg.Read(lpkgs, &r.cfg)
+
 	// Log the final syscfg.
 	r.cfg.Log()
 
@@ -632,6 +638,7 @@ func ResolveFull(
 
 	res := newResolution()
 	res.Cfg = r.cfg
+	res.LCfg = r.lcfg
 
 	// Determine which package satisfies each API and which APIs are
 	// unsatisfied.
@@ -748,6 +755,7 @@ func (res *Resolution) ErrorText() string {
 	}
 
 	str += res.Cfg.ErrorText()
+	str += res.LCfg.ErrorText()
 
 	str = strings.TrimSpace(str)
 	if str != "" {
diff --git a/newt/syscfg/syscfg.go b/newt/syscfg/syscfg.go
index 6e75a842..40f68b30 100644
--- a/newt/syscfg/syscfg.go
+++ b/newt/syscfg/syscfg.go
@@ -68,6 +68,8 @@ const SYSCFG_PRIO_ANY = "any"
 // Reserve last 16 priorities for the system (sanity, idle).
 const SYSCFG_TASK_PRIO_MAX = 0xef
 
+var cfgRefRe = regexp.MustCompile("MYNEWT_VAL\\((\\w+)\\)")
+
 var cfgSettingNameTypeMap = map[string]CfgSettingType{
 	"raw":           CFG_SETTING_TYPE_RAW,
 	"task_priority": CFG_SETTING_TYPE_TASK_PRIO,
@@ -180,34 +182,50 @@ func (cfg *Cfg) SettingValues() map[string]string {
 	return values
 }
 
-func (cfg *Cfg) ResolveValueRefs() {
-	re := regexp.MustCompile("MYNEWT_VAL\\((\\w+)\\)")
-	for k, entry := range cfg.Settings {
-		value := strings.TrimSpace(entry.Value)
-
-		m := re.FindStringSubmatch(value)
-		if len(m) == 0 || len(m[0]) != len(value) {
-			// either there is no reference or there's something else besides
-			// reference - skip it
-			// TODO we may want to emit warning in the latter case (?)
-			continue
-		}
+func ResolveValueRefName(val string) string {
+	// If the value has the `MYNEWT_VAL([...])` notation, then extract the
+	// parenthesized identifier.
+	m := cfgRefRe.FindStringSubmatch(val)
+	if m == nil {
+		return ""
+	} else {
+		// TODO we may try to resolve nested references...
+		return m[1]
+	}
+}
 
-		newName := m[1]
+func (cfg *Cfg) ExpandRef(val string) (string, string, error) {
+	refName := ResolveValueRefName(val)
+	if refName == "" {
+		// Not a reference.
+		return "", val, nil
+	}
 
-		// TODO we may try to resolve nested references...
+	entry, ok := cfg.Settings[refName]
+	if !ok {
+		return "", "", util.FmtNewtError(
+			"setting value \"%s\" references undefined setting", val)
+	}
 
-		newEntry, exists := cfg.Settings[newName]
-		entry.ValueRefName = newName
-		if exists {
-			entry.Value = newEntry.Value
-		} else {
-			// set unresolved setting value to 0, this way restrictions
-			// can be evaluated and won't create spurious warnings
+	return entry.Name, entry.Value, nil
+
+}
+
+func (cfg *Cfg) ResolveValueRefs() {
+	for k, entry := range cfg.Settings {
+		refName, val, err := cfg.ExpandRef(strings.TrimSpace(entry.Value))
+		if err != nil {
+			// Referenced setting doesn't exist.  Set unresolved setting value
+			// to 0, this way restrictions can be evaluated and won't create
+			// spurious warnings.
 			entry.Value = "0"
 			cfg.UnresolvedValueRefs[k] = struct{}{}
+			cfg.Settings[k] = entry
+		} else if refName != "" {
+			entry.ValueRefName = refName
+			entry.Value = val
+			cfg.Settings[k] = entry
 		}
-		cfg.Settings[k] = entry
 	}
 }
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services