func (cfg *Cfg) detectPriorityViolations() {
	for _, entry := range cfg.Settings {
		cfg.PriorityViolations = append(
			cfg.PriorityViolations, entry.priorityViolations()...)
	}
}
Detects all priority violations in the syscfg and records them internally.
package syscfg
import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"mynewt.apache.org/newt/newt/flashmap"
	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/newt/parse"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)
const HEADER_PATH = "syscfg/syscfg.h"
const SYSCFG_PREFIX_SETTING = "MYNEWT_VAL_"
type CfgSettingType int
const (
	CFG_SETTING_TYPE_RAW CfgSettingType = iota
	CFG_SETTING_TYPE_TASK_PRIO
	CFG_SETTING_TYPE_INTERRUPT_PRIO
	CFG_SETTING_TYPE_FLASH_OWNER
)
type CfgSettingState int
const (
	CFG_SETTING_STATE_GOOD CfgSettingState = iota
	CFG_SETTING_STATE_DEPRECATED
	CFG_SETTING_STATE_DEFUNCT
)
type CfgFlashConflictCode int
const (
	CFG_FLASH_CONFLICT_CODE_BAD_NAME CfgFlashConflictCode = iota
	CFG_FLASH_CONFLICT_CODE_NOT_UNIQUE
)
const SYSCFG_PRIO_ANY = "any"
const SYSCFG_TASK_PRIO_MAX = 0xef
var cfgRefRe = regexp.MustCompile("MYNEWT_VAL\\((\\w+)\\)")
var cfgChoiceValRe = regexp.MustCompile("^[A-Za-z0-9_]+$")
type CfgPoint struct {
	Value  string
	Source *pkg.LocalPackage
}
type CfgDeprecatedPoint struct {
	Entry  *CfgEntry
	Source *pkg.LocalPackage
}
type CfgEntry struct {
	Name         string
	Value        string
	ValueRefName string
	Description  string
	SettingType  CfgSettingType
	Restrictions []CfgRestriction
	ValidChoices []string
	PackageDef   *pkg.LocalPackage
	History      []CfgPoint
	State        CfgSettingState
}
type CfgPriority struct {
	SettingName string
	PackageDef  *pkg.LocalPackage  
	PackageSrc  *pkg.LocalPackage  
}
type CfgFlashConflict struct {
	SettingNames []string
	Code         CfgFlashConflictCode
}
type Cfg struct {
	Settings map[string]CfgEntry
	PackageRestrictions map[string][]CfgRestriction
	Orphans map[string][]CfgPoint
	Ambiguities map[string][]CfgPoint
	SettingViolations map[string][]CfgRestriction
	PackageViolations map[string][]CfgRestriction
	PriorityViolations []CfgPriority
	FlashConflicts []CfgFlashConflict
	Redefines map[string]map[*pkg.LocalPackage]struct{}
	Deprecated map[string]struct{}
	Defunct map[string]struct{}
	UnresolvedValueRefs map[string]struct{}
}
func NewCfg() Cfg {
	return Cfg{
		Settings:            map[string]CfgEntry{},
		PackageRestrictions: map[string][]CfgRestriction{},
		Orphans:             map[string][]CfgPoint{},
		Ambiguities:         map[string][]CfgPoint{},
		SettingViolations:   map[string][]CfgRestriction{},
		PackageViolations:   map[string][]CfgRestriction{},
		PriorityViolations:  []CfgPriority{},
		FlashConflicts:      []CfgFlashConflict{},
		Redefines:           map[string]map[*pkg.LocalPackage]struct{}{},
		Deprecated:          map[string]struct{}{},
		Defunct:             map[string]struct{}{},
		UnresolvedValueRefs: map[string]struct{}{},
	}
}
func SettingValues(settings map[string]CfgEntry) map[string]string {
	values := make(map[string]string, len(settings))
	for k, v := range settings {
		values[k] = v.Value
	}
	return values
}
func (cfg *Cfg) SettingValues() map[string]string {
	return SettingValues(cfg.Settings)
}
func ResolveValueRefName(val string) string {
	m := cfgRefRe.FindStringSubmatch(val)
	if m == nil {
		return ""
	} else {
		return m[1]
	}
}
func (cfg *Cfg) ExpandRef(val string) (string, string, error) {
	refName := ResolveValueRefName(val)
	if refName == "" {
		return "", val, nil
	}
	entry, ok := cfg.Settings[refName]
	if !ok {
		return "", "", util.FmtNewtError(
			"setting value \"%s\" references undefined setting", val)
	}
	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 {
			entry.Value = "0"
			cfg.UnresolvedValueRefs[k] = struct{}{}
			cfg.Settings[k] = entry
		} else if refName != "" {
			entry.ValueRefName = refName
			entry.Value = val
			cfg.Settings[k] = entry
		}
	}
}
func (cfg *Cfg) settingsForLpkg(lpkg *pkg.LocalPackage,
	baseSettings map[string]string) map[string]string {
	var settings map[string]string
	injected := lpkg.InjectedSettings()
	if len(injected) == 0 {
		return baseSettings
	}
	settings = make(map[string]string, len(baseSettings))
	for k, v := range baseSettings {
		settings[k] = v
	}
	for k, v := range lpkg.InjectedSettings() {
		_, ok := settings[k]
		if ok {
			log.Warnf("Attempt to override syscfg setting %s with "+
				"injected feature from package %s", k, lpkg.FullName())
		} else {
			settings[k] = v
		}
	}
	return settings
}
func (cfg *Cfg) AllSettingsForLpkg(lpkg *pkg.LocalPackage) map[string]string {
	return cfg.settingsForLpkg(lpkg, cfg.SettingValues())
}
func (point CfgPoint) Name() string {
	if point.Source == nil {
		return "newt"
	} else {
		return point.Source.FullName()
	}
}
func (point CfgPoint) IsInjected() bool {
	return point.Source == nil
}
func (entry *CfgEntry) IsTrue() bool {
	return parse.ValueIsTrue(entry.Value)
}
func (entry *CfgEntry) appendValue(lpkg *pkg.LocalPackage, value interface{}) {
	strval := stringValue(value)
	point := CfgPoint{Value: strval, Source: lpkg}
	entry.History = append(entry.History, point)
	entry.Value = strval
}
func (entry *CfgEntry) replaceSource(lpkg *pkg.LocalPackage) {
	entry.PackageDef = lpkg
	entry.History[0].Source = lpkg
}
func historyToString(history []CfgPoint) string {
	if len(history) == 0 {
		return "(undefined)"
	}
	str := "["
	for i, _ := range history {
		if i != 0 {
			str += ", "
		}
		p := history[len(history)-i-1]
		str += fmt.Sprintf("%s:%s", p.Name(), p.Value)
	}
	str += "]"
	return str
}
func (entry *CfgEntry) ambiguities() []CfgPoint {
	diffVals := false
	var points []CfgPoint
	for i := 1; i < len(entry.History)-1; i++ {
		cur := entry.History[len(entry.History)-i-1]
		next := entry.History[len(entry.History)-i]
		if cur.Source == nil || next.Source == nil {
			break
		}
		if normalizePkgType(cur.Source.Type()) !=
			normalizePkgType(next.Source.Type()) {
			break
		}
		if cur.Value != next.Value {
			diffVals = true
		}
		if len(points) == 0 {
			points = append(points, cur)
		}
		points = append(points, next)
	}
	if !diffVals {
		points = nil
	}
	return points
}
func (entry *CfgEntry) ambiguityText() string {
	points := entry.ambiguities()
	if len(points) == 0 {
		return ""
	}
	str := fmt.Sprintf("Setting: %s, Packages: [", entry.Name)
	for i, p := range points {
		if i > 0 {
			str += ", "
		}
		str += p.Source.FullName()
	}
	str += "]"
	return str
}
func (entry *CfgEntry) priorityViolations() []CfgPriority {
	var violations []CfgPriority
	for i := 1; i < len(entry.History); i++ {
		p := entry.History[i]
		curType := normalizePkgType(p.Source.Type())
		defType := normalizePkgType(entry.PackageDef.Type())
		if p.Source != entry.PackageDef && curType <= defType && entry.History[0].Value != "" {
			priority := CfgPriority{
				PackageDef:  entry.PackageDef,
				PackageSrc:  p.Source,
				SettingName: entry.Name,
			}
			violations = append(violations, priority)
		}
	}
	return violations
}
func FeatureToCflag(featureName string) string {
	return fmt.Sprintf("-D%s=1", settingName(featureName))
}
func stringValue(val interface{}) string {
	return strings.TrimSpace(cast.ToString(val))
}
func boolValue(val interface{}) bool {
	if val == nil {
		return false
	}
	switch v := val.(type) {
	case bool:
		return v
	case int:
		return v != 0
	default:
		return true
	}
}
func readSetting(name string, lpkg *pkg.LocalPackage,
	vals map[interface{}]interface{}) (CfgEntry, error) {
	entry := CfgEntry{}
	entry.Name = name
	entry.PackageDef = lpkg
	entry.Description = stringValue(vals["description"])
	if boolValue(vals["defunct"]) {
		entry.State = CFG_SETTING_STATE_DEFUNCT
	} else if boolValue(vals["deprecated"]) {
		entry.State = CFG_SETTING_STATE_DEPRECATED
	} else {
		entry.State = CFG_SETTING_STATE_GOOD
	}
	valueVal, valueExist := vals["value"]
	if valueExist {
		entry.Value = stringValue(valueVal)
	} else {
		return entry, util.FmtNewtError(
			"setting %s does not have required value field", name)
	}
	if vals["type"] == nil {
		entry.SettingType = CFG_SETTING_TYPE_RAW
	} else {
		var ok bool
		typename := stringValue(vals["type"])
		entry.SettingType, ok = cfgSettingNameTypeMap[typename]
		if !ok {
			return entry, util.FmtNewtError(
				"setting %s specifies invalid type: %s", name, typename)
		}
	}
	entry.appendValue(lpkg, entry.Value)
	entry.Restrictions = []CfgRestriction{}
	restrictionStrings := cast.ToStringSlice(vals["restrictions"])
	for _, rstring := range restrictionStrings {
		r, err := readRestriction(name, rstring)
		if err != nil {
			return entry,
				util.PreNewtError(err, "error parsing setting %s", name)
		}
		entry.Restrictions = append(entry.Restrictions, r)
	}
	if vals["choices"] != nil && vals["range"] != nil {
		return entry, util.FmtNewtError(
			"setting %s uses both choice and range restrictions", name)
	}
	if vals["choices"] != nil {
		choices := cast.ToStringSlice(vals["choices"])
		entry.ValidChoices = choices
		sort.Slice(choices, func(a, b int) bool {
			return strings.ToLower(choices[a]) < strings.ToLower(choices[b])
		})
		for i, choice := range choices {
			if !cfgChoiceValRe.MatchString(choice) {
				return entry, util.FmtNewtError(
					"setting %s has invalid choice defined (%s) - "+
						"only letters, numbers and underscore are allowed", name, choice)
			}
			if i > 0 && strings.ToLower(choices[i-1]) == strings.ToLower(choice) {
				return entry, util.FmtNewtError(
					"setting %s has duplicated choice defined ('%s' and '%s')",
					name, choice, choices[i-1])
			}
		}
		r := CfgRestriction{
			BaseSetting: name,
			Code:        CFG_RESTRICTION_CODE_CHOICE,
		}
		entry.Restrictions = append(entry.Restrictions, r)
	}
	if vals["range"] != nil {
		r, err := createRangeRestriction(name, stringValue(vals["range"]))
		if err != nil {
			return entry,
				util.PreNewtError(err, "error parsing setting %s", name)
		}
		entry.Restrictions = append(entry.Restrictions, r)
	}
	return entry, nil
}
func (cfg *Cfg) addRedefine(settingName string,
	pkg1 *pkg.LocalPackage, pkg2 *pkg.LocalPackage) {
	if cfg.Redefines[settingName] == nil {
		cfg.Redefines[settingName] = map[*pkg.LocalPackage]struct{}{}
	}
	log.Debugf("Detected multiple definitions of the same setting: "+
		"setting=%s pkg1=%s pkg2=%s",
		settingName, pkg1.FullName(), pkg2.FullName())
	cfg.Redefines[settingName][pkg1] = struct{}{}
	cfg.Redefines[settingName][pkg2] = struct{}{}
}
func (cfg *Cfg) readDefsOnce(lpkg *pkg.LocalPackage,
	settings map[string]string) error {
	yc := lpkg.SyscfgY
	lsettings := cfg.settingsForLpkg(lpkg, settings)
	defs, err := yc.GetValStringMap("syscfg.defs", lsettings)
	util.OneTimeWarningError(err)
	if defs != nil {
		for k, v := range defs {
			vals, ok := v.(map[interface{}]interface{})
			if !ok {
				return util.FmtNewtError("Package \"%s\" contains invalid "+
					"\"syscfg.defs\" map; expected full setting definition, "+
					"but setting \"%s\" specifies a single value.  "+
					"Did you mix up \"syscfg.defs\" and \"syscfg.vals\"?",
					lpkg.FullName(), k)
			}
			entry, err := readSetting(k, lpkg, vals)
			if err != nil {
				return util.FmtNewtError("Config for package %s: %s",
					lpkg.FullName(), err.Error())
			}
			replace := true
			if oldEntry, exists := cfg.Settings[k]; exists {
				point := mostRecentPoint(oldEntry)
				if point.IsInjected() {
					entry.replaceSource(lpkg)
					entry.Value = oldEntry.Value
				} else {
					replace = false
					cfg.addRedefine(k, oldEntry.PackageDef, lpkg)
				}
			}
			if replace {
				cfg.Settings[k] = entry
			}
		}
	}
	return nil
}
func (cfg *Cfg) addOrphan(settingName string, value string,
	lpkg *pkg.LocalPackage) {
	cfg.Orphans[settingName] = append(cfg.Orphans[settingName], CfgPoint{
		Value:  value,
		Source: lpkg,
	})
}
func (cfg *Cfg) readRestrictions(lpkg *pkg.LocalPackage,
	settings map[string]string) error {
	yc := lpkg.SyscfgY
	lsettings := cfg.settingsForLpkg(lpkg, settings)
	restrictionStrings, err := yc.GetValStringSlice("syscfg.restrictions", lsettings)
	util.OneTimeWarningError(err)
	for _, rstring := range restrictionStrings {
		r, err := readRestriction("", rstring)
		if err != nil {
			return util.PreNewtError(err, "error parsing restriction: %s", rstring)
		}
		cfg.PackageRestrictions[lpkg.Name()] =
			append(cfg.PackageRestrictions[lpkg.Name()], r)
	}
	return nil
}
func (cfg *Cfg) readValsOnce(lpkg *pkg.LocalPackage,
	settings map[string]string) error {
	yc := lpkg.SyscfgY
	lsettings := cfg.settingsForLpkg(lpkg, settings)
	values, err := yc.GetValStringMap("syscfg.vals", lsettings)
	util.OneTimeWarningError(err)
	for k, v := range values {
		switch v.(type) {
		case map[interface{}]interface{}, []interface{}:
			return util.FmtNewtError("Package \"%s\" contains invalid "+
				"\"syscfg.vals\" map; expected single value, but setting "+
				"\"%s\" specifies multiple values.  Did you mix up "+
				"\"syscfg.defs\" and \"syscfg.vals\"?",
				lpkg.FullName(), k)
		}
		entry, ok := cfg.Settings[k]
		if ok {
			entry.appendValue(lpkg, v)
			cfg.Settings[k] = entry
		} else {
			cfg.addOrphan(k, stringValue(v), lpkg)
		}
		switch entry.State {
		case CFG_SETTING_STATE_DEPRECATED:
			cfg.Deprecated[k] = struct{}{}
		case CFG_SETTING_STATE_DEFUNCT:
			cfg.Defunct[k] = struct{}{}
		}
	}
	return nil
}
func (cfg *Cfg) Log() {
	keys := make([]string, len(cfg.Settings))
	i := 0
	for k, _ := range cfg.Settings {
		keys[i] = k
		i++
	}
	sort.Strings(keys)
	log.Debugf("syscfg settings (%d entries):", len(cfg.Settings))
	for _, k := range keys {
		entry := cfg.Settings[k]
		log.Debugf("    %s=%s %s", k, entry.Value,
			historyToString(entry.History))
	}
}
func (cfg *Cfg) DeletePkg(lpkg *pkg.LocalPackage) {
	log.Debugf("Deleting package from syscfg: %s", lpkg.FullName())
	for name, m := range cfg.Redefines {
		delete(m, lpkg)
		if len(m) == 1 {
			delete(cfg.Redefines, name)
			var source *pkg.LocalPackage
			for lp, _ := range m {
				source = lp
			}
			entry := cfg.Settings[name]
			entry.replaceSource(source)
			cfg.Settings[name] = entry
			log.Debugf("Resolved syscfg redefine; setting=%s pkg=%s",
				name, source.FullName())
		}
	}
	for name, entry := range cfg.Settings {
		if entry.PackageDef == lpkg {
			for i := 1; i < len(entry.History); i++ {
				p := entry.History[i]
				cfg.addOrphan(name, stringValue(p.Value), p.Source)
			}
			delete(cfg.Settings, name)
		} else {
			for i := 1; i < len(entry.History);   {
				if entry.History[i].Source == lpkg {
					entry.History = append(
						entry.History[:i], entry.History[i+1:]...)
					cfg.Settings[name] = entry
				} else {
					i++
				}
			}
		}
	}
}
func (cfg *Cfg) settingsOfType(typ CfgSettingType) []CfgEntry {
	entries := []CfgEntry{}
	for _, entry := range cfg.Settings {
		if entry.SettingType == typ {
			entries = append(entries, entry)
		}
	}
	return entries
}
func (cfg *Cfg) detectViolations() {
	settings := cfg.SettingValues()
	for _, entry := range cfg.Settings {
		var sv []CfgRestriction
		for _, r := range entry.Restrictions {
			if !cfg.restrictionMet(r, settings) {
				sv = append(sv, r)
			}
		}
		if sv != nil {
			cfg.SettingViolations[entry.Name] = sv
		}
	}
	pkgNames := make([]string, 0, len(cfg.PackageRestrictions))
	for n, _ := range cfg.PackageRestrictions {
		pkgNames = append(pkgNames, n)
	}
	sort.Strings(pkgNames)
	for _, n := range pkgNames {
		var pv []CfgRestriction
		for _, r := range cfg.PackageRestrictions[n] {
			if !cfg.restrictionMet(r, settings) {
				pv = append(pv, r)
			}
		}
		if pv != nil {
			cfg.PackageViolations[n] = pv
		}
	}
}