blob: 6e75a842dfc1e896a86171afff88cdd9ec6ef744 [file] [log] [blame]
/**
* 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 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/flash"
"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/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
)
const SYSCFG_PRIO_ANY = "any"
// Reserve last 16 priorities for the system (sanity, idle).
const SYSCFG_TASK_PRIO_MAX = 0xef
var cfgSettingNameTypeMap = map[string]CfgSettingType{
"raw": CFG_SETTING_TYPE_RAW,
"task_priority": CFG_SETTING_TYPE_TASK_PRIO,
"flash_owner": CFG_SETTING_TYPE_FLASH_OWNER,
}
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
PackageDef *pkg.LocalPackage
History []CfgPoint
State CfgSettingState
}
type CfgPriority struct {
SettingName string
PackageDef *pkg.LocalPackage // package that define the setting.
PackageSrc *pkg.LocalPackage // package overriding setting value.
}
type CfgFlashConflictCode int
const (
CFG_FLASH_CONFLICT_CODE_BAD_NAME CfgFlashConflictCode = iota
CFG_FLASH_CONFLICT_CODE_NOT_UNIQUE
)
type CfgFlashConflict struct {
SettingNames []string
Code CfgFlashConflictCode
}
type Cfg struct {
Settings map[string]CfgEntry
// Restrictions at the package level (i.e. "syscfg.restrictions").
PackageRestrictions map[string][]CfgRestriction
//// Errors
// Overrides of undefined settings.
Orphans map[string][]CfgPoint
// Two packages of equal priority override a setting with different
// values; not overridden by higher priority package.
Ambiguities map[string][]CfgPoint
// Setting-level restrictions not met.
SettingViolations map[string][]CfgRestriction
// Package-level restrictions not met.
PackageViolations map[string][]CfgRestriction
// Attempted override by bottom-priority packages (libraries).
PriorityViolations []CfgPriority
// Two or more flash areas overlap.
FlashConflicts []CfgFlashConflict
// Multiple packages defining the same setting.
// [setting-name][defining-package][{}]
Redefines map[string]map[*pkg.LocalPackage]struct{}
// Use of deprecated settings (warning).
Deprecated map[string]struct{}
// Use of defunct settings (error).
Defunct map[string]struct{}
// Unresolved value references
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 (cfg *Cfg) SettingValues() map[string]string {
values := make(map[string]string, len(cfg.Settings))
for k, v := range cfg.Settings {
values[k] = v.Value
}
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
}
newName := m[1]
// TODO we may try to resolve nested references...
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
entry.Value = "0"
cfg.UnresolvedValueRefs[k] = struct{}{}
}
cfg.Settings[k] = entry
}
}
// If the specified package has any injected settings, returns a new map
// consisting of the union of the injected settings and the provided base
// settings.
//
// Else (no injected settings), returns the base settings unmodified.
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
}
// Replaces the source (defining) package in a syscfg entry.
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 either setting is injected, there is no ambiguity
if cur.Source == nil || next.Source == nil {
break
}
// If the two package have different priorities, there is no ambiguity.
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 all values are identical, there is no ambiguity
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
}
// Detects all priority violations in an entry and returns them in a slice.
func (entry *CfgEntry) priorityViolations() []CfgPriority {
var violations []CfgPriority
for i := 1; i < len(entry.History); i++ {
p := entry.History[i]
// Overrides must come from a higher priority package, with one
// exception: a package can override its own setting.
curType := normalizePkgType(p.Source.Type())
defType := normalizePkgType(entry.PackageDef.Type())
if p.Source != entry.PackageDef && curType <= defType {
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
}
// The value field for setting definition is required.
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)
}
return entry, nil
}
// Records a redefinition error (two packages defining the same setting).
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 := yc.GetValStringMap("syscfg.defs", lsettings)
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 {
// Setting already defined. This is allowed for injected
// settings (settings automatically populated by newt). For
// all other settings, record a redefinition error.
point := mostRecentPoint(oldEntry)
if point.IsInjected() {
// Indicate that the setting came from this package, not
// from newt.
entry.replaceSource(lpkg)
// Keep the injected value (override the default).
entry.Value = oldEntry.Value
} else {
replace = false
cfg.addRedefine(k, oldEntry.PackageDef, lpkg)
}
}
if replace {
// Not an illegal redefine; populate the master settings list
// with the new entry.
cfg.Settings[k] = entry
}
}
}
return nil
}
// Records an orphan override error (override of undefined setting).
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 := yc.GetValStringSlice("syscfg.restrictions", lsettings)
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 := yc.GetValStringMap("syscfg.vals", lsettings)
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))
}
}
// Removes all traces of a package from syscfg.
func (cfg *Cfg) DeletePkg(lpkg *pkg.LocalPackage) {
log.Debugf("Deleting package from syscfg: %s", lpkg.FullName())
// First, check if the specified package contributes to a redefinition
// error. If so, the deletion of this package might resolve the error.
for name, m := range cfg.Redefines {
delete(m, lpkg)
if len(m) == 1 {
// Only one package defines this setting now; the redefinition
// error is resolved.
delete(cfg.Redefines, name)
//Loop through the map `m` just to get access to its only element.
var source *pkg.LocalPackage
for lp, _ := range m {
source = lp
}
// Replace the setting's source with the one remaining package that
// defines the setting.
entry := cfg.Settings[name]
entry.replaceSource(source)
// Update the master settings map.
cfg.Settings[name] = entry
log.Debugf("Resolved syscfg redefine; setting=%s pkg=%s",
name, source.FullName())
}
}
// Next, delete the specified package from the master settings map.
for name, entry := range cfg.Settings {
if entry.PackageDef == lpkg {
// The package-to-delete is this setting's source. All overrides
// become orphans.
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 {
// Remove any overrides created by the deleted package.
for i := 1; i < len(entry.History); /* i inc. in loop body */ {
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
}
}
}
// Detects all priority violations in the syscfg and records them internally.
func (cfg *Cfg) detectPriorityViolations() {
for _, entry := range cfg.Settings {
cfg.PriorityViolations = append(
cfg.PriorityViolations, entry.priorityViolations()...)
}
}
// Detects all flash conflict errors in the syscfg and records them internally.
func (cfg *Cfg) detectFlashConflicts(flashMap flash.FlashMap) {
entries := cfg.settingsOfType(CFG_SETTING_TYPE_FLASH_OWNER)
areaEntryMap := map[string][]CfgEntry{}
for _, entry := range entries {
if entry.Value != "" {
area, ok := flashMap.Areas[entry.Value]
if !ok {
conflict := CfgFlashConflict{
SettingNames: []string{entry.Name},
Code: CFG_FLASH_CONFLICT_CODE_BAD_NAME,
}
cfg.FlashConflicts = append(cfg.FlashConflicts, conflict)
} else {
areaEntryMap[area.Name] =
append(areaEntryMap[area.Name], entry)
}
}
}
// Settings with type flash_owner must have unique values.
for _, entries := range areaEntryMap {
if len(entries) > 1 {
conflict := CfgFlashConflict{
SettingNames: []string{},
Code: CFG_FLASH_CONFLICT_CODE_NOT_UNIQUE,
}
for _, entry := range entries {
conflict.SettingNames =
append(conflict.SettingNames, entry.Name)
}
cfg.FlashConflicts = append(cfg.FlashConflicts, conflict)
}
}
}
func (cfg *Cfg) flashConflictErrorText(conflict CfgFlashConflict) string {
entry := cfg.Settings[conflict.SettingNames[0]]
switch conflict.Code {
case CFG_FLASH_CONFLICT_CODE_BAD_NAME:
return fmt.Sprintf("Setting %s specifies unknown flash area: %s\n",
entry.Name, entry.Value)
case CFG_FLASH_CONFLICT_CODE_NOT_UNIQUE:
return fmt.Sprintf(
"Multiple flash_owner settings specify the same flash area\n"+
" settings: %s\n"+
" flash area: %s\n",
strings.Join(conflict.SettingNames, ", "),
entry.Value)
default:
panic("Invalid flash conflict code: " + string(conflict.Code))
}
}
func historyTextOnce(settingName string, points []CfgPoint) string {
return fmt.Sprintf(" %s: %s\n", settingName, historyToString(points))
}
func historyText(historyMap map[string][]CfgPoint) string {
if len(historyMap) == 0 {
return ""
}
str := "Setting history (newest -> oldest):\n"
names := make([]string, 0, len(historyMap))
for name, _ := range historyMap {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
points := historyMap[name]
str += historyTextOnce(name, points)
}
return str
}
func (cfg *Cfg) ErrorText() string {
str := ""
historyMap := map[string][]CfgPoint{}
// Redefinition errors.
if len(cfg.Redefines) > 0 {
settingNames := make([]string, len(cfg.Redefines))
i := 0
for k, _ := range cfg.Redefines {
settingNames[i] = k
i++
}
sort.Strings(settingNames)
str += "Settings defined by multiple packages:"
for _, n := range settingNames {
pkgNames := make([]string, len(cfg.Redefines[n]))
i := 0
for p, _ := range cfg.Redefines[n] {
pkgNames[i] = p.FullName()
i++
}
sort.Strings(pkgNames)
str += fmt.Sprintf("\n %s:", n)
for _, pn := range pkgNames {
str += " " + pn
}
}
}
// Violation errors.
if len(cfg.SettingViolations) > 0 || len(cfg.PackageViolations) > 0 {
str += "Syscfg restriction violations detected:\n"
for settingName, rslice := range cfg.SettingViolations {
baseEntry := cfg.Settings[settingName]
historyMap[settingName] = baseEntry.History
for _, r := range rslice {
for _, name := range r.relevantSettingNames() {
reqEntry := cfg.Settings[name]
historyMap[name] = reqEntry.History
}
str += " " + cfg.settingViolationText(baseEntry, r) + "\n"
}
}
for pkgName, rslice := range cfg.PackageViolations {
for _, r := range rslice {
for _, name := range r.relevantSettingNames() {
reqEntry := cfg.Settings[name]
historyMap[name] = reqEntry.History
}
str += " " + cfg.packageViolationText(pkgName, r) + "\n"
}
}
}
// Ambiguity errors.
if len(cfg.Ambiguities) > 0 {
str += "Syscfg ambiguities detected:\n"
settingNames := make([]string, 0, len(cfg.Ambiguities))
for k, _ := range cfg.Ambiguities {
settingNames = append(settingNames, k)
}
sort.Strings(settingNames)
for _, name := range settingNames {
entry := cfg.Settings[name]
historyMap[entry.Name] = entry.History
str += " " + entry.ambiguityText()
}
}
// Priority violation errors.
if len(cfg.PriorityViolations) > 0 {
str += "Priority violations detected (Packages can only override " +
"settings defined by packages of lower priority):\n"
for _, priority := range cfg.PriorityViolations {
entry := cfg.Settings[priority.SettingName]
historyMap[priority.SettingName] = entry.History
str += fmt.Sprintf(
" Package: %s overriding setting: %s defined by %s\n",
priority.PackageSrc.FullName(), priority.SettingName,
priority.PackageDef.FullName())
}
}
// Flash conflicts.
if len(cfg.FlashConflicts) > 0 {
str += "Flash errors detected:\n"
for _, conflict := range cfg.FlashConflicts {
for _, name := range conflict.SettingNames {
entry := cfg.Settings[name]
historyMap[name] = entry.History
}
str += " " + cfg.flashConflictErrorText(conflict)
}
}
// Overrides of defunct settings.
if len(cfg.Defunct) > 0 {
str += "Override of defunct settings detected:\n"
for name, _ := range cfg.Defunct {
entry := cfg.Settings[name]
str += " " + fmt.Sprintf("%s: %s\n", name, entry.Description)
historyMap[name] = entry.History
}
}
// Unresolved value references
if len(cfg.UnresolvedValueRefs) > 0 {
str += "Unresolved value references:\n"
for name, _ := range cfg.UnresolvedValueRefs {
entry := cfg.Settings[name]
str += " " + fmt.Sprintf("%s -> %s\n", name, entry.ValueRefName)
historyMap[name] = entry.History
}
}
if len(historyMap) > 0 {
str += "\n" + historyText(historyMap)
}
return str
}
func (cfg *Cfg) WarningText() string {
str := ""
historyMap := map[string][]CfgPoint{}
if len(cfg.Orphans) > 0 {
settingNames := make([]string, len(cfg.Orphans))
i := 0
for k, _ := range cfg.Orphans {
settingNames[i] = k
i++
}
sort.Strings(settingNames)
str += "Ignoring override of undefined settings:"
for _, n := range settingNames {
historyMap[n] = cfg.Orphans[n]
str += fmt.Sprintf("\n %s", n)
}
}
if len(historyMap) > 0 {
str += "\n" + historyText(historyMap)
}
return str
}
func (cfg *Cfg) DeprecatedWarning() []string {
lines := []string{}
for k, _ := range cfg.Deprecated {
entry, ok := cfg.Settings[k]
if !ok {
log.Errorf("Internal error; deprecated setting \"%s\" not in cfg",
k)
}
point := mostRecentPoint(entry)
lines = append(lines,
fmt.Sprintf("Use of deprecated setting %s in %s: %s", k,
point.Source.FullName(), entry.Description))
}
return lines
}
func escapeStr(s string) string {
return strings.ToUpper(util.CIdentifier(s))
}
func settingName(setting string) string {
return SYSCFG_PREFIX_SETTING + escapeStr(setting)
}
func normalizePkgType(typ interfaces.PackageType) interfaces.PackageType {
switch typ {
case pkg.PACKAGE_TYPE_TARGET:
return pkg.PACKAGE_TYPE_TARGET
case pkg.PACKAGE_TYPE_APP:
return pkg.PACKAGE_TYPE_APP
case pkg.PACKAGE_TYPE_UNITTEST:
return pkg.PACKAGE_TYPE_UNITTEST
case pkg.PACKAGE_TYPE_BSP:
return pkg.PACKAGE_TYPE_BSP
default:
return pkg.PACKAGE_TYPE_LIB
}
}
func categorizePkgs(
lpkgs []*pkg.LocalPackage) map[interfaces.PackageType][]*pkg.LocalPackage {
pmap := map[interfaces.PackageType][]*pkg.LocalPackage{
pkg.PACKAGE_TYPE_TARGET: []*pkg.LocalPackage{},
pkg.PACKAGE_TYPE_APP: []*pkg.LocalPackage{},
pkg.PACKAGE_TYPE_UNITTEST: []*pkg.LocalPackage{},
pkg.PACKAGE_TYPE_BSP: []*pkg.LocalPackage{},
pkg.PACKAGE_TYPE_LIB: []*pkg.LocalPackage{},
}
for _, lpkg := range lpkgs {
typ := normalizePkgType(lpkg.Type())
pmap[typ] = append(pmap[typ], lpkg)
}
for k, v := range pmap {
pmap[k] = pkg.SortLclPkgs(v)
}
return pmap
}
func (cfg *Cfg) readDefsForPkgType(lpkgs []*pkg.LocalPackage,
settings map[string]string) error {
for _, lpkg := range lpkgs {
if err := cfg.readDefsOnce(lpkg, settings); err != nil {
return err
}
}
return nil
}
func (cfg *Cfg) readValsForPkgType(lpkgs []*pkg.LocalPackage,
settings map[string]string) error {
for _, lpkg := range lpkgs {
if err := cfg.readValsOnce(lpkg, settings); err != nil {
return err
}
}
return nil
}
func (cfg *Cfg) detectAmbiguities() {
for _, entry := range cfg.Settings {
if points := entry.ambiguities(); len(points) > 0 {
cfg.Ambiguities[entry.Name] = points
}
}
}
// Detects and records errors in the build's syscfg. This should only be
// called after APIs are resolved to avoid false positives.
func (cfg *Cfg) DetectErrors(flashMap flash.FlashMap) {
cfg.detectAmbiguities()
cfg.detectViolations()
cfg.detectPriorityViolations()
cfg.detectFlashConflicts(flashMap)
}
func Read(lpkgs []*pkg.LocalPackage, apis []string,
injectedSettings map[string]string, settings map[string]string,
flashMap flash.FlashMap) (Cfg, error) {
cfg := NewCfg()
for k, v := range injectedSettings {
cfg.Settings[k] = CfgEntry{
Name: k,
Description: "Injected setting",
Value: v,
History: []CfgPoint{{
Value: v,
Source: nil,
}},
}
settings[k] = v
}
// Read system configuration files. In case of conflicting settings, the
// higher priority pacakge's setting wins. Package priorities are assigned
// as follows (highest priority first):
// * target
// * app (if present)
// * unittest (if no app)
// * bsp
// * everything else (lib, sdk, compiler)
lpkgMap := categorizePkgs(lpkgs)
for _, ptype := range []interfaces.PackageType{
pkg.PACKAGE_TYPE_LIB,
pkg.PACKAGE_TYPE_BSP,
pkg.PACKAGE_TYPE_UNITTEST,
pkg.PACKAGE_TYPE_APP,
pkg.PACKAGE_TYPE_TARGET,
} {
if err := cfg.readDefsForPkgType(lpkgMap[ptype], settings); err != nil {
return cfg, err
}
}
for _, ptype := range []interfaces.PackageType{
pkg.PACKAGE_TYPE_LIB,
pkg.PACKAGE_TYPE_BSP,
pkg.PACKAGE_TYPE_UNITTEST,
pkg.PACKAGE_TYPE_APP,
pkg.PACKAGE_TYPE_TARGET,
} {
if err := cfg.readValsForPkgType(lpkgMap[ptype], settings); err != nil {
return cfg, err
}
}
for _, lpkg := range lpkgs {
if err := cfg.readRestrictions(lpkg, settings); err != nil {
return cfg, err
}
}
return cfg, nil
}
func mostRecentPoint(entry CfgEntry) CfgPoint {
if len(entry.History) == 0 {
panic("invalid cfg entry; len(history) == 0")
}
return entry.History[len(entry.History)-1]
}
func calcPriorities(cfg Cfg, settingType CfgSettingType, max int,
allowDups bool) error {
// setting-name => entry
anyEntries := map[string]CfgEntry{}
// priority-value => entry
valEntries := map[int]CfgEntry{}
for name, entry := range cfg.Settings {
if entry.SettingType == settingType {
if entry.Value == SYSCFG_PRIO_ANY {
anyEntries[name] = entry
} else {
prio, err := util.AtoiNoOct(entry.Value)
if err != nil {
return util.FmtNewtError(
"invalid priority value: setting=%s value=%s pkg=%s",
name, entry.Value, entry.History[0].Name())
}
if prio > max {
return util.FmtNewtError(
"invalid priority value: value too great (> %d); "+
"setting=%s value=%s pkg=%s",
max, entry.Name, entry.Value,
mostRecentPoint(entry).Name())
}
if !allowDups {
if oldEntry, ok := valEntries[prio]; ok {
return util.FmtNewtError(
"duplicate priority value: setting1=%s "+
"setting2=%s pkg1=%s pkg2=%s value=%s",
oldEntry.Name, entry.Name,
oldEntry.History[0].Name(),
entry.History[0].Name(), entry.Value)
}
}
valEntries[prio] = entry
}
}
}
greatest := 0
for prio, _ := range valEntries {
if prio > greatest {
greatest = prio
}
}
anyNames := make([]string, 0, len(anyEntries))
for name, _ := range anyEntries {
anyNames = append(anyNames, name)
}
sort.Strings(anyNames)
for _, name := range anyNames {
entry := anyEntries[name]
greatest++
if greatest > max {
return util.FmtNewtError("could not assign 'any' priority: "+
"value too great (> %d); setting=%s value=%s pkg=%s",
max, name, greatest,
mostRecentPoint(entry).Name())
}
entry.Value = strconv.Itoa(greatest)
cfg.Settings[name] = entry
}
return nil
}
func writeCheckMacros(w io.Writer) {
s := `/**
* This macro exists to ensure code includes this header when needed. If code
* checks the existence of a setting directly via ifdef without including this
* header, the setting macro will silently evaluate to 0. In contrast, an
* attempt to use these macros without including this header will result in a
* compiler error.
*/
#define MYNEWT_VAL(x) MYNEWT_VAL_ ## x
`
fmt.Fprintf(w, "%s\n", s)
}
func writeComment(entry CfgEntry, w io.Writer) {
if len(entry.History) > 1 {
fmt.Fprintf(w, "/* Overridden by %s (defined by %s) */\n",
mostRecentPoint(entry).Name(),
entry.History[0].Name())
}
if len(entry.ValueRefName) > 1 {
fmt.Fprintf(w, "/* Value copied from %s */\n",
entry.ValueRefName)
}
}
func writeDefine(key string, value string, w io.Writer) {
if value == "" {
fmt.Fprintf(w, "#undef %s\n", key)
} else {
fmt.Fprintf(w, "#ifndef %s\n", key)
fmt.Fprintf(w, "#define %s (%s)\n", key, value)
fmt.Fprintf(w, "#endif\n")
}
}
func EntriesByPkg(cfg Cfg) map[string][]CfgEntry {
pkgEntries := map[string][]CfgEntry{}
for _, v := range cfg.Settings {
name := v.History[0].Name()
pkgEntries[name] = append(pkgEntries[name], v)
}
return pkgEntries
}
func writeSettingsOnePkg(cfg Cfg, pkgName string, pkgEntries []CfgEntry,
w io.Writer) {
names := make([]string, len(pkgEntries), len(pkgEntries))
for i, entry := range pkgEntries {
names[i] = entry.Name
}
if len(names) == 0 {
return
}
sort.Strings(names)
fmt.Fprintf(w, "/*** %s */\n", pkgName)
first := true
for _, n := range names {
entry := cfg.Settings[n]
if first {
first = false
} else {
fmt.Fprintf(w, "\n")
}
writeComment(entry, w)
writeDefine(settingName(n), entry.Value, w)
}
}
func writeSettings(cfg Cfg, w io.Writer) {
// Group settings by package name so that the generated header file is
// easier to read.
pkgEntries := EntriesByPkg(cfg)
pkgNames := make([]string, 0, len(pkgEntries))
for name, _ := range pkgEntries {
pkgNames = append(pkgNames, name)
}
sort.Strings(pkgNames)
for _, name := range pkgNames {
fmt.Fprintf(w, "\n")
entries := pkgEntries[name]
writeSettingsOnePkg(cfg, name, entries, w)
}
}
func write(cfg Cfg, w io.Writer) {
fmt.Fprintf(w, newtutil.GeneratedPreamble())
fmt.Fprintf(w, "#ifndef H_MYNEWT_SYSCFG_\n")
fmt.Fprintf(w, "#define H_MYNEWT_SYSCFG_\n\n")
writeCheckMacros(w)
fmt.Fprintf(w, "\n")
writeSettings(cfg, w)
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "#endif\n")
}
func EnsureWritten(cfg Cfg, includeDir string) error {
// XXX: Detect these problems at error text generation time.
if err := calcPriorities(cfg, CFG_SETTING_TYPE_TASK_PRIO,
SYSCFG_TASK_PRIO_MAX, false); err != nil {
return err
}
buf := bytes.Buffer{}
write(cfg, &buf)
path := includeDir + "/" + HEADER_PATH
writeReqd, err := util.FileContentsChanged(path, buf.Bytes())
if err != nil {
return err
}
if !writeReqd {
log.Debugf("syscfg unchanged; not writing header file (%s).", path)
return nil
}
log.Debugf("syscfg 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
}
func KeyValueFromStr(str string) (map[string]string, error) {
vals := map[string]string{}
if strings.TrimSpace(str) == "" {
return vals, nil
}
// Separate syscfg vals are delimited by ':'.
fields := strings.Split(str, ":")
// Key-value pairs are delimited by '='. If no '=' is present, assume the
// string is the key name and the value is 1.
for _, f := range fields {
if _, err := util.AtoiNoOct(f); err == nil {
return nil, util.FmtNewtError(
"Invalid setting name \"%s\"; must not be a number", f)
}
kv := strings.SplitN(f, "=", 2)
switch len(kv) {
case 1:
vals[f] = "1"
case 2:
vals[kv[0]] = kv[1]
}
}
return vals, nil
}
func KeyValueToStr(syscfgKv map[string]string) string {
str := ""
names := make([]string, 0, len(syscfgKv))
for k, _ := range syscfgKv {
names = append(names, k)
}
sort.Strings(names)
for i, name := range names {
if i != 0 {
str += ":"
}
str += fmt.Sprintf("%s=%s", name, syscfgKv[name])
}
return str
}