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/01/29 18:15:54 UTC

[GitHub] ccollins476ad closed pull request #122: Code for parsing and evaluating syscfg expressions

ccollins476ad closed pull request #122:     Code for parsing and evaluating syscfg expressions
URL: https://github.com/apache/mynewt-newt/pull/122
 
 
   

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/buildpackage.go b/newt/builder/buildpackage.go
index e82566fb..7c9ed561 100644
--- a/newt/builder/buildpackage.go
+++ b/newt/builder/buildpackage.go
@@ -128,20 +128,17 @@ func (bpkg *BuildPackage) CompilerInfo(
 	}
 
 	ci := toolchain.NewCompilerInfo()
-	features := b.cfg.FeaturesForLpkg(bpkg.rpkg.Lpkg)
+	settings := b.cfg.SettingsForLpkg(bpkg.rpkg.Lpkg)
 
 	// Read each set of flags and expand repo designators ("@<repo-nme>") into
 	// paths.
-	ci.Cflags = newtutil.GetStringSliceFeatures(bpkg.rpkg.Lpkg.PkgV, features,
-		"pkg.cflags")
+	ci.Cflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.cflags", settings)
 	expandFlags(ci.Cflags)
 
-	ci.Lflags = newtutil.GetStringSliceFeatures(bpkg.rpkg.Lpkg.PkgV, features,
-		"pkg.lflags")
+	ci.Lflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.lflags", settings)
 	expandFlags(ci.Lflags)
 
-	ci.Aflags = newtutil.GetStringSliceFeatures(bpkg.rpkg.Lpkg.PkgV, features,
-		"pkg.aflags")
+	ci.Aflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.aflags", settings)
 	expandFlags(ci.Aflags)
 
 	// Package-specific injected settings get specified as C flags on the
@@ -151,8 +148,7 @@ func (bpkg *BuildPackage) CompilerInfo(
 	}
 
 	ci.IgnoreFiles = []*regexp.Regexp{}
-	ignPats := newtutil.GetStringSliceFeatures(bpkg.rpkg.Lpkg.PkgV,
-		features, "pkg.ign_files")
+	ignPats := bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.ign_files", settings)
 	for _, str := range ignPats {
 		re, err := regexp.Compile(str)
 		if err != nil {
@@ -163,8 +159,7 @@ func (bpkg *BuildPackage) CompilerInfo(
 	}
 
 	ci.IgnoreDirs = []*regexp.Regexp{}
-	ignPats = newtutil.GetStringSliceFeatures(bpkg.rpkg.Lpkg.PkgV,
-		features, "pkg.ign_dirs")
+	ignPats = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.ign_dirs", settings)
 	for _, str := range ignPats {
 		re, err := regexp.Compile(str)
 		if err != nil {
@@ -174,9 +169,8 @@ func (bpkg *BuildPackage) CompilerInfo(
 		ci.IgnoreDirs = append(ci.IgnoreDirs, re)
 	}
 
-	bpkg.SourceDirectories = newtutil.GetStringSliceFeatures(
-		bpkg.rpkg.Lpkg.PkgV,
-		features, "pkg.src_dirs")
+	bpkg.SourceDirectories = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice(
+		"pkg.src_dirs", settings)
 
 	includePaths, err := bpkg.recursiveIncludePaths(b)
 	if err != nil {
diff --git a/newt/builder/buildutil.go b/newt/builder/buildutil.go
index 61e5b653..113cbfde 100644
--- a/newt/builder/buildutil.go
+++ b/newt/builder/buildutil.go
@@ -26,6 +26,7 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 
+	"mynewt.apache.org/newt/newt/parse"
 	"mynewt.apache.org/newt/newt/resolve"
 )
 
@@ -36,10 +37,12 @@ func TestTargetName(testPkgName string) string {
 func (b *Builder) FeatureString() string {
 	var buffer bytes.Buffer
 
-	featureMap := b.cfg.Features()
-	featureSlice := make([]string, 0, len(featureMap))
-	for k, _ := range featureMap {
-		featureSlice = append(featureSlice, k)
+	settingMap := b.cfg.SettingValues()
+	featureSlice := make([]string, 0, len(settingMap))
+	for k, v := range settingMap {
+		if parse.ValueIsTrue(v) {
+			featureSlice = append(featureSlice, k)
+		}
 	}
 	sort.Strings(featureSlice)
 
diff --git a/newt/builder/cmake.go b/newt/builder/cmake.go
index af26011c..4d02fcb5 100644
--- a/newt/builder/cmake.go
+++ b/newt/builder/cmake.go
@@ -41,7 +41,7 @@ func CmakeListsPath() string {
 }
 
 func EscapeName(name string) string {
-	return strings.Replace(name, "/","_", -1)
+	return strings.Replace(name, "/", "_", -1)
 }
 
 func CmakeSourceObjectWrite(w io.Writer, cj toolchain.CompilerJob) {
@@ -200,7 +200,7 @@ func (t *TargetBuilder) CMakeTargetBuilderWrite(w io.Writer, targetCompiler *too
 
 	targetCompiler.LinkerScripts = t.bspPkg.LinkerScripts
 
-	if err := t.bspPkg.Reload(t.AppBuilder.cfg.Features()); err != nil {
+	if err := t.bspPkg.Reload(t.AppBuilder.cfg.SettingValues()); err != nil {
 		return err
 	}
 
diff --git a/newt/builder/depgraph.go b/newt/builder/depgraph.go
index 5dba2579..ba12d3ce 100644
--- a/newt/builder/depgraph.go
+++ b/newt/builder/depgraph.go
@@ -22,6 +22,7 @@ package builder
 import (
 	"bytes"
 	"fmt"
+	"strings"
 
 	"mynewt.apache.org/newt/newt/resolve"
 )
@@ -111,8 +112,18 @@ func revdepGraph(rs *resolve.ResolveSet) (DepGraph, error) {
 
 func depString(dep *resolve.ResolveDep) string {
 	s := fmt.Sprintf("%s", dep.Rpkg.Lpkg.FullName())
+
+	var reasons []string
 	if dep.Api != "" {
-		s += fmt.Sprintf("(api:%s)", dep.Api)
+		reasons = append(reasons, fmt.Sprintf("api:%s", dep.Api))
+	}
+
+	if dep.Expr != "" {
+		reasons = append(reasons, fmt.Sprintf("syscfg:%s", dep.Expr))
+	}
+
+	if len(reasons) > 0 {
+		s += fmt.Sprintf("(%s)", strings.Join(reasons, " "))
 	}
 
 	return s
diff --git a/newt/builder/load.go b/newt/builder/load.go
index fafd4b80..1281f245 100644
--- a/newt/builder/load.go
+++ b/newt/builder/load.go
@@ -26,6 +26,7 @@ import (
 	"strconv"
 	"strings"
 
+	"mynewt.apache.org/newt/newt/parse"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/util"
@@ -116,10 +117,10 @@ func (b *Builder) Load(imageSlot int, extraJtagCmd string) error {
 	if extraJtagCmd != "" {
 		envSettings["EXTRA_JTAG_CMD"] = extraJtagCmd
 	}
-	features := b.cfg.Features()
+	settings := b.cfg.SettingValues()
 
 	var flashTargetArea string
-	if _, ok := features["BOOT_LOADER"]; ok {
+	if parse.ValueIsTrue(settings["BOOT_LOADER"]) {
 		envSettings["BOOT_LOADER"] = "1"
 
 		flashTargetArea = "FLASH_AREA_BOOTLOADER"
diff --git a/newt/builder/targetbuild.go b/newt/builder/targetbuild.go
index 8acb276f..cc2b6be7 100644
--- a/newt/builder/targetbuild.go
+++ b/newt/builder/targetbuild.go
@@ -192,9 +192,7 @@ func (t *TargetBuilder) validateAndWriteCfg() error {
 
 	warningText := strings.TrimSpace(t.res.WarningText())
 	if warningText != "" {
-		for _, line := range strings.Split(warningText, "\n") {
-			log.Debugf(line)
-		}
+		log.Debug(warningText)
 	}
 
 	if err := syscfg.EnsureWritten(t.res.Cfg,
@@ -312,7 +310,7 @@ func (t *TargetBuilder) buildLoader() error {
 	/* rebuild the loader */
 	project.ResetDeps(t.LoaderList)
 
-	if err := t.bspPkg.Reload(t.LoaderBuilder.cfg.Features()); err != nil {
+	if err := t.bspPkg.Reload(t.LoaderBuilder.cfg.SettingValues()); err != nil {
 		return err
 	}
 
@@ -357,7 +355,7 @@ func (t *TargetBuilder) Build() error {
 	/* Build the Apps */
 	project.ResetDeps(t.AppList)
 
-	if err := t.bspPkg.Reload(t.AppBuilder.cfg.Features()); err != nil {
+	if err := t.bspPkg.Reload(t.AppBuilder.cfg.SettingValues()); err != nil {
 		return err
 	}
 
@@ -572,7 +570,8 @@ func (t *TargetBuilder) createManifest() error {
 	for _, k := range keys {
 		manifest.TgtVars = append(manifest.TgtVars, k+"="+vars[k])
 	}
-	syscfgKV := t.GetTarget().Package().SyscfgV.GetStringMapString("syscfg.vals")
+	syscfgKV := t.GetTarget().Package().SyscfgY.GetValStringMapString(
+		"syscfg.vals", nil)
 	if len(syscfgKV) > 0 {
 		tgtSyscfg := fmt.Sprintf("target.syscfg=%s",
 			syscfg.KeyValueToStr(syscfgKV))
@@ -793,7 +792,7 @@ func (t *TargetBuilder) CreateImages(version string,
 			t.LoaderBuilder.AppHexPath(),
 			tgtArea.Offset)
 		err = c.ConvertBinToHex(t.LoaderBuilder.AppImgPath(),
-				t.LoaderBuilder.AppHexPath(), tgtArea.Offset)
+			t.LoaderBuilder.AppHexPath(), tgtArea.Offset)
 		if err != nil {
 			log.Errorf("Can't convert to hexfile %s\n", err.Error())
 		}
@@ -807,7 +806,7 @@ func (t *TargetBuilder) CreateImages(version string,
 	flashTargetArea := ""
 	if t.LoaderBuilder == nil {
 		flashTargetArea = flash.FLASH_AREA_NAME_IMAGE_0
-	} else  {
+	} else {
 		flashTargetArea = flash.FLASH_AREA_NAME_IMAGE_1
 	}
 	tgtArea := t.bspPkg.FlashMap.Areas[flashTargetArea]
@@ -817,7 +816,7 @@ func (t *TargetBuilder) CreateImages(version string,
 			t.AppBuilder.AppHexPath(),
 			tgtArea.Offset)
 		err = c.ConvertBinToHex(t.AppBuilder.AppImgPath(),
-				t.AppBuilder.AppHexPath(), tgtArea.Offset)
+			t.AppBuilder.AppHexPath(), tgtArea.Offset)
 		if err != nil {
 			log.Errorf("Can't convert to hexfile %s\n", err.Error())
 		}
diff --git a/newt/cli/run_cmds.go b/newt/cli/run_cmds.go
index 5ad80d1e..49a2e4d3 100644
--- a/newt/cli/run_cmds.go
+++ b/newt/cli/run_cmds.go
@@ -25,6 +25,7 @@ import (
 
 	"mynewt.apache.org/newt/newt/image"
 	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/parse"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -72,9 +73,11 @@ func runRunCmd(cmd *cobra.Command, args []string) {
 			if err != nil {
 				NewtUsage(nil, err)
 			}
-			features := res.Cfg.Features()
+			settings := res.Cfg.SettingValues()
+
+			if !parse.ValueIsTrue(settings["BOOT_LOADER"]) &&
+				!parse.ValueIsTrue(settings["BSP_SIMULATED"]) {
 
-			if !features["BOOT_LOADER"] && !features["BSP_SIMULATED"] {
 				version = "0"
 				fmt.Println("Enter image version(default 0):")
 				fmt.Scanf("%s\n", &version)
diff --git a/newt/cli/target_cmds.go b/newt/cli/target_cmds.go
index 5a29df40..282314ad 100644
--- a/newt/cli/target_cmds.go
+++ b/newt/cli/target_cmds.go
@@ -36,8 +36,8 @@ import (
 	"mynewt.apache.org/newt/newt/resolve"
 	"mynewt.apache.org/newt/newt/syscfg"
 	"mynewt.apache.org/newt/newt/target"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 )
 
 var targetForce bool = false
@@ -81,11 +81,11 @@ func targetContainsUserFiles(t *target.Target) (bool, error) {
 }
 
 func pkgVarSliceString(pack *pkg.LocalPackage, key string) string {
-	features := pack.PkgV.GetStringSlice(key)
-	sort.Strings(features)
+	vals := pack.PkgY.GetValStringSlice(key, nil)
+	sort.Strings(vals)
 	var buffer bytes.Buffer
-	for _, f := range features {
-		buffer.WriteString(f)
+	for _, v := range vals {
+		buffer.WriteString(v)
 		buffer.WriteString(" ")
 	}
 	return buffer.String()
@@ -95,7 +95,7 @@ func pkgVarSliceString(pack *pkg.LocalPackage, key string) string {
 func amendSysCfg(value string, t *target.Target) error {
 
 	// Get the current syscfg.vals name-value pairs
-	sysVals := t.Package().SyscfgV.GetStringMapString("syscfg.vals")
+	sysVals := t.Package().SyscfgY.GetValStringMapString("syscfg.vals", nil)
 
 	// Convert the input syscfg into name-value pairs
 	amendSysVals, err := syscfg.KeyValueFromStr(value)
@@ -119,14 +119,14 @@ func amendSysCfg(value string, t *target.Target) error {
 			sysVals = amendSysVals
 		}
 	}
-	t.Package().SyscfgV.Set("syscfg.vals", sysVals)
+	t.Package().SyscfgY.Replace("syscfg.vals", sysVals)
 	return nil
 }
 
 //Process amend command for aflags, cflags, and lflags target variables.
 func amendBuildFlags(kv []string, t *target.Target) error {
-	pkg_var := "pkg." + kv[0]
-	curFlags := t.Package().PkgV.GetStringSlice(pkg_var)
+	pkgVar := "pkg." + kv[0]
+	curFlags := t.Package().PkgY.GetValStringSlice(pkgVar, nil)
 	amendFlags := strings.Fields(kv[1])
 
 	newFlags := []string{}
@@ -164,7 +164,7 @@ func amendBuildFlags(kv []string, t *target.Target) error {
 			}
 		}
 	}
-	t.Package().PkgV.Set(pkg_var, newFlags)
+	t.Package().PkgY.Replace(pkgVar, newFlags)
 	return nil
 }
 
@@ -206,7 +206,7 @@ func targetShowCmd(cmd *cobra.Command, args []string) {
 
 		// A few variables come from the base package rather than the target.
 		kvPairs["syscfg"] = syscfg.KeyValueToStr(
-			target.Package().SyscfgV.GetStringMapString("syscfg.vals"))
+			target.Package().SyscfgY.GetValStringMapString("syscfg.vals", nil))
 		kvPairs["cflags"] = pkgVarSliceString(target.Package(), "pkg.cflags")
 		kvPairs["lflags"] = pkgVarSliceString(target.Package(), "pkg.lflags")
 		kvPairs["aflags"] = pkgVarSliceString(target.Package(), "pkg.aflags")
@@ -300,13 +300,13 @@ func targetSetCmd(cmd *cobra.Command, args []string) {
 		// A few variables are special cases; they get set in the base package
 		// instead of the target.
 		if kv[0] == "target.syscfg" {
-			t.Package().SyscfgV = viper.New()
+			t.Package().SyscfgY = ycfg.YCfg{}
 			kv, err := syscfg.KeyValueFromStr(kv[1])
 			if err != nil {
 				NewtUsage(cmd, err)
 			}
 
-			t.Package().SyscfgV.Set("syscfg.vals", kv)
+			t.Package().SyscfgY.Replace("syscfg.vals", kv)
 		} else if kv[0] == "target.cflags" ||
 			kv[0] == "target.lflags" ||
 			kv[0] == "target.aflags" {
@@ -314,9 +314,9 @@ func targetSetCmd(cmd *cobra.Command, args []string) {
 			kv[0] = "pkg." + strings.TrimPrefix(kv[0], "target.")
 			if kv[1] == "" {
 				// User specified empty value; delete variable.
-				t.Package().PkgV.Set(kv[0], nil)
+				t.Package().PkgY.Replace(kv[0], nil)
 			} else {
-				t.Package().PkgV.Set(kv[0], strings.Fields(kv[1]))
+				t.Package().PkgY.Replace(kv[0], strings.Fields(kv[1]))
 			}
 		} else {
 			if kv[1] == "" {
@@ -653,9 +653,7 @@ func targetBuilderConfigResolve(b *builder.TargetBuilder) *resolve.Resolution {
 
 	warningText := strings.TrimSpace(res.WarningText())
 	if warningText != "" {
-		for _, line := range strings.Split(warningText, "\n") {
-			log.Warn(line)
-		}
+		log.Warn(warningText)
 	}
 
 	return res
diff --git a/newt/cli/vars.go b/newt/cli/vars.go
index 7ed25de5..cba62703 100644
--- a/newt/cli/vars.go
+++ b/newt/cli/vars.go
@@ -26,6 +26,7 @@ import (
 	"strings"
 
 	"mynewt.apache.org/newt/newt/interfaces"
+	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/util"
@@ -57,7 +58,7 @@ func settingValues(settingName string) ([]string, error) {
 	packs := project.GetProject().PackagesOfType(-1)
 	for _, pack := range packs {
 		settings :=
-			pack.(*pkg.LocalPackage).PkgV.GetStringSlice(settingName)
+			pack.(*pkg.LocalPackage).PkgY.GetValStringSlice(settingName, nil)
 
 		for _, setting := range settings {
 			settingMap[setting] = struct{}{}
@@ -78,7 +79,7 @@ func buildProfileValues() ([]string, error) {
 
 	packs := project.GetProject().PackagesOfType(pkg.PACKAGE_TYPE_COMPILER)
 	for _, pack := range packs {
-		v, err := util.ReadConfig(pack.(*pkg.LocalPackage).BasePath(),
+		v, err := newtutil.ReadConfig(pack.(*pkg.LocalPackage).BasePath(),
 			"compiler")
 		if err != nil {
 			return nil, err
diff --git a/newt/compat/compat.go b/newt/compat/compat.go
index 2e4355fe..1e3c363c 100644
--- a/newt/compat/compat.go
+++ b/newt/compat/compat.go
@@ -27,8 +27,8 @@ import (
 	"github.com/spf13/cast"
 
 	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 )
 
 type NewtCompatCode int
@@ -104,9 +104,9 @@ func ParseNcTable(strMap map[string]string) (NewtCompatTable, error) {
 	return tbl, nil
 }
 
-func ReadNcMap(v *viper.Viper) (NewtCompatMap, error) {
+func ReadNcMap(yc ycfg.YCfg) (NewtCompatMap, error) {
 	mp := NewtCompatMap{}
-	ncMap := v.GetStringMap("repo.newt_compatibility")
+	ncMap := yc.GetValStringMap("repo.newt_compatibility", nil)
 
 	for k, v := range ncMap {
 		repoVer, err := newtutil.ParseVersion(k)
diff --git a/newt/downloader/downloader.go b/newt/downloader/downloader.go
index e4ccf07f..5746bea2 100644
--- a/newt/downloader/downloader.go
+++ b/newt/downloader/downloader.go
@@ -30,6 +30,7 @@ import (
 	log "github.com/Sirupsen/logrus"
 
 	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/settings"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -694,8 +695,8 @@ func LoadDownloader(repoName string, repoVars map[string]string) (
 
 		// Alternatively, the user can put security material in
 		// $HOME/.newt/repos.yml.
-		newtrc := newtutil.Newtrc()
-		privRepo := newtrc.GetStringMapString("repository." + repoName)
+		newtrc := settings.Newtrc()
+		privRepo := newtrc.GetValStringMapString("repository."+repoName, nil)
 		if privRepo != nil {
 			if gd.Login == "" {
 				gd.Login = privRepo["login"]
diff --git a/newt/mfg/load.go b/newt/mfg/load.go
index b2250ed4..e17635b1 100644
--- a/newt/mfg/load.go
+++ b/newt/mfg/load.go
@@ -28,6 +28,7 @@ import (
 
 	"github.com/spf13/cast"
 
+	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/target"
@@ -220,7 +221,7 @@ func (mi *MfgImage) detectOverlaps() error {
 }
 
 func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
-	v, err := util.ReadConfig(basePkg.BasePath(),
+	v, err := newtutil.ReadConfig(basePkg.BasePath(),
 		strings.TrimSuffix(MFG_YAML_FILENAME, ".yml"))
 	if err != nil {
 		return nil, err
@@ -230,7 +231,7 @@ func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
 		basePkg: basePkg,
 	}
 
-	bootName := v.GetString("mfg.bootloader")
+	bootName := v.GetValString("mfg.bootloader", nil)
 	if bootName == "" {
 		return nil, mi.loadError("mfg.bootloader field required")
 	}
@@ -239,7 +240,7 @@ func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
 		return nil, err
 	}
 
-	imgNames := v.GetStringSlice("mfg.images")
+	imgNames := v.GetValStringSlice("mfg.images", nil)
 	if imgNames != nil {
 		for _, imgName := range imgNames {
 			imgTarget, err := mi.loadTarget(imgName)
@@ -256,7 +257,7 @@ func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
 			len(mi.images))
 	}
 
-	itf := v.Get("mfg.raw")
+	itf := v.GetFirstVal("mfg.raw", nil)
 	slice := cast.ToSlice(itf)
 	if slice != nil {
 		for i, entryItf := range slice {
@@ -289,7 +290,7 @@ func Load(basePkg *pkg.LocalPackage) (*MfgImage, error) {
 		return nil, mi.loadError(err.Error())
 	}
 	mi.compiler, err = toolchain.NewCompiler(compilerPkg.BasePath(), "",
-							target.DEFAULT_BUILD_PROFILE)
+		target.DEFAULT_BUILD_PROFILE)
 	if err != nil {
 		return nil, mi.loadError(err.Error())
 	}
diff --git a/newt/newtutil/newtutil.go b/newt/newtutil/newtutil.go
index f68bb181..d40719a5 100644
--- a/newt/newtutil/newtutil.go
+++ b/newt/newtutil/newtutil.go
@@ -22,17 +22,14 @@ package newtutil
 import (
 	"fmt"
 	"io/ioutil"
-	"os/user"
-	"sort"
+	"path/filepath"
 	"strconv"
 	"strings"
 
-	log "github.com/Sirupsen/logrus"
-	"github.com/spf13/cast"
-
 	"mynewt.apache.org/newt/newt/interfaces"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
+	"mynewt.apache.org/newt/yaml"
 )
 
 var NewtVersion Version = Version{1, 3, 0}
@@ -41,9 +38,6 @@ var NewtBlinkyTag string = "mynewt_1_3_0_tag"
 var NewtNumJobs int
 var NewtForce bool
 
-const NEWTRC_DIR string = ".newt"
-const REPOS_FILENAME string = "repos.yml"
-
 const CORE_REPO_NAME string = "apache-mynewt-core"
 const ARDUINO_ZERO_REPO_NAME string = "mynewt_arduino_zero"
 
@@ -102,143 +96,6 @@ func VerCmp(v1 Version, v2 Version) int64 {
 	return 0
 }
 
-// Contains general newt settings read from $HOME/.newt
-var newtrc *viper.Viper
-
-func readNewtrc() *viper.Viper {
-	usr, err := user.Current()
-	if err != nil {
-		return viper.New()
-	}
-
-	dir := usr.HomeDir + "/" + NEWTRC_DIR
-	v, err := util.ReadConfig(dir, strings.TrimSuffix(REPOS_FILENAME, ".yml"))
-	if err != nil {
-		log.Debugf("Failed to read %s/%s file", dir, REPOS_FILENAME)
-		return viper.New()
-	}
-
-	return v
-}
-
-func Newtrc() *viper.Viper {
-	if newtrc != nil {
-		return newtrc
-	}
-
-	newtrc = readNewtrc()
-	return newtrc
-}
-
-func GetSliceFeatures(v *viper.Viper, features map[string]bool,
-	key string) []interface{} {
-
-	val := v.Get(key)
-	vals := []interface{}{val}
-
-	// Process the features in alphabetical order to ensure consistent
-	// results across repeated runs.
-	featureKeys := make([]string, 0, len(features))
-	for feature, _ := range features {
-		featureKeys = append(featureKeys, feature)
-	}
-	sort.Strings(featureKeys)
-
-	for _, feature := range featureKeys {
-		overwriteVal := v.Get(key + "." + feature + ".OVERWRITE")
-		if overwriteVal != nil {
-			return []interface{}{overwriteVal}
-		}
-
-		appendVal := v.Get(key + "." + feature)
-		if appendVal != nil {
-			vals = append(vals, appendVal)
-		}
-	}
-
-	return vals
-}
-
-func GetStringMapFeatures(v *viper.Viper, features map[string]bool,
-	key string) map[string]interface{} {
-
-	result := map[string]interface{}{}
-
-	slice := GetSliceFeatures(v, features, key)
-	for _, itf := range slice {
-		sub := cast.ToStringMap(itf)
-		for k, v := range sub {
-			result[k] = v
-		}
-	}
-
-	return result
-}
-
-func GetStringFeatures(v *viper.Viper, features map[string]bool,
-	key string) string {
-	val := v.GetString(key)
-
-	// Process the features in alphabetical order to ensure consistent
-	// results across repeated runs.
-	var featureKeys []string
-	for feature, _ := range features {
-		featureKeys = append(featureKeys, feature)
-	}
-	sort.Strings(featureKeys)
-
-	for _, feature := range featureKeys {
-		overwriteVal := v.GetString(key + "." + feature + ".OVERWRITE")
-		if overwriteVal != "" {
-			val = strings.Trim(overwriteVal, "\n")
-			break
-		}
-
-		appendVal := v.GetString(key + "." + feature)
-		if appendVal != "" {
-			val += " " + strings.Trim(appendVal, "\n")
-		}
-	}
-	return strings.TrimSpace(val)
-}
-
-func GetBoolFeaturesDflt(v *viper.Viper, features map[string]bool,
-	key string, dflt bool) (bool, error) {
-
-	s := GetStringFeatures(v, features, key)
-	if s == "" {
-		return dflt, nil
-	}
-
-	b, err := strconv.ParseBool(s)
-	if err != nil {
-		return dflt, util.FmtNewtError("invalid bool value for %s: %s",
-			key, s)
-	}
-
-	return b, nil
-}
-
-func GetBoolFeatures(v *viper.Viper, features map[string]bool,
-	key string) (bool, error) {
-
-	return GetBoolFeaturesDflt(v, features, key, false)
-}
-
-func GetStringSliceFeatures(v *viper.Viper, features map[string]bool,
-	key string) []string {
-
-	vals := GetSliceFeatures(v, features, key)
-
-	strVals := []string{}
-	for _, v := range vals {
-		subVals := cast.ToStringSlice(v)
-		strVals = append(strVals, subVals...)
-	}
-
-	return strVals
-}
-
 // Parses a string of the following form:
 //     [@repo]<path/to/package>
 //
@@ -317,3 +174,20 @@ func MakeTempRepoDir() (string, error) {
 
 	return tmpdir, nil
 }
+
+// Read in the configuration file specified by name, in path
+// return a new viper config object if successful, and error if not
+func ReadConfig(path string, name string) (ycfg.YCfg, error) {
+	file, err := ioutil.ReadFile(path + "/" + name + ".yml")
+	if err != nil {
+		return nil, util.NewNewtError(fmt.Sprintf("Error reading %s.yml: %s",
+			filepath.Join(path, name), err.Error()))
+	}
+
+	settings := map[string]interface{}{}
+	if err := yaml.Unmarshal(file, &settings); err != nil {
+		return nil, err
+	}
+
+	return ycfg.NewYCfg(settings)
+}
diff --git a/newt/parse/lex.go b/newt/parse/lex.go
new file mode 100644
index 00000000..09edac07
--- /dev/null
+++ b/newt/parse/lex.go
@@ -0,0 +1,175 @@
+package parse
+
+import (
+	"fmt"
+	"strings"
+
+	"mynewt.apache.org/newt/util"
+)
+
+type TokenCode int
+
+const (
+	TOKEN_NOT_EQUALS TokenCode = iota
+	TOKEN_NOT
+	TOKEN_EQUALS
+	TOKEN_AND
+	TOKEN_OR
+	TOKEN_XOR
+	TOKEN_LPAREN
+	TOKEN_RPAREN
+	TOKEN_STRING
+	TOKEN_NUMBER
+	TOKEN_IDENT
+)
+
+type Token struct {
+	Code   TokenCode
+	Text   string
+	Offset int
+}
+
+// Returns length of token on success; 0 if no match.
+type LexFn func(s string) (string, int, error)
+
+const delimChars = "!='\"&|^() \t\n"
+
+func lexString(s string, sought string) (string, int, error) {
+	if strings.HasPrefix(s, sought) {
+		return sought, len(sought), nil
+	}
+
+	return "", 0, nil
+}
+
+func lexStringFn(sought string) LexFn {
+	return func(s string) (string, int, error) { return lexString(s, sought) }
+}
+
+func lexLitNumber(s string) (string, int, error) {
+	var sub string
+	idx := strings.IndexAny(s, delimChars)
+	if idx == -1 {
+		sub = s
+	} else {
+		sub = s[:idx]
+	}
+	if _, ok := util.AtoiNoOctTry(sub); ok {
+		return sub, len(sub), nil
+	}
+
+	return "", 0, nil
+}
+
+func lexLitString(s string) (string, int, error) {
+	if s[0] != '"' {
+		return "", 0, nil
+	}
+
+	quote2 := strings.IndexByte(s[1:], '"')
+	if quote2 == -1 {
+		return "", 0, fmt.Errorf("unterminated quote: %s", s)
+	}
+
+	return s[1 : quote2+1], quote2 + 2, nil
+}
+
+func lexIdent(s string) (string, int, error) {
+	idx := strings.IndexAny(s, delimChars)
+	if idx == -1 {
+		return s, len(s), nil
+	} else {
+		return s[:idx], idx, nil
+	}
+}
+
+type lexEntry struct {
+	code TokenCode
+	fn   LexFn
+}
+
+var lexEntries = []lexEntry{
+	{TOKEN_NOT_EQUALS, lexStringFn("!=")},
+	{TOKEN_NOT, lexStringFn("!")},
+	{TOKEN_EQUALS, lexStringFn("==")},
+	{TOKEN_AND, lexStringFn("&&")},
+	{TOKEN_OR, lexStringFn("||")},
+	{TOKEN_XOR, lexStringFn("^^")},
+	{TOKEN_LPAREN, lexStringFn("(")},
+	{TOKEN_RPAREN, lexStringFn(")")},
+	{TOKEN_STRING, lexLitString},
+	{TOKEN_NUMBER, lexLitNumber},
+	{TOKEN_IDENT, lexIdent},
+}
+
+func lexOneToken(expr string, offset int) (Token, int, error) {
+	var t Token
+
+	subexpr := expr[offset:]
+	for _, e := range lexEntries {
+		text, sz, err := e.fn(subexpr)
+		if err != nil {
+			return t, 0, err
+		}
+
+		if sz != 0 {
+			t.Code = e.code
+			t.Text = text
+			t.Offset = offset
+			return t, sz, nil
+		}
+	}
+
+	return t, 0, nil
+}
+
+func skipSpaceLeft(s string, offset int) int {
+	sub := s[offset:]
+	newSub := strings.TrimLeft(sub, " \t\n'")
+	return len(sub) - len(newSub)
+}
+
+// Tokenizes a string.
+func Lex(expr string) ([]Token, error) {
+	tokens := []Token{}
+	off := 0
+
+	off += skipSpaceLeft(expr, off)
+	for off < len(expr) {
+		t, skip, err := lexOneToken(expr, off)
+		if err != nil {
+			return nil, err
+		}
+
+		if skip == 0 {
+			return nil, fmt.Errorf("Invalid token starting with: %s", expr)
+		}
+
+		tokens = append(tokens, t)
+
+		off += skip
+		off += skipSpaceLeft(expr, off)
+	}
+
+	return tokens, nil
+}
+
+// Produces a string representation of a token sequence.
+func SprintfTokens(tokens []Token) string {
+	s := ""
+
+	for _, t := range tokens {
+		switch t.Code {
+		case TOKEN_NUMBER:
+			s += fmt.Sprintf("#[%s] ", t.Text)
+		case TOKEN_IDENT:
+			s += fmt.Sprintf("i[%s] ", t.Text)
+		case TOKEN_STRING:
+			s += fmt.Sprintf("\"%s\" ", t.Text)
+		default:
+			s += fmt.Sprintf("%s ", t.Text)
+		}
+	}
+
+	return s
+}
diff --git a/newt/parse/parse.go b/newt/parse/parse.go
new file mode 100644
index 00000000..9c2fe09e
--- /dev/null
+++ b/newt/parse/parse.go
@@ -0,0 +1,467 @@
+package parse
+
+import (
+	"fmt"
+
+	"mynewt.apache.org/newt/util"
+)
+
+// expr     ::= <unary><expr> | "("<expr>")"
+//              <expr><binary><expr> | <ident> | <literal>
+// ident    ::= <printable-char> { <printable-char> }
+// literal  ::= """ <printable-char> { <printable-char> } """
+// unary    ::= "!"
+// binary   ::= "=" | "!=" | "&" | "|" | "^"
+
+type ParseCode int
+
+const (
+	PARSE_NOT_EQUALS ParseCode = iota
+	PARSE_NOT
+	PARSE_EQUALS
+	PARSE_AND
+	PARSE_OR
+	PARSE_XOR
+	PARSE_NUMBER
+	PARSE_STRING
+	PARSE_IDENT
+)
+
+type Node struct {
+	Code ParseCode
+	Data string
+
+	Left  *Node
+	Right *Node
+}
+
+func (n *Node) String() string {
+	s := fmt.Sprintf("<%s>", n.Data)
+	if n.Left != nil {
+		s += " " + n.Left.String()
+	}
+	if n.Right != nil {
+		s += " " + n.Right.String()
+	}
+
+	return s
+}
+
+// Searches a tokenized expression.  The location of the first token that
+// matches a member of the supplied token set is returned.  This function does
+// not descend into parenthesized expressions.
+func findAnyToken(tokens []Token, any []TokenCode) (int, error) {
+	pcount := 0
+
+	for _, a := range any {
+		for i, t := range tokens {
+			if t.Code == TOKEN_LPAREN {
+				pcount++
+			} else if t.Code == TOKEN_RPAREN {
+				pcount--
+				if pcount < 0 {
+					return -1, fmt.Errorf("imbalanced parenthesis")
+				}
+			} else if pcount == 0 && t.Code == a {
+				return i, nil
+			}
+		}
+
+	}
+	return -1, nil
+}
+
+func binTokenToParse(t TokenCode) ParseCode {
+	return map[TokenCode]ParseCode{
+		TOKEN_NOT_EQUALS: PARSE_NOT_EQUALS,
+		TOKEN_EQUALS:     PARSE_EQUALS,
+		TOKEN_AND:        PARSE_AND,
+		TOKEN_OR:         PARSE_OR,
+		TOKEN_XOR:        PARSE_XOR,
+	}[t]
+}
+
+// Removes the outer layer of parentheses from a tokenized expression.
+func stripParens(tokens []Token) ([]Token, error) {
+	if tokens[0].Code != TOKEN_LPAREN {
+		panic("internal error: stripParens() received unparenthesized string")
+	}
+
+	pcount := 1
+	for i := 1; i < len(tokens); i++ {
+		switch tokens[i].Code {
+		case TOKEN_LPAREN:
+			pcount++
+
+		case TOKEN_RPAREN:
+			pcount--
+			if pcount == 0 {
+				return tokens[1:i], nil
+			}
+
+		default:
+		}
+	}
+
+	return nil, fmt.Errorf("unterminated parenthesis")
+}
+
+var binaryTokens = []TokenCode{
+	TOKEN_EQUALS,
+	TOKEN_NOT_EQUALS,
+	TOKEN_OR,
+	TOKEN_XOR,
+	TOKEN_AND,
+}
+
+func FindBinaryToken(tokens []Token) int {
+	binIdx, err := findAnyToken(tokens, binaryTokens)
+	if err != nil {
+		return -1
+	}
+	return binIdx
+}
+
+// Recursively parses a tokenized expression.
+//
+// @param tokens                The sequence of tokens representing the
+//                                  expression to parse.  This is acquired by a
+//                                  call to `Lex()`.
+//
+// @return *Node                The expression parse tree.
+func Parse(tokens []Token) (*Node, error) {
+	if len(tokens) == 0 {
+		return nil, nil
+	}
+
+	////// Terminal symbols.
+
+	if len(tokens) == 1 {
+		switch tokens[0].Code {
+		case TOKEN_NUMBER:
+			return &Node{
+				Code: PARSE_NUMBER,
+				Data: tokens[0].Text,
+			}, nil
+
+		case TOKEN_STRING:
+			return &Node{
+				Code: PARSE_STRING,
+				Data: tokens[0].Text,
+			}, nil
+
+		case TOKEN_IDENT:
+			return &Node{
+				Code: PARSE_IDENT,
+				Data: tokens[0].Text,
+			}, nil
+
+		default:
+			return nil, fmt.Errorf("invalid expression: %s", tokens[0].Text)
+		}
+	}
+
+	////// Nonterminal symbols.
+
+	// <expr><binary><expr>
+	binIdx, err := findAnyToken(tokens, binaryTokens)
+	if err != nil {
+		return nil, err
+	}
+	if binIdx == 0 || binIdx == len(tokens)-1 {
+		return nil, fmt.Errorf("binary operator %s at start or end",
+			tokens[binIdx].Text)
+	}
+	if binIdx != -1 {
+		n := &Node{
+			Code: binTokenToParse(tokens[binIdx].Code),
+			Data: tokens[binIdx].Text,
+		}
+
+		l, err := Parse(tokens[0:binIdx])
+		if err != nil {
+			return nil, err
+		}
+
+		r, err := Parse(tokens[binIdx+1 : len(tokens)])
+		if err != nil {
+			return nil, err
+		}
+
+		n.Left = l
+		n.Right = r
+
+		return n, nil
+	}
+
+	// <unary><expr>
+	if tokens[0].Code == TOKEN_NOT {
+		n := &Node{
+			Code: PARSE_NOT,
+			Data: tokens[0].Text,
+		}
+		r, err := Parse(tokens[1:])
+		if err != nil {
+			return nil, err
+		}
+		n.Right = r
+		return n, nil
+	}
+
+	// "("<expr>")"
+	if tokens[0].Code == TOKEN_LPAREN {
+		stripped, err := stripParens(tokens)
+		if err != nil {
+			return nil, err
+		}
+
+		return Parse(stripped)
+	}
+
+	return nil, fmt.Errorf("invalid expression")
+}
+
+// Evaluates two expressions into boolean values.
+func evalTwo(expr1 *Node, expr2 *Node,
+	settings map[string]string) (bool, bool, error) {
+
+	v1, err := Eval(expr1, settings)
+	if err != nil {
+		return false, false, err
+	}
+	v2, err := Eval(expr2, settings)
+	if err != nil {
+		return false, false, err
+	}
+
+	return v1, v2, nil
+}
+
+type equalsFn func(left *Node, right *Node, settings map[string]string) bool
+type equalsEntry struct {
+	LeftCode  ParseCode
+	RightCode ParseCode
+	Fn        equalsFn
+}
+
+var equalsDispatch = []equalsEntry{
+	// <ident1> == <ident2>
+	// True if both syscfg settings have identical text values.
+	equalsEntry{
+		LeftCode:  PARSE_IDENT,
+		RightCode: PARSE_IDENT,
+		Fn: func(left *Node, right *Node, settings map[string]string) bool {
+			return settings[left.Data] == settings[right.Data]
+		},
+	},
+
+	// <ident> == <number>
+	// True if the syscfg setting's value is a valid representation of the
+	// number on the right.
+	equalsEntry{
+		LeftCode:  PARSE_IDENT,
+		RightCode: PARSE_NUMBER,
+		Fn: func(left *Node, right *Node, settings map[string]string) bool {
+			lnum, ok := util.AtoiNoOctTry(settings[left.Data])
+			if !ok {
+				return false
+			}
+			rnum, ok := util.AtoiNoOctTry(right.Data)
+			if !ok {
+				return false
+			}
+			return lnum == rnum
+		},
+	},
+
+	// <ident> == <string>
+	// True if the syscfg setting's text value is identical to the string on
+	// the right.
+	equalsEntry{
+		LeftCode:  PARSE_IDENT,
+		RightCode: PARSE_STRING,
+		Fn: func(left *Node, right *Node, settings map[string]string) bool {
+			return settings[left.Data] == right.Data
+		},
+	},
+
+	// <number1> == <number2>
+	// True if both numbers have the same value.
+	equalsEntry{
+		LeftCode:  PARSE_NUMBER,
+		RightCode: PARSE_NUMBER,
+		Fn: func(left *Node, right *Node, settings map[string]string) bool {
+			lnum, ok := util.AtoiNoOctTry(left.Data)
+			if !ok {
+				return false
+			}
+			rnum, ok := util.AtoiNoOctTry(right.Data)
+			if !ok {
+				return false
+			}
+			return lnum == rnum
+		},
+	},
+
+	// <number> == <string>
+	// True if the string is a valid representation of the number.
+	equalsEntry{
+		LeftCode:  PARSE_NUMBER,
+		RightCode: PARSE_STRING,
+		Fn: func(left *Node, right *Node, settings map[string]string) bool {
+			return left.Data == right.Data
+		},
+	},
+
+	// <string1> == <string2>
+	// True if both strings are identical (case-sensitive).
+	equalsEntry{
+		LeftCode:  PARSE_STRING,
+		RightCode: PARSE_STRING,
+		Fn: func(left *Node, right *Node, settings map[string]string) bool {
+			return left.Data == right.Data
+		},
+	},
+}
+
+func evalEqualsOnce(
+	left *Node, right *Node, settings map[string]string) (bool, bool) {
+
+	for _, entry := range equalsDispatch {
+		if entry.LeftCode == left.Code && entry.RightCode == right.Code {
+			return entry.Fn(left, right, settings), true
+		}
+	}
+
+	return false, false
+}
+
+// Evaluates an equals expression (`x == y`)
+//
+// @param left                  The fully-parsed left operand.
+// @param left                  The fully-parsed right operand.
+// @param settings              The map of syscfg settings.
+//
+// @return bool                 Whether the expression evaluates to true.
+func evalEquals(
+	left *Node, right *Node, settings map[string]string) (bool, error) {
+
+	// The equals operator has special semantics.  Rather than evaluating both
+	// operands as booleans and then comparing, the behavior of this operator
+	// varies based on the types of operands.  Perform a table lookup using the
+	// operand types, and call the appropriate comparison function if a match
+	// is found.
+	val, ok := evalEqualsOnce(left, right, settings)
+	if ok {
+		return val, nil
+	}
+	val, ok = evalEqualsOnce(right, left, settings)
+	if ok {
+		return val, nil
+	}
+
+	// No special procedure identified.  Fallback to evaluating both operands
+	// as booleans and comparing the results.
+	booll, boolr, err := evalTwo(left, right, settings)
+	if err != nil {
+		return false, err
+	}
+	return booll == boolr, nil
+}
+
+// Evaluates a fully-parsed expression.
+//
+// @param node                  The root of the expression to evaluate.
+// @param settings              The map of syscfg settings.
+//
+// @return bool                 Whether the expression evaluates to true.
+func Eval(expr *Node, settings map[string]string) (bool, error) {
+	switch expr.Code {
+	case PARSE_NOT:
+		r, err := Eval(expr.Right, settings)
+		if err != nil {
+			return false, err
+		}
+		return !r, nil
+
+	case PARSE_EQUALS:
+		return evalEquals(expr.Left, expr.Right, settings)
+
+	case PARSE_NOT_EQUALS:
+		v, err := evalEquals(expr.Left, expr.Right, settings)
+		if err != nil {
+			return false, err
+		}
+		return !v, nil
+
+	case PARSE_AND:
+		l, r, err := evalTwo(expr.Left, expr.Right, settings)
+		if err != nil {
+			return false, err
+		}
+		return l && r, nil
+
+	case PARSE_OR:
+		l, r, err := evalTwo(expr.Left, expr.Right, settings)
+		if err != nil {
+			return false, err
+		}
+		return l || r, nil
+
+	case PARSE_XOR:
+		l, r, err := evalTwo(expr.Left, expr.Right, settings)
+		if err != nil {
+			return false, err
+		}
+		return (l && !r) || (!l && r), nil
+
+	case PARSE_NUMBER:
+		num, ok := util.AtoiNoOctTry(expr.Data)
+		return ok && num != 0, nil
+
+	case PARSE_STRING:
+		return ValueIsTrue(expr.Data), nil
+
+	case PARSE_IDENT:
+		val := settings[expr.Data]
+		return ValueIsTrue(val), nil
+
+	default:
+		return false, fmt.Errorf("invalid parse code: %d", expr.Code)
+	}
+}
+
+// Parses and evaluates string containing a syscfg expression.
+//
+// @param expr                  The expression to parse.
+// @param settings              The map of syscfg settings.
+//
+// @return bool                 Whether the expression evaluates to true.
+func ParseAndEval(expr string, settings map[string]string) (bool, error) {
+	tokens, err := Lex(expr)
+	if err != nil {
+		return false, err
+	}
+
+	n, err := Parse(tokens)
+	if err != nil {
+		return false, fmt.Errorf("error parsing [%s]: %s\n", expr, err.Error())
+	}
+
+	v, err := Eval(n, settings)
+	return v, err
+}
+
+// Evaluates the truthfulness of a text expression.
+func ValueIsTrue(val string) bool {
+	if val == "" {
+		return false
+	}
+
+	i, ok := util.AtoiNoOctTry(val)
+	if ok && i == 0 {
+		return false
+	}
+
+	return true
+}
diff --git a/newt/pkg/bsp_package.go b/newt/pkg/bsp_package.go
index 146347f2..56399a00 100644
--- a/newt/pkg/bsp_package.go
+++ b/newt/pkg/bsp_package.go
@@ -26,8 +26,8 @@ import (
 	"mynewt.apache.org/newt/newt/flash"
 	"mynewt.apache.org/newt/newt/interfaces"
 	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 )
 
 const BSP_YAML_FILENAME = "bsp.yml"
@@ -41,15 +41,15 @@ type BspPackage struct {
 	DownloadScript     string
 	DebugScript        string
 	FlashMap           flash.FlashMap
-	BspV               *viper.Viper
+	BspV               ycfg.YCfg
 }
 
 func (bsp *BspPackage) resolvePathSetting(
-	features map[string]bool, key string) (string, error) {
+	settings map[string]string, key string) (string, error) {
 
 	proj := interfaces.GetProject()
 
-	val := newtutil.GetStringFeatures(bsp.BspV, features, key)
+	val := bsp.BspV.GetValString(key, settings)
 	if val == "" {
 		return "", nil
 	}
@@ -65,21 +65,23 @@ func (bsp *BspPackage) resolvePathSetting(
 // Interprets a setting as either a single linker script or a list of linker
 // scripts.
 func (bsp *BspPackage) resolveLinkerScriptSetting(
-	features map[string]bool, key string) ([]string, error) {
+	settings map[string]string, key string) ([]string, error) {
 
 	paths := []string{}
 
 	// Assume config file specifies a list of scripts.
-	vals := newtutil.GetStringSliceFeatures(bsp.BspV, features, key)
+	vals := bsp.BspV.GetValStringSlice(key, settings)
 	if vals == nil {
 		// Couldn't read a list of scripts; try to interpret setting as a
 		// single script.
-		path, err := bsp.resolvePathSetting(features, key)
+		path, err := bsp.resolvePathSetting(settings, key)
 		if err != nil {
 			return nil, err
 		}
 
-		paths = append(paths, path)
+		if path != "" {
+			paths = append(paths, path)
+		}
 	} else {
 		proj := interfaces.GetProject()
 
@@ -92,55 +94,52 @@ func (bsp *BspPackage) resolveLinkerScriptSetting(
 					bsp.Name(), key)
 			}
 
-			paths = append(paths, path)
+			if path != "" {
+				paths = append(paths, path)
+			}
 		}
 	}
 
 	return paths, nil
 }
 
-func (bsp *BspPackage) Reload(features map[string]bool) error {
+func (bsp *BspPackage) Reload(settings map[string]string) error {
 	var err error
 
-	if features == nil {
-		features = map[string]bool{
-			strings.ToUpper(runtime.GOOS): true,
-		}
-	} else {
-		features[strings.ToUpper(runtime.GOOS)] = true
+	if settings == nil {
+		settings = map[string]string{}
 	}
-	bsp.BspV, err = util.ReadConfig(bsp.BasePath(),
+	settings[strings.ToUpper(runtime.GOOS)] = "1"
+
+	bsp.BspV, err = newtutil.ReadConfig(bsp.BasePath(),
 		strings.TrimSuffix(BSP_YAML_FILENAME, ".yml"))
 	if err != nil {
 		return err
 	}
 	bsp.AddCfgFilename(bsp.BasePath() + BSP_YAML_FILENAME)
 
-	bsp.CompilerName = newtutil.GetStringFeatures(bsp.BspV,
-		features, "bsp.compiler")
-
-	bsp.Arch = newtutil.GetStringFeatures(bsp.BspV,
-		features, "bsp.arch")
+	bsp.CompilerName = bsp.BspV.GetValString("bsp.compiler", settings)
+	bsp.Arch = bsp.BspV.GetValString("bsp.arch", settings)
 
 	bsp.LinkerScripts, err = bsp.resolveLinkerScriptSetting(
-		features, "bsp.linkerscript")
+		settings, "bsp.linkerscript")
 	if err != nil {
 		return err
 	}
 
 	bsp.Part2LinkerScripts, err = bsp.resolveLinkerScriptSetting(
-		features, "bsp.part2linkerscript")
+		settings, "bsp.part2linkerscript")
 	if err != nil {
 		return err
 	}
 
 	bsp.DownloadScript, err = bsp.resolvePathSetting(
-		features, "bsp.downloadscript")
+		settings, "bsp.downloadscript")
 	if err != nil {
 		return err
 	}
 	bsp.DebugScript, err = bsp.resolvePathSetting(
-		features, "bsp.debugscript")
+		settings, "bsp.debugscript")
 	if err != nil {
 		return err
 	}
@@ -154,8 +153,7 @@ func (bsp *BspPackage) Reload(features map[string]bool) error {
 			"(bsp.arch)")
 	}
 
-	ymlFlashMap := newtutil.GetStringMapFeatures(bsp.BspV, features,
-		"bsp.flash_map")
+	ymlFlashMap := bsp.BspV.GetValStringMap("bsp.flash_map", settings)
 	if ymlFlashMap == nil {
 		return util.NewNewtError("BSP does not specify a flash map " +
 			"(bsp.flash_map)")
@@ -173,7 +171,7 @@ func NewBspPackage(lpkg *LocalPackage) (*BspPackage, error) {
 		CompilerName:   "",
 		DownloadScript: "",
 		DebugScript:    "",
-		BspV:           viper.New(),
+		BspV:           ycfg.YCfg{},
 	}
 	lpkg.Load()
 	bsp.LocalPackage = lpkg
diff --git a/newt/pkg/localpackage.go b/newt/pkg/localpackage.go
index 3b436870..48f6bfe3 100644
--- a/newt/pkg/localpackage.go
+++ b/newt/pkg/localpackage.go
@@ -35,8 +35,8 @@ import (
 	"mynewt.apache.org/newt/newt/interfaces"
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/repo"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 	"mynewt.apache.org/newt/yaml"
 )
 
@@ -70,10 +70,10 @@ type LocalPackage struct {
 	injectedSettings map[string]string
 
 	// Settings read from pkg.yml.
-	PkgV *viper.Viper
+	PkgY ycfg.YCfg
 
 	// Settings read from syscfg.yml.
-	SyscfgV *viper.Viper
+	SyscfgY ycfg.YCfg
 
 	// Names of all source yml files; used to determine if rebuild required.
 	cfgFilenames []string
@@ -82,8 +82,8 @@ type LocalPackage struct {
 func NewLocalPackage(r *repo.Repo, pkgDir string) *LocalPackage {
 	pkg := &LocalPackage{
 		desc:             &PackageDesc{},
-		PkgV:             viper.New(),
-		SyscfgV:          viper.New(),
+		PkgY:             ycfg.YCfg{},
+		SyscfgY:          ycfg.YCfg{},
 		repo:             r,
 		basePath:         filepath.ToSlash(filepath.Clean(pkgDir)),
 		init:             map[string]int{},
@@ -128,8 +128,6 @@ func (pkg *LocalPackage) Desc() *PackageDesc {
 
 func (pkg *LocalPackage) SetName(name string) {
 	pkg.name = name
-	// XXX: Also set "pkg.name" in viper object (possibly just remove cached
-	// variable from code entirely).
 }
 
 func (pkg *LocalPackage) SetBasePath(basePath string) {
@@ -138,14 +136,10 @@ func (pkg *LocalPackage) SetBasePath(basePath string) {
 
 func (pkg *LocalPackage) SetType(packageType interfaces.PackageType) {
 	pkg.packageType = packageType
-	// XXX: Also set "pkg.type" in viper object (possibly just remove cached
-	// variable from code entirely).
 }
 
 func (pkg *LocalPackage) SetDesc(desc *PackageDesc) {
 	pkg.desc = desc
-	// XXX: Also set desc fields in viper object (possibly just remove cached
-	// variable from code entirely).
 }
 
 func (pkg *LocalPackage) SetRepo(r *repo.Repo) {
@@ -192,13 +186,13 @@ func (pkg *LocalPackage) AddCfgFilename(cfgFilename string) {
 	pkg.cfgFilenames = append(pkg.cfgFilenames, cfgFilename)
 }
 
-func (pkg *LocalPackage) readDesc(v *viper.Viper) (*PackageDesc, error) {
+func (pkg *LocalPackage) readDesc(yc ycfg.YCfg) (*PackageDesc, error) {
 	pdesc := &PackageDesc{}
 
-	pdesc.Author = v.GetString("pkg.author")
-	pdesc.Homepage = v.GetString("pkg.homepage")
-	pdesc.Description = v.GetString("pkg.description")
-	pdesc.Keywords = v.GetStringSlice("pkg.keywords")
+	pdesc.Author = yc.GetValString("pkg.author", nil)
+	pdesc.Homepage = yc.GetValString("pkg.homepage", nil)
+	pdesc.Description = yc.GetValString("pkg.description", nil)
+	pdesc.Keywords = yc.GetValStringSlice("pkg.keywords", nil)
 
 	return pdesc, nil
 }
@@ -206,8 +200,8 @@ func (pkg *LocalPackage) readDesc(v *viper.Viper) (*PackageDesc, error) {
 func (pkg *LocalPackage) sequenceString(key string) string {
 	var buffer bytes.Buffer
 
-	if pkg.PkgV != nil {
-		for _, f := range pkg.PkgV.GetStringSlice(key) {
+	if pkg.PkgY != nil {
+		for _, f := range pkg.PkgY.GetValStringSlice(key, nil) {
 			buffer.WriteString("    - " + yaml.EscapeString(f) + "\n")
 		}
 	}
@@ -227,7 +221,7 @@ func (lpkg *LocalPackage) SaveSyscfgVals() error {
 
 	filepath := dirpath + "/" + SYSCFG_YAML_FILENAME
 
-	syscfgVals := lpkg.SyscfgV.GetStringMapString("syscfg.vals")
+	syscfgVals := lpkg.SyscfgY.GetValStringMapString("syscfg.vals", nil)
 	if syscfgVals == nil || len(syscfgVals) == 0 {
 		os.Remove(filepath)
 		return nil
@@ -273,7 +267,7 @@ func (pkg *LocalPackage) Save() error {
 
 	file.WriteString("### Package: " + pkg.Name() + "\n")
 
-	// XXX: Just iterate viper object's settings rather than calling out
+	// XXX: Just iterate ycfg object's settings rather than calling out
 	// cached settings individually.
 	file.WriteString("pkg.name: " + yaml.EscapeString(pkg.Name()) + "\n")
 	file.WriteString("pkg.type: " +
@@ -312,7 +306,7 @@ func (pkg *LocalPackage) Load() error {
 
 	var err error
 
-	pkg.PkgV, err = util.ReadConfig(pkg.basePath,
+	pkg.PkgY, err = newtutil.ReadConfig(pkg.basePath,
 		strings.TrimSuffix(PACKAGE_FILE_NAME, ".yml"))
 	if err != nil {
 		return err
@@ -320,7 +314,7 @@ func (pkg *LocalPackage) Load() error {
 	pkg.AddCfgFilename(pkg.basePath + "/" + PACKAGE_FILE_NAME)
 
 	// Set package name from the package
-	pkg.name = pkg.PkgV.GetString("pkg.name")
+	pkg.name = pkg.PkgY.GetValString("pkg.name", nil)
 	if pkg.name == "" {
 		return util.FmtNewtError(
 			"Package \"%s\" missing \"pkg.name\" field in its `pkg.yml` file",
@@ -333,7 +327,7 @@ func (pkg *LocalPackage) Load() error {
 				"`pkg.yml` file (pkg.name=%s)", pkg.basePath, pkg.name)
 	}
 
-	typeString := pkg.PkgV.GetString("pkg.type")
+	typeString := pkg.PkgY.GetValString("pkg.type", nil)
 	pkg.packageType = PACKAGE_TYPE_LIB
 	for t, n := range PackageTypeNames {
 		if typeString == n {
@@ -342,7 +336,7 @@ func (pkg *LocalPackage) Load() error {
 		}
 	}
 
-	init := pkg.PkgV.GetStringMapString("pkg.init")
+	init := pkg.PkgY.GetValStringMapString("pkg.init", nil)
 	for name, stageStr := range init {
 		stage, err := strconv.ParseInt(stageStr, 10, 64)
 		if err != nil {
@@ -351,22 +345,22 @@ func (pkg *LocalPackage) Load() error {
 		}
 		pkg.init[name] = int(stage)
 	}
-	initFnName := pkg.PkgV.GetString("pkg.init_function")
-	initStage := pkg.PkgV.GetInt("pkg.init_stage")
+	initFnName := pkg.PkgY.GetValString("pkg.init_function", nil)
+	initStage := pkg.PkgY.GetValInt("pkg.init_stage", nil)
 
 	if initFnName != "" {
 		pkg.init[initFnName] = initStage
 	}
 
 	// Read the package description from the file
-	pkg.desc, err = pkg.readDesc(pkg.PkgV)
+	pkg.desc, err = pkg.readDesc(pkg.PkgY)
 	if err != nil {
 		return err
 	}
 
 	// Load syscfg settings.
 	if util.NodeExist(pkg.basePath + "/" + SYSCFG_YAML_FILENAME) {
-		pkg.SyscfgV, err = util.ReadConfig(pkg.basePath,
+		pkg.SyscfgY, err = newtutil.ReadConfig(pkg.basePath,
 			strings.TrimSuffix(SYSCFG_YAML_FILENAME, ".yml"))
 		if err != nil {
 			return err
diff --git a/newt/project/project.go b/newt/project/project.go
index 69569d26..6bfdb9a0 100644
--- a/newt/project/project.go
+++ b/newt/project/project.go
@@ -36,8 +36,8 @@ import (
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/repo"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 )
 
 var globalProject *Project = nil
@@ -66,7 +66,7 @@ type Project struct {
 
 	localRepo *repo.Repo
 
-	v *viper.Viper
+	yc ycfg.YCfg
 }
 
 func initProject(dir string) error {
@@ -434,10 +434,10 @@ func (proj *Project) Upgrade(force bool) error {
 	return proj.Install(true, force)
 }
 
-func (proj *Project) loadRepo(rname string, v *viper.Viper) error {
+func (proj *Project) loadRepo(rname string, yc ycfg.YCfg) error {
 	varName := fmt.Sprintf("repository.%s", rname)
 
-	repoVars := v.GetStringMapString(varName)
+	repoVars := yc.GetValStringMapString(varName, nil)
 	if len(repoVars) == 0 {
 		return util.NewNewtError(fmt.Sprintf("Missing configuration for "+
 			"repository %s.", rname))
@@ -494,7 +494,7 @@ func (proj *Project) loadRepo(rname string, v *viper.Viper) error {
 }
 
 func (proj *Project) checkNewtVer() error {
-	compatSms := proj.v.GetStringMapString("project.newt_compatibility")
+	compatSms := proj.yc.GetValStringMapString("project.newt_compatibility", nil)
 	// If this project doesn't have a newt compatibility map, just assume there
 	// is no incompatibility.
 	if len(compatSms) == 0 {
@@ -524,7 +524,7 @@ func (proj *Project) checkNewtVer() error {
 }
 
 func (proj *Project) loadConfig() error {
-	v, err := util.ReadConfig(proj.BasePath,
+	yc, err := newtutil.ReadConfig(proj.BasePath,
 		strings.TrimSuffix(PROJECT_FILE_NAME, ".yml"))
 	if err != nil {
 		return util.NewNewtError(err.Error())
@@ -532,14 +532,14 @@ func (proj *Project) loadConfig() error {
 	// Store configuration object for access to future values,
 	// this avoids keeping every string around as a project variable when
 	// we need to process it later.
-	proj.v = v
+	proj.yc = yc
 
 	proj.projState, err = LoadProjectState()
 	if err != nil {
 		return err
 	}
 
-	proj.name = v.GetString("project.name")
+	proj.name = yc.GetValString("project.name", nil)
 
 	// Local repository always included in initialization
 	r, err := repo.NewLocalRepo(proj.name)
@@ -553,14 +553,14 @@ func (proj *Project) loadConfig() error {
 		r.AddIgnoreDir(ignDir)
 	}
 
-	rstrs := v.GetStringSlice("project.repositories")
+	rstrs := yc.GetValStringSlice("project.repositories", nil)
 	for _, repoName := range rstrs {
-		if err := proj.loadRepo(repoName, v); err != nil {
+		if err := proj.loadRepo(repoName, yc); err != nil {
 			return err
 		}
 	}
 
-	ignoreDirs := v.GetStringSlice("project.ignore_dirs")
+	ignoreDirs := yc.GetValStringSlice("project.ignore_dirs", nil)
 	for _, ignDir := range ignoreDirs {
 		repoName, dirName, err := newtutil.ParsePackageString(ignDir)
 		if err != nil {
diff --git a/newt/repo/repo.go b/newt/repo/repo.go
index 6a05c698..8eb8844a 100644
--- a/newt/repo/repo.go
+++ b/newt/repo/repo.go
@@ -36,8 +36,8 @@ import (
 	"mynewt.apache.org/newt/newt/downloader"
 	"mynewt.apache.org/newt/newt/interfaces"
 	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 )
 
 const REPO_NAME_LOCAL = "local"
@@ -710,7 +710,7 @@ func (r *Repo) DownloadDesc() error {
 	return nil
 }
 
-func (r *Repo) readDepRepos(v *viper.Viper) ([]*Repo, error) {
+func (r *Repo) readDepRepos(yc ycfg.YCfg) ([]*Repo, error) {
 	rdesc := r.rdesc
 	repos := []*Repo{}
 
@@ -723,7 +723,7 @@ func (r *Repo) readDepRepos(v *viper.Viper) ([]*Repo, error) {
 
 	repoTag := fmt.Sprintf("%s.repositories", branch)
 
-	repoList := v.GetStringMap(repoTag)
+	repoList := yc.GetValStringMap(repoTag, nil)
 	for repoName, repoItf := range repoList {
 		repoVars := cast.ToStringMapString(repoItf)
 
@@ -757,14 +757,14 @@ func (r *Repo) ReadDesc() (*RepoDesc, []*Repo, error) {
 			util.NewNewtError("No configuration exists for repository " + r.name)
 	}
 
-	v, err := util.ReadConfig(r.repoFilePath(),
+	yc, err := newtutil.ReadConfig(r.repoFilePath(),
 		strings.TrimSuffix(REPO_FILE_NAME, ".yml"))
 	if err != nil {
 		return nil, nil, err
 	}
 
-	name := v.GetString("repo.name")
-	versMap := v.GetStringMapString("repo.versions")
+	name := yc.GetValString("repo.name", nil)
+	versMap := yc.GetValStringMapString("repo.versions", nil)
 
 	rdesc, err := NewRepoDesc(name, versMap)
 	if err != nil {
@@ -772,13 +772,13 @@ func (r *Repo) ReadDesc() (*RepoDesc, []*Repo, error) {
 	}
 	r.rdesc = rdesc
 
-	repos, err := r.readDepRepos(v)
+	repos, err := r.readDepRepos(yc)
 	if err != nil {
 		return nil, nil, err
 	}
 
 	// Read the newt version compatibility map.
-	r.ncMap, err = compat.ReadNcMap(v)
+	r.ncMap, err = compat.ReadNcMap(yc)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/newt/resolve/resolve.go b/newt/resolve/resolve.go
index 0ecf9ed9..737bedce 100644
--- a/newt/resolve/resolve.go
+++ b/newt/resolve/resolve.go
@@ -27,19 +27,39 @@ import (
 	log "github.com/Sirupsen/logrus"
 
 	"mynewt.apache.org/newt/newt/flash"
-	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/syscfg"
 	"mynewt.apache.org/newt/util"
 )
 
+// Represents a supplied API.
+type resolveApi struct {
+	// The package which supplies the API.
+	rpkg *ResolvePackage
+
+	// The expression which enabled this API.
+	expr string
+}
+
+// Represents a required API.
+type resolveReqApi struct {
+	// Whether the API requirement has been satisfied by a hard dependency.
+	satisfied bool
+
+	// The expression which enabled this API requirement.
+	expr string
+}
+
 type Resolver struct {
-	apis             map[string]*ResolvePackage
+	apis             map[string]resolveApi
 	pkgMap           map[*pkg.LocalPackage]*ResolvePackage
 	injectedSettings map[string]string
 	flashMap         flash.FlashMap
 	cfg              syscfg.Cfg
+
+	// [api-name][api-supplier]
+	apiConflicts map[string]map[*ResolvePackage]struct{}
 }
 
 type ResolveDep struct {
@@ -49,7 +69,8 @@ type ResolveDep struct {
 	// Name of API that generated the dependency; "" if a hard dependency.
 	Api string
 
-	// XXX: slice of syscfg settings that generated this dependency.
+	// Text of syscfg expression that generated this dependency; "" for none.
+	Expr string
 }
 
 type ResolvePackage struct {
@@ -57,10 +78,11 @@ type ResolvePackage struct {
 	Deps map[*ResolvePackage]*ResolveDep
 
 	// Keeps track of API requirements and whether they are satisfied.
-	reqApiMap map[string]bool
+	reqApiMap map[string]resolveReqApi
 
 	depsResolved  bool
 	apisSatisfied bool
+	refCount      int
 }
 
 type ResolveSet struct {
@@ -71,11 +93,17 @@ type ResolveSet struct {
 	Rpkgs []*ResolvePackage
 }
 
+type ApiConflict struct {
+	Api  string
+	Pkgs []*ResolvePackage
+}
+
 // The result of resolving a target's configuration, APIs, and dependencies.
 type Resolution struct {
 	Cfg             syscfg.Cfg
 	ApiMap          map[string]*ResolvePackage
 	UnsatisfiedApis map[string][]*ResolvePackage
+	ApiConflicts    []ApiConflict
 
 	LpkgRpkgMap map[*pkg.LocalPackage]*ResolvePackage
 
@@ -92,11 +120,12 @@ func newResolver(
 	flashMap flash.FlashMap) *Resolver {
 
 	r := &Resolver{
-		apis:             map[string]*ResolvePackage{},
+		apis:             map[string]resolveApi{},
 		pkgMap:           map[*pkg.LocalPackage]*ResolvePackage{},
 		injectedSettings: injectedSettings,
 		flashMap:         flashMap,
 		cfg:              syscfg.NewCfg(),
+		apiConflicts:     map[string]map[*ResolvePackage]struct{}{},
 	}
 
 	if injectedSettings == nil {
@@ -126,7 +155,7 @@ func newResolution() *Resolution {
 func NewResolvePkg(lpkg *pkg.LocalPackage) *ResolvePackage {
 	return &ResolvePackage{
 		Lpkg:      lpkg,
-		reqApiMap: map[string]bool{},
+		reqApiMap: map[string]resolveReqApi{},
 		Deps:      map[*ResolvePackage]*ResolveDep{},
 	}
 }
@@ -145,7 +174,9 @@ func (r *Resolver) resolveDep(dep *pkg.Dependency, depender string) (*pkg.LocalP
 
 // @return                      true if the package's dependency list was
 //                                  modified.
-func (rpkg *ResolvePackage) AddDep(apiPkg *ResolvePackage, api string) bool {
+func (rpkg *ResolvePackage) AddDep(
+	apiPkg *ResolvePackage, api string, expr string) bool {
+
 	if dep := rpkg.Deps[apiPkg]; dep != nil {
 		if dep.Api != "" && api == "" {
 			dep.Api = api
@@ -157,6 +188,7 @@ func (rpkg *ResolvePackage) AddDep(apiPkg *ResolvePackage, api string) bool {
 		rpkg.Deps[apiPkg] = &ResolveDep{
 			Rpkg: apiPkg,
 			Api:  api,
+			Expr: expr,
 		}
 		return true
 	}
@@ -197,45 +229,69 @@ func (r *Resolver) addPkg(lpkg *pkg.LocalPackage) (*ResolvePackage, bool) {
 
 	rpkg := NewResolvePkg(lpkg)
 	r.pkgMap[lpkg] = rpkg
+	rpkg.refCount = 1
 	return rpkg, true
 }
 
 // @return bool                 true if this is a new API.
-func (r *Resolver) addApi(apiString string, rpkg *ResolvePackage) bool {
-	curRpkg := r.apis[apiString]
-	if curRpkg == nil {
-		r.apis[apiString] = rpkg
+func (r *Resolver) addApi(
+	apiString string, rpkg *ResolvePackage, expr string) bool {
+
+	api := r.apis[apiString]
+	if api.rpkg == nil {
+		r.apis[apiString] = resolveApi{
+			rpkg: rpkg,
+			expr: expr,
+		}
 		return true
 	} else {
-		if curRpkg != rpkg {
-			util.StatusMessage(util.VERBOSITY_QUIET,
-				"Warning: API conflict: %s (%s <-> %s)\n", apiString,
-				curRpkg.Lpkg.Name(), rpkg.Lpkg.Name())
+		if api.rpkg != rpkg {
+			if r.apiConflicts[apiString] == nil {
+				r.apiConflicts[apiString] = map[*ResolvePackage]struct{}{}
+			}
+			r.apiConflicts[apiString][api.rpkg] = struct{}{}
+			r.apiConflicts[apiString][rpkg] = struct{}{}
 		}
 		return false
 	}
 }
 
+func joinExprs(expr1 string, expr2 string) string {
+	if expr1 == "" {
+		return expr2
+	}
+	if expr2 == "" {
+		return expr1
+	}
+
+	return expr1 + "," + expr2
+}
+
 // Searches for a package which can satisfy bpkg's API requirement.  If such a
 // package is found, bpkg's API requirement is marked as satisfied, and the
 // package is added to bpkg's dependency list.
 //
 // @return bool                 true if the API is now satisfied.
-func (r *Resolver) satisfyApi(rpkg *ResolvePackage, reqApi string) bool {
-	depRpkg := r.apis[reqApi]
-	if depRpkg == nil {
-		// Insert nil to indicate an unsatisfied API.
-		r.apis[reqApi] = nil
+func (r *Resolver) satisfyApi(
+	rpkg *ResolvePackage, reqApi string, expr string) bool {
+
+	api := r.apis[reqApi]
+	if api.rpkg == nil {
+		// Insert null entry to indicate an unsatisfied API.
+		r.apis[reqApi] = resolveApi{}
 		return false
 	}
 
-	rpkg.reqApiMap[reqApi] = true
+	rpkg.reqApiMap[reqApi] = resolveReqApi{
+		satisfied: true,
+		expr:      expr,
+	}
 
 	// This package now has a new unresolved dependency.
 	rpkg.depsResolved = false
 
-	log.Debugf("API requirement satisfied; pkg=%s API=(%s, %s)",
-		rpkg.Lpkg.Name(), reqApi, depRpkg.Lpkg.FullName())
+	log.Debugf("API requirement satisfied; pkg=%s API=(%s, %s) expr=(%s)",
+		rpkg.Lpkg.Name(), reqApi, api.rpkg.Lpkg.FullName(), expr)
 
 	return true
 }
@@ -249,24 +305,27 @@ func (r *Resolver) satisfyApis(rpkg *ResolvePackage) bool {
 	rpkg.apisSatisfied = true
 	newDeps := false
 
-	features := r.cfg.FeaturesForLpkg(rpkg.Lpkg)
+	settings := r.cfg.SettingsForLpkg(rpkg.Lpkg)
 
 	// Determine if any of the package's API requirements can now be satisfied.
 	// If so, another full iteration is required.
-	reqApis := newtutil.GetStringSliceFeatures(rpkg.Lpkg.PkgV, features,
-		"pkg.req_apis")
-	for _, reqApi := range reqApis {
-		reqStatus := rpkg.reqApiMap[reqApi]
-		if !reqStatus {
-			apiSatisfied := r.satisfyApi(rpkg, reqApi)
-			if apiSatisfied {
-				// An API was satisfied; the package now has a new dependency
-				// that needs to be resolved.
-				newDeps = true
-				reqStatus = true
-			} else {
-				rpkg.reqApiMap[reqApi] = false
-				rpkg.apisSatisfied = false
+	reqApiEntries := rpkg.Lpkg.PkgY.GetSlice("pkg.req_apis", settings)
+	for _, entry := range reqApiEntries {
+		apiStr, ok := entry.Value.(string)
+		if ok && apiStr != "" {
+			if !rpkg.reqApiMap[apiStr].satisfied {
+				apiSatisfied := r.satisfyApi(rpkg, apiStr, entry.Expr)
+				if apiSatisfied {
+					// An API was satisfied; the package now has a new
+					// dependency that needs to be resolved.
+					newDeps = true
+				} else {
+					rpkg.reqApiMap[apiStr] = resolveReqApi{
+						satisfied: false,
+						expr:      "",
+					}
+					rpkg.apisSatisfied = false
+				}
 			}
 		}
 	}
@@ -279,36 +338,53 @@ func (r *Resolver) satisfyApis(rpkg *ResolvePackage) bool {
 //                                  in this case.
 //         error                non-nil on failure.
 func (r *Resolver) loadDepsForPkg(rpkg *ResolvePackage) (bool, error) {
-	features := r.cfg.FeaturesForLpkg(rpkg.Lpkg)
+	settings := r.cfg.SettingsForLpkg(rpkg.Lpkg)
 
 	changed := false
-	newDeps := newtutil.GetStringSliceFeatures(rpkg.Lpkg.PkgV, features,
-		"pkg.deps")
+
+	depEntries := rpkg.Lpkg.PkgY.GetSlice("pkg.deps", settings)
 	depender := rpkg.Lpkg.Name()
-	for _, newDepStr := range newDeps {
-		newDep, err := pkg.NewDependency(rpkg.Lpkg.Repo(), newDepStr)
-		if err != nil {
-			return false, err
-		}
 
-		lpkg, err := r.resolveDep(newDep, depender)
-		if err != nil {
-			return false, err
+	seen := make(map[*ResolvePackage]struct{}, len(rpkg.Deps))
+
+	for _, entry := range depEntries {
+		depStr, ok := entry.Value.(string)
+		if ok && depStr != "" {
+			newDep, err := pkg.NewDependency(rpkg.Lpkg.Repo(), depStr)
+			if err != nil {
+				return false, err
+			}
+
+			lpkg, err := r.resolveDep(newDep, depender)
+			if err != nil {
+				return false, err
+			}
+
+			depRpkg, _ := r.addPkg(lpkg)
+			if rpkg.AddDep(depRpkg, "", entry.Expr) {
+				changed = true
+			}
+			seen[depRpkg] = struct{}{}
 		}
+	}
 
-		depRpkg, _ := r.addPkg(lpkg)
-		if rpkg.AddDep(depRpkg, "") {
+	for rpkg, _ := range rpkg.Deps {
+		if _, ok := seen[rpkg]; !ok {
+			delete(rpkg.Deps, rpkg)
+			rpkg.refCount--
 			changed = true
 		}
 	}
 
 	// Determine if this package supports any APIs that we haven't seen
 	// yet.  If so, another full iteration is required.
-	apis := newtutil.GetStringSliceFeatures(rpkg.Lpkg.PkgV, features,
-		"pkg.apis")
-	for _, api := range apis {
-		if r.addApi(api, rpkg) {
-			changed = true
+	apiEntries := rpkg.Lpkg.PkgY.GetSlice("pkg.apis", settings)
+	for _, entry := range apiEntries {
+		apiStr, ok := entry.Value.(string)
+		if ok && apiStr != "" {
+			if r.addApi(apiStr, rpkg, entry.Expr) {
+				changed = true
+			}
 		}
 	}
 
@@ -352,11 +428,11 @@ func (r *Resolver) reloadCfg() (bool, error) {
 	lpkgs := RpkgSliceToLpkgSlice(r.rpkgSlice())
 	apis := r.apiSlice()
 
-	// Determine which features have been detected so far.  The feature map is
-	// required for reloading syscfg, as features may unlock additional
+	// Determine which settings have been detected so far.  The feature map is
+	// required for reloading syscfg, as settings may unlock additional
 	// settings.
-	features := r.cfg.Features()
-	cfg, err := syscfg.Read(lpkgs, apis, r.injectedSettings, features,
+	settings := r.cfg.SettingValues()
+	cfg, err := syscfg.Read(lpkgs, apis, r.injectedSettings, settings,
 		r.flashMap)
 	if err != nil {
 		return false, err
@@ -451,6 +527,13 @@ func (r *Resolver) resolveDepsAndCfg() error {
 		return err
 	}
 
+	// Remove orphaned packages.
+	for lpkg, rpkg := range r.pkgMap {
+		if rpkg.refCount == 0 {
+			delete(r.pkgMap, lpkg)
+		}
+	}
+
 	// Log the final syscfg.
 	r.cfg.Log()
 
@@ -462,10 +545,11 @@ func (r *Resolver) resolveDepsAndCfg() error {
 // corresponding dependecy to each package which requires the API.
 func (r *Resolver) resolveApiDeps() error {
 	for _, rpkg := range r.pkgMap {
-		for api, _ := range rpkg.reqApiMap {
-			apiPkg := r.apis[api]
-			if apiPkg != nil {
-				rpkg.AddDep(apiPkg, api)
+		for apiString, reqApi := range rpkg.reqApiMap {
+			api := r.apis[apiString]
+			if api.rpkg != nil {
+				rpkg.AddDep(api.rpkg, apiString,
+					joinExprs(api.expr, reqApi.expr))
 			}
 		}
 	}
@@ -479,22 +563,22 @@ func (r *Resolver) apiResolution() (
 
 	apiMap := make(map[string]*ResolvePackage, len(r.apis))
 	anyUnsatisfied := false
-	for api, rpkg := range r.apis {
-		if rpkg == nil {
+	for name, api := range r.apis {
+		if api.rpkg == nil {
 			anyUnsatisfied = true
 		} else {
-			apiMap[api] = rpkg
+			apiMap[name] = api.rpkg
 		}
 	}
 
 	unsatisfied := map[string][]*ResolvePackage{}
 	if anyUnsatisfied {
 		for _, rpkg := range r.pkgMap {
-			for api, satisfied := range rpkg.reqApiMap {
-				if !satisfied {
-					slice := unsatisfied[api]
+			for name, reqApi := range rpkg.reqApiMap {
+				if !reqApi.satisfied {
+					slice := unsatisfied[name]
 					slice = append(slice, rpkg)
-					unsatisfied[api] = slice
+					unsatisfied[name] = slice
 				}
 			}
 		}
@@ -533,6 +617,17 @@ func ResolveFull(
 	apiMap := map[string]*ResolvePackage{}
 	apiMap, res.UnsatisfiedApis = r.apiResolution()
 
+	for api, m := range r.apiConflicts {
+		c := ApiConflict{
+			Api: api,
+		}
+		for rpkg, _ := range m {
+			c.Pkgs = append(c.Pkgs, rpkg)
+		}
+
+		res.ApiConflicts = append(res.ApiConflicts, c)
+	}
+
 	res.LpkgRpkgMap = r.pkgMap
 
 	res.MasterSet.Rpkgs = r.rpkgSlice()
@@ -627,5 +722,17 @@ func (res *Resolution) ErrorText() string {
 }
 
 func (res *Resolution) WarningText() string {
-	return res.Cfg.WarningText()
+	text := ""
+
+	for _, c := range res.ApiConflicts {
+		text += fmt.Sprintf("Warning: API conflict: %s (", c.Api)
+		for i, rpkg := range c.Pkgs {
+			if i != 0 {
+				text += " <-> "
+			}
+			text += rpkg.Lpkg.Name()
+		}
+	}
+
+	return text + res.Cfg.WarningText()
 }
diff --git a/newt/settings/settings.go b/newt/settings/settings.go
new file mode 100644
index 00000000..16135054
--- /dev/null
+++ b/newt/settings/settings.go
@@ -0,0 +1,62 @@
+/**
+ * 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 settings
+
+import (
+	"os/user"
+	"strings"
+
+	log "github.com/Sirupsen/logrus"
+
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/newt/ycfg"
+)
+
+const NEWTRC_DIR string = ".newt"
+const REPOS_FILENAME string = "repos.yml"
+
+// Contains general newt settings read from $HOME/.newt
+var newtrc ycfg.YCfg
+
+func readNewtrc() ycfg.YCfg {
+	usr, err := user.Current()
+	if err != nil {
+		return ycfg.YCfg{}
+	}
+
+	dir := usr.HomeDir + "/" + NEWTRC_DIR
+	yc, err := newtutil.ReadConfig(dir,
+		strings.TrimSuffix(REPOS_FILENAME, ".yml"))
+	if err != nil {
+		log.Debugf("Failed to read %s/%s file", dir, REPOS_FILENAME)
+		return ycfg.YCfg{}
+	}
+
+	return yc
+}
+
+func Newtrc() ycfg.YCfg {
+	if newtrc != nil {
+		return newtrc
+	}
+
+	newtrc = readNewtrc()
+	return newtrc
+}
diff --git a/newt/syscfg/restrict.go b/newt/syscfg/restrict.go
index 5f9c0f79..d78e5221 100644
--- a/newt/syscfg/restrict.go
+++ b/newt/syscfg/restrict.go
@@ -17,12 +17,49 @@
  * under the License.
  */
 
+// Currently, two forms of restrictions are supported:
+// 1. "$notnull"
+// 2. expression
+//
+// The "$notnull" string indicates that the setting must be set to something
+// other than the empty string.
+//
+// An expression string can take two forms:
+// 1. Full expression
+// 2. Shorthand
+//
+// A full expression has the same syntax as syscfg expressions.
+//
+// A shorthand expression has one of the following forms:
+//     [!]<req-setting>
+//     (DEPRECATED) [!]<req-setting> if <base-val>
+//     (DEPRECATED) [!]<req-setting> if <expression>
+//
+// Examples:
+//     # Can't enable this setting unless LOG_FCB is enabled.
+//     # (shorthand)
+//     pkg.restrictions:
+//         - LOG_FCB
+//
+//     # Can't enable this setting unless LOG_FCB is disabled.
+//     # (shorthand)
+//     pkg.restrictions:
+//         - !LOG_FCB
+//
+//     # Can't enable this setting (`MYSETTING`) unless LOG_FCB is enabled and
+//     # CONSOLE_UART is set to "uart0".
+//     # (full expression)
+//     pkg.restrictions:
+//         - '(LOG_FCB && CONSOLE_UART == "uart0") || !MYSETTING
+
 package syscfg
 
 import (
 	"fmt"
-	"strings"
 
+	log "github.com/Sirupsen/logrus"
+
+	"mynewt.apache.org/newt/newt/parse"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -37,85 +74,12 @@ var cfgRestrictionNameCodeMap = map[string]CfgRestrictionCode{
 	"$notnull": CFG_RESTRICTION_CODE_NOTNULL,
 }
 
-type CfgRestrictionExpr struct {
-	ReqSetting string
-	ReqVal     bool
-	BaseVal    bool
-}
 type CfgRestriction struct {
 	BaseSetting string
 	Code        CfgRestrictionCode
 
 	// Only used if Code is CFG_RESTRICTION_CODE_EXPR
-	Expr CfgRestrictionExpr
-}
-
-func parseRestrictionExprConsequent(field string) (string, bool) {
-	var val bool
-	var name string
-
-	if strings.HasPrefix(field, "!") {
-		val = false
-		name = strings.TrimPrefix(field, "!")
-	} else {
-		val = true
-		name = field
-	}
-
-	return name, val
-}
-
-// Parses a restriction value.
-//
-// Currently, two forms of restrictions are supported:
-// 1. "$notnull"
-// 2. expression
-//
-// The "$notnull" string indicates that the setting must be set to something
-// other than the empty string.
-//
-// An expression string indicates dependencies on other settings.  It would be
-// better to have a real expression parser.  For now, only very simple
-// expressions are supported.  A restriction expression must be of the
-// following form:
-//     [!]<req-setting> [if <base-val>]
-//
-// All setting values are interpreted as booleans.  If a setting is "0", "",
-// or undefined, it is false; otherwise it is true.
-//
-// Examples:
-//     # Can't enable this setting unless LOG_FCB is enabled.
-//	   pkg.restrictions:
-//         LOG_FCB
-//
-//     # Can't enable this setting unless LOG_FCB is disabled.
-//	   pkg.restrictions:
-//         !LOG_FCB
-//
-//     # Can't disable this setting unless LOG_FCB is enabled.
-//	   pkg.restrictions:
-//         LOG_FCB if 0
-func readRestrictionExpr(text string) (CfgRestrictionExpr, error) {
-	e := CfgRestrictionExpr{}
-
-	fields := strings.Fields(text)
-	switch len(fields) {
-	case 1:
-		e.ReqSetting, e.ReqVal = parseRestrictionExprConsequent(fields[0])
-		e.BaseVal = true
-
-	case 3:
-		if fields[1] != "if" {
-			return e, util.FmtNewtError("invalid restriction: %s", text)
-		}
-		e.ReqSetting, e.ReqVal = parseRestrictionExprConsequent(fields[0])
-		e.BaseVal = ValueIsTrue(fields[2])
-
-	default:
-		return e, util.FmtNewtError("invalid restriction: %s", text)
-	}
-
-	return e, nil
+	Expr string
 }
 
 func readRestriction(baseSetting string, text string) (CfgRestriction, error) {
@@ -128,70 +92,107 @@ func readRestriction(baseSetting string, text string) (CfgRestriction, error) {
 		// If the restriction text isn't a defined string, parse it as an
 		// expression.
 		r.Code = CFG_RESTRICTION_CODE_EXPR
-
-		var err error
-		if r.Expr, err = readRestrictionExpr(text); err != nil {
-			return r, err
-		}
+		r.Expr = text
 	}
 
 	return r, nil
 }
 
-func (cfg *Cfg) violationText(entry CfgEntry, r CfgRestriction) string {
-	if r.Code == CFG_RESTRICTION_CODE_NOTNULL {
-		return entry.Name + " must not be null"
+func translateShorthandExpr(expr string, baseSetting string) string {
+	tokens, err := parse.Lex(expr)
+	if err != nil {
+		return ""
 	}
 
-	str := fmt.Sprintf("%s=%s ", entry.Name, entry.Value)
-	if r.Expr.ReqVal {
-		str += fmt.Sprintf("requires %s be set", r.Expr.ReqSetting)
-	} else {
-		str += fmt.Sprintf("requires %s not be set", r.Expr.ReqSetting)
+	ifi := -1
+	var ift *parse.Token
+	for i, t := range tokens {
+		if t.Code == parse.TOKEN_IDENT && t.Text == "if" {
+			ifi = i
+			ift = &t
+			break
+		}
 	}
 
-	str += fmt.Sprintf(", but %s", r.Expr.ReqSetting)
-	reqEntry, ok := cfg.Settings[r.Expr.ReqSetting]
-	if !ok {
-		str += "undefined"
-	} else {
-		str += fmt.Sprintf("=%s", reqEntry.Value)
+	if ifi == 0 || ifi == len(tokens)-1 {
+		return ""
+	}
+
+	if ifi == -1 {
+		if parse.FindBinaryToken(tokens) == -1 {
+			// [!]<req-setting>
+			return fmt.Sprintf("(%s) || !%s", expr, baseSetting)
+		} else {
+			// Full expression
+			return ""
+		}
 	}
 
-	return str
+	if parse.FindBinaryToken(tokens[ifi+1:]) == -1 {
+		// [!]<req-setting> if <base-val>
+		return fmt.Sprintf("(%s) || %s != (%s)",
+			expr[:ift.Offset], baseSetting, expr[tokens[ifi+1].Offset:])
+	} else {
+		// [!]<req-setting> if <expression>
+		return fmt.Sprintf("(%s) || !(%s)",
+			expr[:ift.Offset], expr[tokens[ifi+1].Offset:])
+	}
 }
 
-func (r *CfgRestriction) relevantSettingNames() []string {
-	switch r.Code {
-	case CFG_RESTRICTION_CODE_NOTNULL:
-		return []string{r.BaseSetting}
+func normalizeExpr(expr string, baseSetting string) string {
+	shexpr := translateShorthandExpr(expr, baseSetting)
+	if shexpr != "" {
+		log.Debugf("Translating shorthand restriction: `%s` ==> `%s`",
+			expr, shexpr)
+		expr = shexpr
+	}
 
-	case CFG_RESTRICTION_CODE_EXPR:
-		return []string{r.BaseSetting, r.Expr.ReqSetting}
+	return expr
+}
 
-	default:
-		panic("Invalid restriction code: " + string(r.Code))
+func (cfg *Cfg) violationText(entry CfgEntry, r CfgRestriction) string {
+	prefix := fmt.Sprintf("%s(%s) ", entry.Name, entry.Value)
+	if r.Code == CFG_RESTRICTION_CODE_NOTNULL {
+		return prefix + "must not be null"
+	} else {
+		return prefix + "requires: " + r.Expr
 	}
 }
 
-func (cfg *Cfg) restrictionMet(r CfgRestriction) bool {
+func (r *CfgRestriction) relevantSettingNames() []string {
+	names := []string{r.BaseSetting}
+
+	if r.Code == CFG_RESTRICTION_CODE_EXPR {
+		tokens, _ := parse.Lex(normalizeExpr(r.Expr, r.BaseSetting))
+		for _, token := range tokens {
+			if token.Code == parse.TOKEN_IDENT {
+				names = append(names, token.Text)
+			}
+		}
+	}
+
+	return names
+}
+
+func (cfg *Cfg) restrictionMet(
+	r CfgRestriction, settings map[string]string) bool {
+
 	baseEntry := cfg.Settings[r.BaseSetting]
-	baseVal := baseEntry.IsTrue()
 
 	switch r.Code {
 	case CFG_RESTRICTION_CODE_NOTNULL:
 		return baseEntry.Value != ""
 
 	case CFG_RESTRICTION_CODE_EXPR:
-		if baseVal != r.Expr.BaseVal {
-			// Restriction does not apply.
+		expr := normalizeExpr(r.Expr, r.BaseSetting)
+		val, err := parse.ParseAndEval(expr, settings)
+		if err != nil {
+			util.StatusMessage(util.VERBOSITY_QUIET,
+				"WARNING: ignoring illegal expression for setting \"%s\": "+
+					"`%s` %s\n", r.BaseSetting, r.Expr, err.Error())
 			return true
 		}
-
-		reqEntry, ok := cfg.Settings[r.Expr.ReqSetting]
-		reqVal := ok && reqEntry.IsTrue()
-
-		return reqVal == r.Expr.ReqVal
+		return val
 
 	default:
 		panic("Invalid restriction code: " + string(r.Code))
diff --git a/newt/syscfg/syscfg.go b/newt/syscfg/syscfg.go
index 7b861f1e..2bd481bf 100644
--- a/newt/syscfg/syscfg.go
+++ b/newt/syscfg/syscfg.go
@@ -36,6 +36,7 @@ import (
 	"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"
 )
@@ -128,44 +129,29 @@ func NewCfg() Cfg {
 	}
 }
 
-// Determines if a syscfg value is "true".  Only "" and a string representing 0
-// are considered false; everything else is true.
-func ValueIsTrue(val string) bool {
-	if val == "" {
-		return false
-	}
-
-	i, success := util.AtoiNoOctTry(val)
-	return !success || i != 0
-}
-
-func (cfg *Cfg) Features() map[string]bool {
-	features := map[string]bool{}
+func (cfg *Cfg) SettingValues() map[string]string {
+	values := make(map[string]string, len(cfg.Settings))
 	for k, v := range cfg.Settings {
-		if v.IsTrue() {
-			features[k] = true
-		}
+		values[k] = v.Value
 	}
 
-	return features
+	return values
 }
 
-func (cfg *Cfg) FeaturesForLpkg(lpkg *pkg.LocalPackage) map[string]bool {
-	features := cfg.Features()
+func (cfg *Cfg) SettingsForLpkg(lpkg *pkg.LocalPackage) map[string]string {
+	settings := cfg.SettingValues()
 
 	for k, v := range lpkg.InjectedSettings() {
-		_, ok := features[k]
+		_, ok := settings[k]
 		if ok {
 			log.Warnf("Attempt to override syscfg setting %s with "+
 				"injected feature from package %s", k, lpkg.Name())
 		} else {
-			if ValueIsTrue(v) {
-				features[k] = true
-			}
+			settings[k] = v
 		}
 	}
 
-	return features
+	return settings
 }
 
 func (point CfgPoint) Name() string {
@@ -181,7 +167,7 @@ func (point CfgPoint) IsInjected() bool {
 }
 
 func (entry *CfgEntry) IsTrue() bool {
-	return ValueIsTrue(entry.Value)
+	return parse.ValueIsTrue(entry.Value)
 }
 
 func (entry *CfgEntry) appendValue(lpkg *pkg.LocalPackage, value interface{}) {
@@ -192,6 +178,10 @@ func (entry *CfgEntry) appendValue(lpkg *pkg.LocalPackage, value interface{}) {
 }
 
 func historyToString(history []CfgPoint) string {
+	if len(history) == 0 {
+		return "(undefined)"
+	}
+
 	str := "["
 	for i, _ := range history {
 		if i != 0 {
@@ -316,24 +306,22 @@ func readSetting(name string, lpkg *pkg.LocalPackage,
 }
 
 func (cfg *Cfg) readDefsOnce(lpkg *pkg.LocalPackage,
-	features map[string]bool) error {
-	v := lpkg.SyscfgV
+	settings map[string]string) error {
+	yc := lpkg.SyscfgY
 
-	lfeatures := cfg.FeaturesForLpkg(lpkg)
-	for k, v := range features {
-		if v {
-			lfeatures[k] = true
-		}
+	lsettings := cfg.SettingsForLpkg(lpkg)
+	for k, v := range settings {
+		lsettings[k] = v
 	}
-	for k, _ := range lfeatures {
-		if _, ok := features[k]; ok == false {
-			delete(lfeatures, k)
+	for k, _ := range lsettings {
+		if _, ok := settings[k]; ok == false {
+			delete(lsettings, k)
 		}
 	}
 
-	settings := newtutil.GetStringMapFeatures(v, lfeatures, "syscfg.defs")
-	if settings != nil {
-		for k, v := range settings {
+	defs := yc.GetValStringMap("syscfg.defs", lsettings)
+	if defs != nil {
+		for k, v := range defs {
 			vals := v.(map[interface{}]interface{})
 			entry, err := readSetting(k, lpkg, vals)
 			if err != nil {
@@ -361,22 +349,20 @@ func (cfg *Cfg) readDefsOnce(lpkg *pkg.LocalPackage,
 }
 
 func (cfg *Cfg) readValsOnce(lpkg *pkg.LocalPackage,
-	features map[string]bool) error {
-	v := lpkg.SyscfgV
+	settings map[string]string) error {
+	yc := lpkg.SyscfgY
 
-	lfeatures := cfg.FeaturesForLpkg(lpkg)
-	for k, v := range features {
-		if v {
-			lfeatures[k] = true
-		}
+	lsettings := cfg.SettingsForLpkg(lpkg)
+	for k, v := range settings {
+		lsettings[k] = v
 	}
-	for k, _ := range lfeatures {
-		if _, ok := features[k]; ok == false {
-			delete(lfeatures, k)
+	for k, _ := range lsettings {
+		if _, ok := settings[k]; ok == false {
+			delete(lsettings, k)
 		}
 	}
 
-	values := newtutil.GetStringMapFeatures(v, lfeatures, "syscfg.vals")
+	values := yc.GetValStringMap("syscfg.vals", lsettings)
 	for k, v := range values {
 		entry, ok := cfg.Settings[k]
 		if ok {
@@ -439,10 +425,11 @@ func (cfg *Cfg) settingsOfType(typ CfgSettingType) []CfgEntry {
 }
 
 func (cfg *Cfg) detectViolations() {
+	settings := cfg.SettingValues()
 	for _, entry := range cfg.Settings {
 		var ev []CfgRestriction
 		for _, r := range entry.Restrictions {
-			if !cfg.restrictionMet(r) {
+			if !cfg.restrictionMet(r, settings) {
 				ev = append(ev, r)
 			}
 		}
@@ -683,20 +670,20 @@ func categorizePkgs(
 }
 
 func (cfg *Cfg) readDefsForPkgType(lpkgs []*pkg.LocalPackage,
-	features map[string]bool) error {
+	settings map[string]string) error {
 
 	for _, lpkg := range lpkgs {
-		if err := cfg.readDefsOnce(lpkg, features); err != nil {
+		if err := cfg.readDefsOnce(lpkg, settings); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 func (cfg *Cfg) readValsForPkgType(lpkgs []*pkg.LocalPackage,
-	features map[string]bool) error {
+	settings map[string]string) error {
 
 	for _, lpkg := range lpkgs {
-		if err := cfg.readValsOnce(lpkg, features); err != nil {
+		if err := cfg.readValsOnce(lpkg, settings); err != nil {
 			return err
 		}
 	}
@@ -713,7 +700,7 @@ func (cfg *Cfg) detectAmbiguities() {
 }
 
 func Read(lpkgs []*pkg.LocalPackage, apis []string,
-	injectedSettings map[string]string, features map[string]bool,
+	injectedSettings map[string]string, settings map[string]string,
 	flashMap flash.FlashMap) (Cfg, error) {
 
 	cfg := NewCfg()
@@ -728,9 +715,7 @@ func Read(lpkgs []*pkg.LocalPackage, apis []string,
 			}},
 		}
 
-		if ValueIsTrue(v) {
-			features[k] = true
-		}
+		settings[k] = v
 	}
 
 	// Read system configuration files.  In case of conflicting settings, the
@@ -751,7 +736,7 @@ func Read(lpkgs []*pkg.LocalPackage, apis []string,
 		pkg.PACKAGE_TYPE_APP,
 		pkg.PACKAGE_TYPE_TARGET,
 	} {
-		if err := cfg.readDefsForPkgType(lpkgMap[ptype], features); err != nil {
+		if err := cfg.readDefsForPkgType(lpkgMap[ptype], settings); err != nil {
 			return cfg, err
 		}
 	}
@@ -763,7 +748,7 @@ func Read(lpkgs []*pkg.LocalPackage, apis []string,
 		pkg.PACKAGE_TYPE_APP,
 		pkg.PACKAGE_TYPE_TARGET,
 	} {
-		if err := cfg.readValsForPkgType(lpkgMap[ptype], features); err != nil {
+		if err := cfg.readValsForPkgType(lpkgMap[ptype], settings); err != nil {
 			return cfg, err
 		}
 	}
diff --git a/newt/target/target.go b/newt/target/target.go
index bb52f4db..d814efa4 100644
--- a/newt/target/target.go
+++ b/newt/target/target.go
@@ -27,6 +27,7 @@ import (
 	"strconv"
 	"strings"
 
+	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/repo"
@@ -73,7 +74,7 @@ func (target *Target) Init(basePkg *pkg.LocalPackage) {
 }
 
 func (target *Target) Load(basePkg *pkg.LocalPackage) error {
-	v, err := util.ReadConfig(basePkg.BasePath(),
+	yc, err := newtutil.ReadConfig(basePkg.BasePath(),
 		strings.TrimSuffix(TARGET_FILENAME, ".yml"))
 	if err != nil {
 		return err
@@ -81,8 +82,7 @@ func (target *Target) Load(basePkg *pkg.LocalPackage) error {
 
 	target.Vars = map[string]string{}
 
-	settings := v.AllSettings()
-	for k, v := range settings {
+	for k, v := range yc.AllSettings() {
 		target.Vars[k] = fmt.Sprintf("%v", v)
 	}
 
diff --git a/newt/toolchain/compiler.go b/newt/toolchain/compiler.go
index 5eb4ef2f..cc19968b 100644
--- a/newt/toolchain/compiler.go
+++ b/newt/toolchain/compiler.go
@@ -38,8 +38,8 @@ import (
 	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/newt/project"
 	"mynewt.apache.org/newt/newt/symbol"
+	"mynewt.apache.org/newt/newt/ycfg"
 	"mynewt.apache.org/newt/util"
-	"mynewt.apache.org/newt/viper"
 )
 
 const COMPILER_FILENAME string = "compiler.yml"
@@ -246,17 +246,13 @@ func NewCompiler(compilerDir string, dstDir string,
 	return c, nil
 }
 
-func loadFlags(v *viper.Viper, features map[string]bool,
-	key string) []string {
-
+func loadFlags(yc ycfg.YCfg, settings map[string]string, key string) []string {
 	flags := []string{}
 
-	rawFlags := newtutil.GetStringSliceFeatures(v, features, key)
+	rawFlags := yc.GetValStringSlice(key, settings)
 	for _, rawFlag := range rawFlags {
 		if strings.HasPrefix(rawFlag, key) {
-			expandedFlags := newtutil.GetStringSliceFeatures(v, features,
-				rawFlag)
-
+			expandedFlags := yc.GetValStringSlice(rawFlag, settings)
 			flags = append(flags, expandedFlags...)
 		} else {
 			flags = append(flags, strings.Trim(rawFlag, "\n"))
@@ -267,45 +263,32 @@ func loadFlags(v *viper.Viper, features map[string]bool,
 }
 
 func (c *Compiler) load(compilerDir string, buildProfile string) error {
-	v, err := util.ReadConfig(compilerDir, "compiler")
+	yc, err := newtutil.ReadConfig(compilerDir, "compiler")
 	if err != nil {
 		return err
 	}
 
-	features := map[string]bool{
-		buildProfile:                  true,
-		strings.ToUpper(runtime.GOOS): true,
+	settings := map[string]string{
+		buildProfile:                  "1",
+		strings.ToUpper(runtime.GOOS): "1",
 	}
 
-	c.ccPath = newtutil.GetStringFeatures(v, features, "compiler.path.cc")
-	c.cppPath = newtutil.GetStringFeatures(v, features, "compiler.path.cpp")
-	c.asPath = newtutil.GetStringFeatures(v, features, "compiler.path.as")
-	c.arPath = newtutil.GetStringFeatures(v, features, "compiler.path.archive")
-	c.odPath = newtutil.GetStringFeatures(v, features, "compiler.path.objdump")
-	c.osPath = newtutil.GetStringFeatures(v, features, "compiler.path.objsize")
-	c.ocPath = newtutil.GetStringFeatures(v, features, "compiler.path.objcopy")
+	c.ccPath = yc.GetValString("compiler.path.cc", settings)
+	c.cppPath = yc.GetValString("compiler.path.cpp", settings)
+	c.asPath = yc.GetValString("compiler.path.as", settings)
+	c.arPath = yc.GetValString("compiler.path.archive", settings)
+	c.odPath = yc.GetValString("compiler.path.objdump", settings)
+	c.osPath = yc.GetValString("compiler.path.objsize", settings)
+	c.ocPath = yc.GetValString("compiler.path.objcopy", settings)
 
-	c.lclInfo.Cflags = loadFlags(v, features, "compiler.flags")
-	c.lclInfo.Lflags = loadFlags(v, features, "compiler.ld.flags")
-	c.lclInfo.Aflags = loadFlags(v, features, "compiler.as.flags")
+	c.lclInfo.Cflags = loadFlags(yc, settings, "compiler.flags")
+	c.lclInfo.Lflags = loadFlags(yc, settings, "compiler.ld.flags")
+	c.lclInfo.Aflags = loadFlags(yc, settings, "compiler.as.flags")
 
-	c.ldResolveCircularDeps, err = newtutil.GetBoolFeatures(v, features,
-		"compiler.ld.resolve_circular_deps")
-	if err != nil {
-		return err
-	}
-
-	c.ldMapFile, err = newtutil.GetBoolFeatures(v, features,
-		"compiler.ld.mapfile")
-	if err != nil {
-		return err
-	}
-
-	c.ldBinFile, err = newtutil.GetBoolFeaturesDflt(v, features,
-		"compiler.ld.binfile", true)
-	if err != nil {
-		return err
-	}
+	c.ldResolveCircularDeps = yc.GetValBool(
+		"compiler.ld.resolve_circular_deps", settings)
+	c.ldMapFile = yc.GetValBool("compiler.ld.mapfile", settings)
+	c.ldBinFile = yc.GetValBoolDflt("compiler.ld.binfile", settings, true)
 
 	if len(c.lclInfo.Cflags) == 0 {
 		// Assume no Cflags implies an unsupported build profile.
diff --git a/newt/vendor/mynewt.apache.org/newt/util/util.go b/newt/vendor/mynewt.apache.org/newt/util/util.go
index 261762e7..8657a673 100644
--- a/newt/vendor/mynewt.apache.org/newt/util/util.go
+++ b/newt/vendor/mynewt.apache.org/newt/util/util.go
@@ -37,8 +37,6 @@ import (
 	"time"
 
 	log "github.com/Sirupsen/logrus"
-
-	"mynewt.apache.org/newt/viper"
 )
 
 var Verbosity int
@@ -255,23 +253,6 @@ func Init(logLevel log.Level, logFile string, verbosity int) error {
 	return nil
 }
 
-// Read in the configuration file specified by name, in path
-// return a new viper config object if successful, and error if not
-func ReadConfig(path string, name string) (*viper.Viper, error) {
-	v := viper.New()
-	v.SetConfigType("yaml")
-	v.SetConfigName(name)
-	v.AddConfigPath(path)
-
-	err := v.ReadInConfig()
-	if err != nil {
-		return nil, NewNewtError(fmt.Sprintf("Error reading %s.yml: %s",
-			filepath.Join(path, name), err.Error()))
-	} else {
-		return v, nil
-	}
-}
-
 func LogShellCmd(cmdStrs []string, env []string) {
 	envLogStr := ""
 	if len(env) > 0 {
diff --git a/newt/ycfg/ycfg.go b/newt/ycfg/ycfg.go
new file mode 100644
index 00000000..28b85640
--- /dev/null
+++ b/newt/ycfg/ycfg.go
@@ -0,0 +1,424 @@
+package ycfg
+
+import (
+	"fmt"
+	"strings"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cast"
+
+	"mynewt.apache.org/newt/newt/parse"
+)
+
+// YAML configuration object.  This is a substitute for a viper configuration
+// object, with the following newt-specific advantages:
+// 1. Case sensitive.
+// 2. Efficient conditionals based on syscfg values.
+//
+// A single YCfg setting is implemented as a tree of nodes.  Each word in the
+// setting name represents a node; each "." in the name is a link in the tree.
+// For example, the following syscfg lines:
+//
+// OS_MAIN_STACK_SIZE: 100
+// OS_MAIN_STACK_SIZE.BLE_DEVICE: 200
+// OS_MAIN_STACK_SIZE.SHELL_TASK: 300
+//
+// Is represented as the following tree:
+//
+//                      [OS_MAIN_STACK_SIZE (100)]
+//                     /                          \
+//            [BLE_DEVICE (200)]           [SHELL_TASK (300)]
+//
+// This allows us to quickly determine the value of OS_MAIN_STACK_SIZE.  After
+// finding the OS_MAIN_STACK_SIZE node, the logic is something like this:
+//
+// Is BLE_DEVICE true? --> 200
+// Is SHELL_TASK true? --> 300
+// Else: --> 100
+//
+// The tree structure also allows for arbitrary expressions as conditionals, as
+// opposed to simple setting names.  For example:
+//
+// OS_MAIN_STACK_SIZE: 100
+// OS_MAIN_STACK_SIZE.'(BLE_DEVICE && !SHELL_TASK): 200
+// OS_MAIN_STACK_SIZE.'(SHELL_TASK && !BLE_DEVICE): 300
+// OS_MAIN_STACK_SIZE.'(SHELL_TASK && BLE_DEVICE):  400
+//
+// Since each expression is a child node of the setting in question, they are
+// all known at the time of the lookup.  To determine the value of the setting,
+// each expression is parsed, and only the one evaluating to true is selected.
+type YCfg map[string]*YCfgNode
+
+type YCfgEntry struct {
+	Value interface{}
+	Expr  string
+}
+
+type YCfgNode struct {
+	Overwrite bool
+	Name      string
+	Value     interface{}
+	Children  YCfg
+	Parent    *YCfgNode
+}
+
+func (node *YCfgNode) addChild(name string) (*YCfgNode, error) {
+	if node.Children == nil {
+		node.Children = YCfg{}
+	}
+
+	if node.Children[name] != nil {
+		return nil, fmt.Errorf("Duplicate YCfgNode: %s", name)
+	}
+
+	child := &YCfgNode{
+		Name:   name,
+		Parent: node,
+	}
+	node.Children[name] = child
+
+	return child, nil
+}
+
+func (yc YCfg) Replace(key string, val interface{}) error {
+	elems := strings.Split(key, ".")
+	if len(elems) == 0 {
+		return fmt.Errorf("Invalid ycfg key: \"\"")
+	}
+
+	var overwrite bool
+	if elems[len(elems)-1] == "OVERWRITE" {
+		overwrite = true
+		elems = elems[:len(elems)-1]
+	}
+
+	var parent *YCfgNode
+	for i, e := range elems {
+		var parentChildren YCfg
+		if parent == nil {
+			parentChildren = yc
+		} else {
+			if parent.Children == nil {
+				parent.Children = YCfg{}
+			}
+			parentChildren = parent.Children
+		}
+		child := parentChildren[e]
+		if child == nil {
+			var err error
+			if parent != nil {
+				child, err = parent.addChild(e)
+				if err != nil {
+					return err
+				}
+			} else {
+				child = &YCfgNode{Name: e}
+				parentChildren[e] = child
+			}
+		}
+
+		if i == len(elems)-1 {
+			child.Overwrite = overwrite
+			child.Value = val
+		}
+
+		parent = child
+	}
+
+	return nil
+}
+
+func NewYCfg(kv map[string]interface{}) (YCfg, error) {
+	yc := YCfg{}
+
+	for k, v := range kv {
+		if err := yc.Replace(k, v); err != nil {
+			return nil, err
+		}
+	}
+
+	return yc, nil
+}
+
+func (yc YCfg) find(key string) *YCfgNode {
+	elems := strings.Split(key, ".")
+	if len(elems) == 0 {
+		return nil
+	}
+
+	cur := &YCfgNode{
+		Children: yc,
+	}
+	for _, e := range elems {
+		if cur.Children == nil {
+			return nil
+		}
+
+		cur = cur.Children[e]
+		if cur == nil {
+			return nil
+		}
+	}
+
+	return cur
+}
+
+func (yc YCfg) Get(key string, settings map[string]string) []YCfgEntry {
+	node := yc.find(key)
+	if node == nil {
+		return nil
+	}
+
+	entries := []YCfgEntry{}
+
+	if node.Value != nil {
+		entry := YCfgEntry{Value: node.Value}
+		entries = append(entries, entry)
+	}
+
+	for _, child := range node.Children {
+		val, err := parse.ParseAndEval(child.Name, settings)
+		if err != nil {
+			log.Error(err.Error())
+		} else if val {
+			entry := YCfgEntry{
+				Value: child.Value,
+				Expr:  child.Name,
+			}
+			if child.Overwrite {
+				return []YCfgEntry{entry}
+			}
+
+			entries = append(entries, entry)
+		}
+	}
+
+	return entries
+}
+
+func (yc YCfg) GetSlice(key string, settings map[string]string) []YCfgEntry {
+	sliceEntries := yc.Get(key, settings)
+	if len(sliceEntries) == 0 {
+		return nil
+	}
+
+	result := []YCfgEntry{}
+	for _, sliceEntry := range sliceEntries {
+		if sliceEntry.Value != nil {
+			slice, err := cast.ToSliceE(sliceEntry.Value)
+			if err != nil {
+				// Not a slice.  Put the single value in a new slice.
+				slice = []interface{}{sliceEntry.Value}
+			}
+			for _, v := range slice {
+				entry := YCfgEntry{
+					Value: v,
+					Expr:  sliceEntry.Expr,
+				}
+				result = append(result, entry)
+			}
+		}
+	}
+
+	return result
+}
+
+func (yc YCfg) GetStringMap(
+	key string, settings map[string]string) map[string]YCfgEntry {
+
+	mapEntries := yc.Get(key, settings)
+	if len(mapEntries) == 0 {
+		return nil
+	}
+
+	result := map[string]YCfgEntry{}
+
+	for _, mapEntry := range mapEntries {
+		for k, v := range cast.ToStringMap(mapEntry.Value) {
+			entry := YCfgEntry{
+				Value: v,
+				Expr:  mapEntry.Expr,
+			}
+
+			// XXX: Report collisions?
+			result[k] = entry
+		}
+	}
+
+	return result
+}
+
+func (yc YCfg) GetValStringMap(
+	key string, settings map[string]string) map[string]interface{} {
+
+	entryMap := yc.GetStringMap(key, settings)
+
+	smap := make(map[string]interface{}, len(entryMap))
+	for k, v := range entryMap {
+		if v.Value != nil {
+			smap[k] = v.Value
+		}
+	}
+
+	return smap
+}
+
+func (yc YCfg) GetFirst(key string, settings map[string]string) (YCfgEntry, bool) {
+	entries := yc.Get(key, settings)
+	if len(entries) == 0 {
+		return YCfgEntry{}, false
+	}
+
+	return entries[0], true
+}
+
+func (yc YCfg) GetFirstVal(key string, settings map[string]string) interface{} {
+	entry, ok := yc.GetFirst(key, settings)
+	if !ok {
+		return nil
+	}
+
+	return entry.Value
+}
+
+func (yc YCfg) GetValString(key string, settings map[string]string) string {
+	entry, ok := yc.GetFirst(key, settings)
+	if !ok {
+		return ""
+	} else {
+		return cast.ToString(entry.Value)
+	}
+}
+
+func (yc YCfg) GetValInt(key string, settings map[string]string) int {
+	entry, ok := yc.GetFirst(key, settings)
+	if !ok {
+		return 0
+	} else {
+		return cast.ToInt(entry.Value)
+	}
+}
+
+func (yc YCfg) GetValBoolDflt(key string, settings map[string]string,
+	dflt bool) bool {
+
+	entry, ok := yc.GetFirst(key, settings)
+	if !ok {
+		return dflt
+	} else {
+		return cast.ToBool(entry.Value)
+	}
+}
+
+func (yc YCfg) GetValBool(key string, settings map[string]string) bool {
+	return yc.GetValBoolDflt(key, settings, false)
+}
+
+func (yc YCfg) GetValStringSlice(
+	key string, settings map[string]string) []string {
+
+	entries := yc.GetSlice(key, settings)
+	if len(entries) == 0 {
+		return nil
+	}
+
+	vals := make([]string, len(entries))
+	for i, e := range entries {
+		if e.Value != nil {
+			vals[i] = cast.ToString(e.Value)
+		}
+	}
+
+	return vals
+}
+
+func (yc YCfg) GetValStringSliceNonempty(
+	key string, settings map[string]string) []string {
+
+	strs := yc.GetValStringSlice(key, settings)
+	filtered := make([]string, 0, len(strs))
+	for _, s := range strs {
+		if s != "" {
+			filtered = append(filtered, s)
+		}
+	}
+
+	return filtered
+}
+
+func (yc YCfg) GetValStringMapString(key string,
+	settings map[string]string) map[string]string {
+
+	entryMap := yc.GetStringMap(key, settings)
+
+	valMap := make(map[string]string, len(entryMap))
+	for k, v := range entryMap {
+		if v.Value != nil {
+			valMap[k] = cast.ToString(v.Value)
+		}
+	}
+
+	return valMap
+}
+
+func (node *YCfgNode) FullName() string {
+	tokens := []string{}
+
+	for n := node; n != nil; n = n.Parent {
+		tokens = append(tokens, n.Name)
+	}
+
+	last := len(tokens) - 1
+	for i := 0; i < len(tokens)/2; i++ {
+		tokens[i], tokens[last-i] = tokens[last-i], tokens[i]
+	}
+
+	return strings.Join(tokens, ".")
+}
+
+func (yc YCfg) Traverse(cb func(node *YCfgNode, depth int)) {
+	var traverseLevel func(
+		node *YCfgNode,
+		cb func(node *YCfgNode, depth int),
+		depth int)
+
+	traverseLevel = func(
+		node *YCfgNode,
+		cb func(node *YCfgNode, depth int),
+		depth int) {
+
+		cb(node, depth)
+		for _, child := range node.Children {
+			traverseLevel(child, cb, depth+1)
+		}
+	}
+
+	for _, n := range yc {
+		traverseLevel(n, cb, 0)
+	}
+}
+
+func (yc YCfg) AllSettings() map[string]interface{} {
+	settings := map[string]interface{}{}
+
+	yc.Traverse(func(node *YCfgNode, depth int) {
+		if node.Value != nil {
+			settings[node.FullName()] = node.Value
+		}
+	})
+
+	return settings
+}
+
+func (yc YCfg) String() string {
+	lines := make([]string, 0, len(yc))
+	yc.Traverse(func(node *YCfgNode, depth int) {
+		line := strings.Repeat(" ", depth*4) + node.Name
+		if node.Value != nil {
+			line += fmt.Sprintf(": %+v", node.Value)
+		}
+		lines = append(lines, line)
+	})
+
+	return strings.Join(lines, "\n")
+}
diff --git a/util/util.go b/util/util.go
index 261762e7..8657a673 100644
--- a/util/util.go
+++ b/util/util.go
@@ -37,8 +37,6 @@ import (
 	"time"
 
 	log "github.com/Sirupsen/logrus"
-
-	"mynewt.apache.org/newt/viper"
 )
 
 var Verbosity int
@@ -255,23 +253,6 @@ func Init(logLevel log.Level, logFile string, verbosity int) error {
 	return nil
 }
 
-// Read in the configuration file specified by name, in path
-// return a new viper config object if successful, and error if not
-func ReadConfig(path string, name string) (*viper.Viper, error) {
-	v := viper.New()
-	v.SetConfigType("yaml")
-	v.SetConfigName(name)
-	v.AddConfigPath(path)
-
-	err := v.ReadInConfig()
-	if err != nil {
-		return nil, NewNewtError(fmt.Sprintf("Error reading %s.yml: %s",
-			filepath.Join(path, name), err.Error()))
-	} else {
-		return v, nil
-	}
-}
-
 func LogShellCmd(cmdStrs []string, env []string) {
 	envLogStr := ""
 	if len(env) > 0 {


 

----------------------------------------------------------------
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