You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2016/03/07 12:56:22 UTC

[2/2] incubator-mynewt-newt git commit: newt build command

newt build command

The test command probably doesn't work
The binary paths are ridiculous (way too many subdirectories)
Lots more...


Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/commit/6f357ff9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/tree/6f357ff9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/diff/6f357ff9

Branch: refs/heads/rename_project_app
Commit: 6f357ff90e24c3036aca80b0fff8db4c8d06d3b3
Parents: ca12c25
Author: Christopher Collins <cc...@gmail.com>
Authored: Mon Mar 7 03:43:53 2016 -0800
Committer: Christopher Collins <cc...@gmail.com>
Committed: Mon Mar 7 03:56:03 2016 -0800

----------------------------------------------------------------------
 newt/builder/build.go        | 281 +++++++++++++++++++++++++++++++++++---
 newt/builder/buildpackage.go | 196 ++++++++++++++++----------
 newt/builder/cmds.go         |  19 ++-
 newt/cli/util.go             |  41 +-----
 newt/newt.go                 |   2 +
 newt/pkg/localpackage.go     |  22 ++-
 newt/pkg/package.go          |   4 +-
 newt/project/project.go      |   7 +-
 newt/toolchain/compiler.go   | 232 +++++++++++++++++++------------
 newt/toolchain/deps.go       |  67 ++++++---
 10 files changed, 613 insertions(+), 258 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/builder/build.go
----------------------------------------------------------------------
diff --git a/newt/builder/build.go b/newt/builder/build.go
index 057c28c..91fa80d 100644
--- a/newt/builder/build.go
+++ b/newt/builder/build.go
@@ -20,14 +20,21 @@
 package builder
 
 import (
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"mynewt.apache.org/newt/newt/cli"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/target"
+	"mynewt.apache.org/newt/newt/toolchain"
 	"mynewt.apache.org/newt/util"
 )
 
 type Builder struct {
 	Packages   map[*pkg.LocalPackage]*BuildPackage
 	identities map[string]bool
+	apis       map[string]*BuildPackage
 
 	target *target.Target
 }
@@ -40,29 +47,61 @@ func (b *Builder) AddIdentity(identity string) {
 	b.identities[identity] = true
 }
 
-func (b *Builder) GetPackage(pkg *pkg.LocalPackage) (*BuildPackage, bool) {
+func (b *Builder) AddPackage(pkg *pkg.LocalPackage) {
+	// Don't allow nil entries to the map
 	if pkg == nil {
-		panic("Package should not equal NIL")
+		panic("Cannot add nil package builder map")
 	}
 
-	bpkg, ok := b.Packages[pkg]
-	if !ok {
-		return nil, false
+	b.Packages[pkg] = NewBuildPackage(pkg)
+}
+
+func (b *Builder) AddApi(apiString string, bpkg *BuildPackage) {
+	curBpkg := b.apis[apiString]
+	if curBpkg != nil {
+		cli.StatusMessage(util.VERBOSITY_QUIET,
+			"Warning: API conflict: %s <-> %s\n", curBpkg.Name(), bpkg.Name())
 	} else {
-		return bpkg, true
+		b.apis[apiString] = bpkg
 	}
 }
 
-func (b *Builder) AddPackage(pkg *pkg.LocalPackage) {
-	// Don't allow nil entries to the map
-	if pkg == nil {
-		panic("Cannot add nil package builder map")
+func (b *Builder) populateApis() {
+	// Satisfy API requirements.
+	for _, bpkg := range b.Packages {
+		for _, api := range bpkg.Apis() {
+			b.AddApi(api, bpkg)
+		}
 	}
+}
 
-	b.Packages[pkg] = NewBuildPackage(pkg)
+// Returns true if at least one new API was satisfied.
+func (b *Builder) satisfyApis() (bool, error) {
+	moreWork := false
+
+	for _, bpkg := range b.Packages {
+		for _, reqApi := range bpkg.ReqApis() {
+			apiBpkg := b.apis[reqApi]
+			if apiBpkg == nil {
+				return false, util.NewNewtError(fmt.Sprintf("Could not "+
+					"satisfy API requirement; package=%s api=%s",
+					bpkg.Name(), reqApi))
+			}
+
+			dep := &pkg.Dependency{
+				Name: apiBpkg.Name(),
+				Repo: apiBpkg.Repo().Name(),
+			}
+			bpkg.AddDep(dep)
+			bpkg.DelReqApi(reqApi)
+			moreWork = true
+		}
+	}
+
+	return moreWork, nil
 }
 
-func (b *Builder) LoadDeps() error {
+func (b *Builder) loadDeps() error {
 	for {
 		reprocess := false
 		for _, bpkg := range b.Packages {
@@ -80,30 +119,233 @@ func (b *Builder) LoadDeps() error {
 			break
 		}
 	}
+	return nil
+}
+
+func (b *Builder) unloadAllPackages() {
+	for _, bpkg := range b.Packages {
+		bpkg.loaded = false
+	}
+}
+
+func (b *Builder) FinalizePackages() error {
+	keepGoing := true
+	for keepGoing {
+		var err error
+
+		keepGoing, err = b.satisfyApis()
+		if err != nil {
+			return err
+		}
+
+		if keepGoing {
+			b.unloadAllPackages()
+		}
+
+		err = b.loadDeps()
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Recursively compiles all the .c and .s files in the specified directory.
+// Architecture-specific files are also compiled.
+func buildDir(srcDir string, c *toolchain.Compiler, t *target.Target,
+	ignDirs []string) error {
+
+	var err error
+
+	cli.StatusMessage(util.VERBOSITY_VERBOSE,
+		"compiling src in base directory: %s\n", srcDir)
+
+	// First change into the pkg src directory, and build all the objects
+	// there
+	err = os.Chdir(srcDir)
+	if err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	// Don't recurse into destination directories.
+	ignDirs = append(ignDirs, "obj")
+	ignDirs = append(ignDirs, "bin")
+
+	// Ignore architecture-specific source files for now.  Use a temporary
+	// string array here so that the "arch" directory is not ignored in the
+	// subsequent architecture-specific compile phase.
+	baseIgnDirs := append(ignDirs, "arch")
+
+	if err = c.RecursiveCompile(toolchain.COMPILER_TYPE_C,
+		baseIgnDirs); err != nil {
+		return err
+	}
+
+	archDir := srcDir + "/arch/" + t.Arch + "/"
+	cli.StatusMessage(util.VERBOSITY_VERBOSE,
+		"compiling architecture specific src pkgs in directory: %s\n",
+		archDir)
+
+	if cli.NodeExist(archDir) {
+		if err := os.Chdir(archDir); err != nil {
+			return util.NewNewtError(err.Error())
+		}
+		if err := c.RecursiveCompile(toolchain.COMPILER_TYPE_C,
+			ignDirs); err != nil {
+			return err
+		}
+
+		// compile assembly sources in recursive compile as well
+		if err = c.RecursiveCompile(toolchain.COMPILER_TYPE_ASM,
+			ignDirs); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (b *Builder) archivePath(bpkg *BuildPackage) string {
+	binDir := bpkg.BasePath() + "/bin/" + b.target.Package().Name()
+	archiveFile := binDir + "/lib" + filepath.Base(bpkg.Name()) + ".a"
+	return archiveFile
+}
+
+func (b *Builder) buildPackage(bpkg *BuildPackage,
+	baseCi *toolchain.CompilerInfo, compilerPkg *pkg.LocalPackage) error {
+
+	srcDir := bpkg.BasePath() + "/src"
+	if cli.NodeNotExist(srcDir) {
+		// Nothing to compile.
+		return nil
+	}
+
+	c, err := toolchain.NewCompiler(compilerPkg.BasePath(),
+		"default", // XXX
+		b.target.Package().Name())
+	if err != nil {
+		return err
+	}
+	c.AddInfo(baseCi)
+
+	ci, err := bpkg.FullCompilerInfo(b)
+	if err != nil {
+		return err
+	}
+	c.AddInfo(ci)
+
+	// For now, ignore test code.  Tests get built later if the test
+	// identity is in effect.
+	ignDirs := []string{"test"}
+
+	// Compile the source files.
+	err = buildDir(srcDir, c, b.target, ignDirs)
+	if err != nil {
+		return err
+	}
+
+	// Archive everything into a static library, which can be linked with a
+	// main program
+	if err := os.Chdir(bpkg.BasePath() + "/"); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+
+	archiveFile := b.archivePath(bpkg)
+	if err := os.MkdirAll(filepath.Dir(archiveFile), 0755); err != nil {
+		return util.NewNewtError(err.Error())
+	}
+	if err = c.CompileArchive(archiveFile); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (b *Builder) linkApp(appPackage *BuildPackage,
+	baseCi *toolchain.CompilerInfo, compilerPkg *pkg.LocalPackage) error {
+
+	c, err := toolchain.NewCompiler(compilerPkg.BasePath(),
+		"default", // XXX
+		b.target.Package().Name())
+	if err != nil {
+		return err
+	}
+
+	c.AddInfo(baseCi)
+	binDir := appPackage.BasePath() + "/bin/" + b.target.Package().Name()
+	if cli.NodeNotExist(binDir) {
+		os.MkdirAll(binDir, 0755)
+	}
+
+	ci, err := appPackage.FullCompilerInfo(b)
+	if err != nil {
+		return err
+	}
+	c.AddInfo(ci)
+
+	pkgNames := []string{}
+	for _, bpkg := range b.Packages {
+		archivePath := b.archivePath(bpkg)
+		if cli.NodeExist(archivePath) {
+			pkgNames = append(pkgNames, archivePath)
+		}
+	}
+
+	elfFile := binDir + "/" + appPackage.Name()
+	err = c.CompileElf(elfFile, pkgNames)
+	if err != nil {
+		return err
+	}
 
 	return nil
 }
 
 func (b *Builder) Build() error {
+	compilerPkg := b.target.Compiler()
+	if compilerPkg == nil {
+		return util.NewNewtError("compiler package not found")
+	}
+
 	b.AddPackage(b.target.Bsp())
 	b.AddPackage(b.target.App())
 	b.AddPackage(b.target.Compiler())
 
-	if err := b.LoadDeps(); err != nil {
+	if err := b.FinalizePackages(); err != nil {
 		return err
 	}
 
-	bspPackage, ok := b.GetPackage(b.target.Bsp())
-	if !ok {
+	bspPackage := b.Packages[b.target.Bsp()]
+	if bspPackage == nil {
 		return util.NewNewtError("BSP package not found!")
 	}
 
-	baseCi := NewCompilerInfo()
-	baseCi.AddCompilerInfo(bspPackage.PackageCompilerInfo())
+	bspCi, err := bspPackage.FullCompilerInfo(b)
+	if err != nil {
+		return err
+	}
 
-	// Loop through all packages and build them
+	appPackage := b.Packages[b.target.App()]
+	if appPackage == nil {
+		return util.NewNewtError("App package not found")
+	}
+	appCi, err := appPackage.FullCompilerInfo(b)
+	if err != nil {
+		return err
+	}
+
+	baseCi := toolchain.NewCompilerInfo()
+	baseCi.AddCompilerInfo(bspCi)
+	baseCi.AddCompilerInfo(appCi)
+
+	for _, bpkg := range b.Packages {
+		err = b.buildPackage(bpkg, baseCi, compilerPkg)
+		if err != nil {
+			return err
+		}
+	}
 
-	// Now, get the App, and link the application
+	b.linkApp(appPackage, baseCi, compilerPkg)
 
 	return nil
 }
@@ -113,6 +355,7 @@ func (b *Builder) Init(target *target.Target) error {
 
 	b.Packages = map[*pkg.LocalPackage]*BuildPackage{}
 	b.identities = map[string]bool{}
+	b.apis = map[string]*BuildPackage{}
 
 	return nil
 }

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/builder/buildpackage.go
----------------------------------------------------------------------
diff --git a/newt/builder/buildpackage.go b/newt/builder/buildpackage.go
index e35fcca..0f61cf3 100644
--- a/newt/builder/buildpackage.go
+++ b/newt/builder/buildpackage.go
@@ -20,52 +20,101 @@
 package builder
 
 import (
+	"fmt"
+	"path/filepath"
+
 	"mynewt.apache.org/newt/newt/cli"
 	"mynewt.apache.org/newt/newt/pkg"
 	"mynewt.apache.org/newt/newt/project"
+	"mynewt.apache.org/newt/newt/toolchain"
 	"mynewt.apache.org/newt/util"
 )
 
-type CompilerInfo struct {
-	Includes []string
-	Cflags   []string
-	Lflags   []string
-	Aflags   []string
-}
-
 type BuildPackage struct {
 	*pkg.LocalPackage
 
-	pkgCi  *CompilerInfo
-	fullCi *CompilerInfo
+	fullCi *toolchain.CompilerInfo
 
 	isBsp bool
 
 	loaded bool
 }
 
-func (ci *CompilerInfo) AddCompilerInfo(newCi *CompilerInfo) {
-	ci.Includes = append(ci.Includes, newCi.Includes...)
-	ci.Cflags = append(ci.Cflags, newCi.Cflags...)
-	ci.Lflags = append(ci.Lflags, newCi.Lflags...)
-	ci.Aflags = append(ci.Aflags, newCi.Aflags...)
+// Recursively iterates through an pkg's dependencies, adding each pkg
+// encountered to the supplied set.
+func (bpkg *BuildPackage) collectDepsAux(b *Builder,
+	set *map[*BuildPackage]bool) error {
+
+	if (*set)[bpkg] {
+		return nil
+	}
+
+	(*set)[bpkg] = true
+
+	for _, dep := range bpkg.Deps() {
+		if dep.Name == "" {
+			break
+		}
+
+		// Get pkg structure
+		dpkg, err := project.GetProject().ResolveDependency(dep)
+		if err != nil {
+			return err
+		}
+
+		dbpkg := b.Packages[dpkg]
+		if dbpkg == nil {
+			return util.NewNewtError(fmt.Sprintf("Package not found (%s)",
+				dpkg.Name()))
+		}
+
+		err = dbpkg.collectDepsAux(b, set)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
 }
 
-func NewCompilerInfo() *CompilerInfo {
-	ci := &CompilerInfo{}
-	ci.Includes = []string{}
-	ci.Cflags = []string{}
-	ci.Lflags = []string{}
-	ci.Aflags = []string{}
+// Recursively iterates through an pkg's dependencies.  The resulting array
+// contains a pointer to each encountered pkg.
+func (bpkg *BuildPackage) collectDeps(b *Builder) ([]*BuildPackage, error) {
+	set := map[*BuildPackage]bool{}
+
+	err := bpkg.collectDepsAux(b, &set)
+	if err != nil {
+		return nil, err
+	}
+
+	arr := []*BuildPackage{}
+	for p, _ := range set {
+		arr = append(arr, p)
+	}
 
-	return ci
+	return arr, nil
 }
 
-func (bpkg *BuildPackage) PackageCompilerInfo() *CompilerInfo {
-	return bpkg.pkgCi
+// Calculates the include paths exported by the specified pkg and all of
+// its recursive dependencies.
+func (bpkg *BuildPackage) recursiveIncludePaths(b *Builder) ([]string, error) {
+	deps, err := bpkg.collectDeps(b)
+	if err != nil {
+		return nil, err
+	}
+
+	incls := []string{}
+	for _, p := range deps {
+		incls = append(incls, p.publicIncludeDirs(b)...)
+	}
+	for _, p := range deps {
+		incls = append(incls, p.publicIncludeDirs(b)...)
+	}
+
+	return incls, nil
 }
 
-func (bpkg *BuildPackage) FullCompilerInfo(b *Builder) (*CompilerInfo, error) {
+func (bpkg *BuildPackage) FullCompilerInfo(b *Builder) (*toolchain.CompilerInfo, error) {
 	if !bpkg.loaded {
 		return nil, util.NewNewtError("Package must be loaded before Compiler info is fetched")
 	}
@@ -74,29 +123,19 @@ func (bpkg *BuildPackage) FullCompilerInfo(b *Builder) (*CompilerInfo, error) {
 		return bpkg.fullCi, nil
 	}
 
-	ci := NewCompilerInfo()
-	ci.AddCompilerInfo(bpkg.pkgCi)
+	ci := toolchain.NewCompilerInfo()
+	ci.Cflags = cli.GetStringSliceIdentities(bpkg.Viper, b.Identities(),
+		"pkg.cflags")
+	ci.Lflags = cli.GetStringSliceIdentities(bpkg.Viper, b.Identities(),
+		"pkg.lflags")
+	ci.Aflags = cli.GetStringSliceIdentities(bpkg.Viper, b.Identities(),
+		"pkg.aflags")
 
-	// Go through every dependency, and add the compiler information for that
-	// dependency
-	for _, dep := range bpkg.Deps() {
-		pkg, err := project.GetProject().ResolveDependency(dep)
-		if err != nil {
-			return nil, err
-		}
-
-		if pkg == nil {
-			return nil, util.NewNewtError("Cannot resolve dep " + dep.String())
-		}
-
-		bpkg, ok := b.GetPackage(pkg)
-		if !ok {
-			return nil, util.NewNewtError("Unknown build info for package " + pkg.Name())
-		}
-
-		ci.AddCompilerInfo(bpkg.PackageCompilerInfo())
+	includePaths, err := bpkg.recursiveIncludePaths(b)
+	if err != nil {
+		return nil, err
 	}
-
+	ci.Includes = append(bpkg.privateIncludeDirs(b), includePaths...)
 	bpkg.fullCi = ci
 
 	return bpkg.fullCi, nil
@@ -107,7 +146,8 @@ func (bpkg *BuildPackage) loadIdentities(b *Builder) (map[string]bool, bool) {
 
 	foundNewIdent := false
 
-	newIdents := cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.identities")
+	newIdents := cli.GetStringSliceIdentities(bpkg.Viper, idents,
+		"pkg.identities")
 	for _, nident := range newIdents {
 		_, ok := idents[nident]
 		if !ok {
@@ -117,13 +157,15 @@ func (bpkg *BuildPackage) loadIdentities(b *Builder) (map[string]bool, bool) {
 	}
 
 	if foundNewIdent {
-		return b.Identities(), foundNewIdent
+		return b.Identities(), true
 	} else {
-		return idents, foundNewIdent
+		return idents, false
 	}
 }
 
-func (bpkg *BuildPackage) loadDeps(b *Builder, idents map[string]bool) (bool, error) {
+func (bpkg *BuildPackage) loadDeps(b *Builder,
+	idents map[string]bool) (bool, error) {
+
 	proj := project.GetProject()
 
 	foundNewDep := false
@@ -141,17 +183,18 @@ func (bpkg *BuildPackage) loadDeps(b *Builder, idents map[string]bool) (bool, er
 		}
 
 		if pkg == nil {
-			return false, util.NewNewtError("Could not resolve package dependency " +
-				newDep.String())
+			return false,
+				util.NewNewtError("Could not resolve package dependency " +
+					newDep.String())
 		}
 
-		_, ok := b.GetPackage(pkg)
-		if !ok {
+		if b.Packages[pkg] == nil {
 			foundNewDep = true
 			b.AddPackage(pkg)
 		}
 
 		if !bpkg.HasDep(newDep) {
+			foundNewDep = true
 			bpkg.AddDep(newDep)
 		}
 	}
@@ -159,6 +202,31 @@ func (bpkg *BuildPackage) loadDeps(b *Builder, idents map[string]bool) (bool, er
 	return foundNewDep, nil
 }
 
+func (bpkg *BuildPackage) publicIncludeDirs(b *Builder) []string {
+	pkgBase := filepath.Base(bpkg.Name())
+
+	return []string{
+		bpkg.BasePath() + "/include",
+		bpkg.BasePath() + "/include/" + pkgBase + "/arch/" + b.target.Arch,
+	}
+}
+
+func (bpkg *BuildPackage) privateIncludeDirs(b *Builder) []string {
+	srcDir := bpkg.BasePath() + "/src/"
+
+	incls := []string{}
+	incls = append(incls, srcDir)
+	incls = append(incls, srcDir+"/arch/"+b.target.Arch)
+
+	if cli.CheckBoolMap(b.Identities(), "test") {
+		testSrcDir := srcDir + "/test"
+		incls = append(incls, testSrcDir)
+		incls = append(incls, testSrcDir+"/arch/"+b.target.Arch)
+	}
+
+	return incls
+}
+
 func (bpkg *BuildPackage) Load(b *Builder) (bool, error) {
 	if bpkg.loaded {
 		return true, nil
@@ -178,31 +246,15 @@ func (bpkg *BuildPackage) Load(b *Builder) (bool, error) {
 
 	// Now, load the rest of the package, this should happen only once.
 	apis := cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.caps")
-	for _, apiStr := range apis {
-		api, err := pkg.NewDependency(bpkg.Repo(), apiStr)
-		if err != nil {
-			return false, err
-		}
+	for _, api := range apis {
 		bpkg.AddApi(api)
 	}
 
 	reqApis := cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.req_caps")
-	for _, apiStr := range reqApis {
-		api, err := pkg.NewDependency(bpkg.Repo(), apiStr)
-		if err != nil {
-			return false, err
-		}
-		bpkg.AddReqApi(api)
+	for _, reqApi := range reqApis {
+		bpkg.AddReqApi(reqApi)
 	}
 
-	ci := NewCompilerInfo()
-	ci.Cflags = cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.cflags")
-	ci.Lflags = cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.lflags")
-	ci.Aflags = cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.aflags")
-	ci.Includes = cli.GetStringSliceIdentities(bpkg.Viper, idents, "pkg.includes")
-
-	bpkg.pkgCi = ci
-
 	bpkg.loaded = true
 
 	return true, nil

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/builder/cmds.go
----------------------------------------------------------------------
diff --git a/newt/builder/cmds.go b/newt/builder/cmds.go
index 3e1dc63..99e438e 100644
--- a/newt/builder/cmds.go
+++ b/newt/builder/cmds.go
@@ -23,16 +23,23 @@ import (
 	"fmt"
 
 	"github.com/spf13/cobra"
+	"mynewt.apache.org/newt/newt/cli"
 	"mynewt.apache.org/newt/newt/target"
+	"mynewt.apache.org/newt/util"
 )
 
 func buildRunCmd(cmd *cobra.Command, args []string) {
-	b, err := NewBuilder(&target.Target{
-		BspName:      "$apache-mynewt-world/hw/bsp/native",
-		AppName:      "apps/blinky",
-		CompilerName: "$apache-mynewt-world/compiler/sim",
-		Arch:         "sim",
-	})
+	if len(args) < 1 {
+		cli.NewtUsage(cmd, nil)
+	}
+
+	targetName := args[0]
+	t := target.GetTargets()[targetName]
+	if t == nil {
+		cli.NewtUsage(cmd, util.NewNewtError("invalid target name"))
+	}
+
+	b, err := NewBuilder(t)
 	if err != nil {
 		fmt.Println(err)
 	}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/cli/util.go
----------------------------------------------------------------------
diff --git a/newt/cli/util.go b/newt/cli/util.go
index 02cb986..b845b1b 100644
--- a/newt/cli/util.go
+++ b/newt/cli/util.go
@@ -21,7 +21,6 @@ package cli
 
 import (
 	"bufio"
-	"bytes"
 	"fmt"
 	"io/ioutil"
 	"log"
@@ -113,7 +112,7 @@ func Init(level string, silent bool, quiet bool, verbose bool) {
 	}
 }
 
-func checkBoolMap(mapVar map[string]bool, item string) bool {
+func CheckBoolMap(mapVar map[string]bool, item string) bool {
 	v, ok := mapVar[item]
 	return v && ok
 }
@@ -384,44 +383,6 @@ func ReadLines(path string) ([]string, error) {
 	return lines, nil
 }
 
-// Determines if a file was previously built with a command line invocation
-// different from the one specified.
-//
-// @param dstFile               The output file whose build invocation is being
-//                                  tested.
-// @param cmd                   The command that would be used to generate the
-//                                  specified destination file.
-//
-// @return                      true if the command has changed or if the
-//                                  destination file was never built;
-//                              false otherwise.
-func CommandHasChanged(dstFile string, cmd string) bool {
-	cmdFile := dstFile + ".cmd"
-	prevCmd, err := ioutil.ReadFile(cmdFile)
-	if err != nil {
-		return true
-	}
-
-	return bytes.Compare(prevCmd, []byte(cmd)) != 0
-}
-
-// Writes a file containing the command-line invocation used to generate the
-// specified file.  The file that this function writes can be used later to
-// determine if the set of compiler options has changed.
-//
-// @param dstFile               The output file whose build invocation is being
-//                                  recorded.
-// @param cmd                   The command to write.
-func WriteCommandFile(dstFile string, cmd string) error {
-	cmdPath := dstFile + ".cmd"
-	err := ioutil.WriteFile(cmdPath, []byte(cmd), 0644)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
 // Removes all duplicate strings from the specified array, while preserving
 // order.
 func UniqueStrings(elems []string) []string {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/newt.go
----------------------------------------------------------------------
diff --git a/newt/newt.go b/newt/newt.go
index 68b0883..4cb8ad6 100644
--- a/newt/newt.go
+++ b/newt/newt.go
@@ -84,6 +84,8 @@ func newtCmd() *cobra.Command {
 }
 
 func main() {
+	cli.Init("ERROR", false, false, false)
+
 	cmd := newtCmd()
 	project.AddCommands(cmd)
 	pkg.AddCommands(cmd)

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/pkg/localpackage.go
----------------------------------------------------------------------
diff --git a/newt/pkg/localpackage.go b/newt/pkg/localpackage.go
index f3de061..a95ef44 100644
--- a/newt/pkg/localpackage.go
+++ b/newt/pkg/localpackage.go
@@ -60,9 +60,9 @@ type LocalPackage struct {
 	// Dependencies for this package
 	deps []*Dependency
 	// APIs that this package exports
-	apis []*Dependency
+	apis []string
 	// APIs that this package requires
-	reqApis []*Dependency
+	reqApis []string
 
 	// Pointer to pkg.yml configuration structure
 	Viper *viper.Viper
@@ -166,19 +166,28 @@ func (pkg *LocalPackage) Deps() []*Dependency {
 	return pkg.deps
 }
 
-func (pkg *LocalPackage) AddApi(api *Dependency) {
+func (pkg *LocalPackage) AddApi(api string) {
 	pkg.apis = append(pkg.apis, api)
 }
 
-func (pkg *LocalPackage) Apis() []*Dependency {
+func (pkg *LocalPackage) Apis() []string {
 	return pkg.apis
 }
 
-func (pkg *LocalPackage) AddReqApi(api *Dependency) {
+func (pkg *LocalPackage) AddReqApi(api string) {
 	pkg.reqApis = append(pkg.reqApis, api)
 }
 
-func (pkg *LocalPackage) ReqApis() []*Dependency {
+func (pkg *LocalPackage) DelReqApi(api string) {
+	for i, entry := range pkg.reqApis {
+		if api == entry {
+			pkg.reqApis = append(pkg.reqApis[:i], pkg.reqApis[i+1:]...)
+			return
+		}
+	}
+}
+
+func (pkg *LocalPackage) ReqApis() []string {
 	return pkg.reqApis
 }
 
@@ -207,7 +216,6 @@ func (pkg *LocalPackage) Save() error {
 	}
 
 	filepath := dirpath + "/" + PACKAGE_FILE_NAME
-	fmt.Printf("filepath=%s\n", filepath)
 	file, err := os.Create(filepath)
 	if err != nil {
 		return util.NewNewtError(err.Error())

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/pkg/package.go
----------------------------------------------------------------------
diff --git a/newt/pkg/package.go b/newt/pkg/package.go
index 94f0c57..bfb9eb1 100644
--- a/newt/pkg/package.go
+++ b/newt/pkg/package.go
@@ -69,9 +69,9 @@ type Package interface {
 	// Dependency list for this package
 	Deps() []*Dependency
 	// APIs exported by this package
-	Apis() []*Dependency
+	Apis() []string
 	// APIs required by this package
-	ReqApis() []*Dependency
+	ReqApis() []string
 }
 
 // Description of a package

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/project/project.go
----------------------------------------------------------------------
diff --git a/newt/project/project.go b/newt/project/project.go
index fb145df..23e3ce3 100644
--- a/newt/project/project.go
+++ b/newt/project/project.go
@@ -203,18 +203,15 @@ func (proj *Project) Init(dir string) error {
 }
 
 func (proj *Project) ResolveDependency(dep *pkg.Dependency) (*pkg.LocalPackage, error) {
-	var myPkg *pkg.LocalPackage = nil
-
 	for _, pkgList := range proj.packages {
 		for _, pkg := range *pkgList {
 			if dep.SatisfiesDependency(pkg) {
-				myPkg = pkg
-				break
+				return pkg, nil
 			}
 		}
 	}
 
-	return myPkg, nil
+	return nil, util.NewNewtError("Can't resolve dependency: " + dep.Name)
 }
 
 func findProjectDir(dir string) (string, error) {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/toolchain/compiler.go
----------------------------------------------------------------------
diff --git a/newt/toolchain/compiler.go b/newt/toolchain/compiler.go
index 17aacb6..82bc537 100644
--- a/newt/toolchain/compiler.go
+++ b/newt/toolchain/compiler.go
@@ -29,7 +29,8 @@ import (
 	"strings"
 	"time"
 
-	_ "mynewt.apache.org/newt/newt/util"
+	"mynewt.apache.org/newt/newt/cli"
+	. "mynewt.apache.org/newt/util"
 )
 
 const (
@@ -37,17 +38,18 @@ const (
 	COMPILER_TYPE_ASM = 1
 )
 
+type CompilerInfo struct {
+	Includes []string
+	Cflags   []string
+	Lflags   []string
+	Aflags   []string
+}
+
 type Compiler struct {
-	ConfigPath   string
 	TargetName   string
-	BaseIncludes map[string]bool
 	ObjPathList  map[string]bool
 	LinkerScript string
 
-	Cflags string
-	Aflags string
-	Lflags string
-
 	depTracker            DepTracker
 	ccPath                string
 	asPath                string
@@ -55,30 +57,42 @@ type Compiler struct {
 	odPath                string
 	osPath                string
 	ocPath                string
-	ldFlags               string
 	ldResolveCircularDeps bool
 	ldMapFile             bool
+
+	info CompilerInfo
+}
+
+func NewCompilerInfo() *CompilerInfo {
+	ci := &CompilerInfo{}
+	ci.Includes = []string{}
+	ci.Cflags = []string{}
+	ci.Lflags = []string{}
+	ci.Aflags = []string{}
+
+	return ci
 }
 
-func NewCompiler(ccPath string, cDef string, tName string, includes []string) (
-	*Compiler, error) {
+func (ci *CompilerInfo) AddCompilerInfo(newCi *CompilerInfo) {
+	ci.Includes = append(ci.Includes, newCi.Includes...)
+	ci.Cflags = append(ci.Cflags, newCi.Cflags...)
+	ci.Lflags = append(ci.Lflags, newCi.Lflags...)
+	ci.Aflags = append(ci.Aflags, newCi.Aflags...)
+}
+
+func NewCompiler(compilerDir string, cDef string,
+	tName string) (*Compiler, error) {
 
 	c := &Compiler{
-		ConfigPath:   ccPath,
-		TargetName:   tName,
-		BaseIncludes: map[string]bool{},
-		ObjPathList:  map[string]bool{},
+		TargetName:  tName,
+		ObjPathList: map[string]bool{},
 	}
 
 	c.depTracker = NewDepTracker(c)
-	for _, incl := range includes {
-		c.BaseIncludes[incl] = true
-	}
 
-	StatusMessage(VERBOSITY_VERBOSE,
-		"Loading compiler %s, target %s, def %s\n", ccPath, tName, cDef)
-
-	err := c.ReadSettings(cDef)
+	cli.StatusMessage(cli.VERBOSITY_VERBOSE,
+		"Loading compiler %s, target %s, def %s\n", compilerDir, tName, cDef)
+	err := c.load(compilerDir, cDef)
 	if err != nil {
 		return nil, err
 	}
@@ -86,8 +100,8 @@ func NewCompiler(ccPath string, cDef string, tName string, includes []string) (
 	return c, nil
 }
 
-func (c *Compiler) ReadSettings(cDef string) error {
-	v, err := ReadConfig(c.ConfigPath, "pkg")
+func (c *Compiler) load(compilerDir string, cDef string) error {
+	v, err := ReadConfig(compilerDir, "compiler")
 	if err != nil {
 		return err
 	}
@@ -102,22 +116,27 @@ func (c *Compiler) ReadSettings(cDef string) error {
 	cflags := v.GetStringSlice("compiler.flags." + cDef)
 	for _, flag := range cflags {
 		if strings.HasPrefix(flag, "compiler.flags") {
-			c.Cflags += " " + strings.Trim(v.GetString(flag), "\n")
+			c.info.Cflags = append(c.info.Cflags,
+				strings.Trim(v.GetString(flag), "\n"))
 		} else {
-			c.Cflags += " " + strings.Trim(flag, "\n")
+			c.info.Cflags = append(c.info.Cflags, strings.Trim(flag, "\n"))
 		}
 	}
 
-	c.ldFlags = v.GetString("compiler.ld.flags")
+	c.info.Lflags = v.GetStringSlice("compiler.ld.flags")
 	c.ldResolveCircularDeps = v.GetBool("compiler.ld.resolve_circular_deps")
 	c.ldMapFile = v.GetBool("compiler.ld.mapfile")
 
 	log.Printf("[INFO] ccPath = %s, arPath = %s, flags = %s", c.ccPath,
-		c.arPath, c.Cflags)
+		c.arPath, c.info.Cflags)
 
 	return nil
 }
 
+func (c *Compiler) AddInfo(info *CompilerInfo) {
+	c.info.AddCompilerInfo(info)
+}
+
 // Skips compilation of the specified C or assembly file, but adds the name of
 // the object file that would have been generated to the compiler's list of
 // object files.  This function is used when the object file is already up to
@@ -144,17 +163,25 @@ func (c *Compiler) SkipSourceFile(srcFile string) error {
 
 // Generates a string consisting of all the necessary include path (-I)
 // options.  The result is sorted and contains no duplicate paths.
-func (c *Compiler) IncludesString() string {
-	includes := make([]string, 0, len(c.BaseIncludes))
-	for k, _ := range c.BaseIncludes {
-		includes = append(includes, filepath.ToSlash(k))
+func (c *Compiler) includesString() string {
+	if len(c.info.Includes) == 0 {
+		return ""
 	}
 
-	sort.Strings(includes)
-
+	includes := cli.SortFields(c.info.Includes...)
 	return "-I" + strings.Join(includes, " -I")
 }
 
+func (c *Compiler) cflagsString() string {
+	cflags := cli.SortFields(c.info.Cflags...)
+	return strings.Join(cflags, " ")
+}
+
+func (c *Compiler) lflagsString() string {
+	lflags := cli.SortFields(c.info.Lflags...)
+	return strings.Join(lflags, " ")
+}
+
 // Calculates the command-line invocation necessary to compile the specified C
 // or assembly file.
 //
@@ -182,7 +209,7 @@ func (c *Compiler) CompileFileCmd(file string,
 	}
 
 	cmd += " -c " + "-o " + objPath + " " + file +
-		" " + c.Cflags + " " + c.IncludesString()
+		" " + c.cflagsString() + " " + c.includesString()
 
 	return cmd, nil
 }
@@ -194,19 +221,36 @@ func (c *Compiler) GenDepsForFile(file string) error {
 	wd, _ := os.Getwd()
 	objDir := wd + "/obj/" + c.TargetName + "/"
 
-	if NodeNotExist(objDir) {
+	if cli.NodeNotExist(objDir) {
 		os.MkdirAll(objDir, 0755)
 	}
 
 	depFile := objDir + strings.TrimSuffix(file, filepath.Ext(file)) + ".d"
 	depFile = filepath.ToSlash(depFile)
-	cFlags := c.Cflags + " " + c.IncludesString()
 
 	var cmd string
 	var err error
 
-	cmd = c.ccPath + " " + cFlags + " -MM -MG " + file + " > " + depFile
-	_, err = ShellCommand(cmd)
+	cmd = c.ccPath + " " + c.cflagsString() + " " + c.includesString() +
+		" -MM -MG " + file + " > " + depFile
+	_, err = cli.ShellCommand(cmd)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Writes a file containing the command-line invocation used to generate the
+// specified file.  The file that this function writes can be used later to
+// determine if the set of compiler options has changed.
+//
+// @param dstFile               The output file whose build invocation is being
+//                                  recorded.
+// @param cmd                   The command to write.
+func writeCommandFile(dstFile string, cmd string) error {
+	cmdPath := dstFile + ".cmd"
+	err := ioutil.WriteFile(cmdPath, []byte(cmd), 0644)
 	if err != nil {
 		return err
 	}
@@ -222,7 +266,7 @@ func (c *Compiler) CompileFile(file string, compilerType int) error {
 	wd, _ := os.Getwd()
 	objDir := wd + "/obj/" + c.TargetName + "/"
 
-	if NodeNotExist(objDir) {
+	if cli.NodeNotExist(objDir) {
 		os.MkdirAll(objDir, 0755)
 	}
 
@@ -238,20 +282,20 @@ func (c *Compiler) CompileFile(file string, compilerType int) error {
 
 	switch compilerType {
 	case COMPILER_TYPE_C:
-		StatusMessage(VERBOSITY_DEFAULT, "Compiling %s\n", file)
+		cli.StatusMessage(cli.VERBOSITY_DEFAULT, "Compiling %s\n", file)
 	case COMPILER_TYPE_ASM:
-		StatusMessage(VERBOSITY_DEFAULT, "Assembling %s\n", file)
+		cli.StatusMessage(cli.VERBOSITY_DEFAULT, "Assembling %s\n", file)
 	default:
 		return NewNewtError("Unknown compiler type")
 	}
 
-	rsp, err := ShellCommand(cmd)
+	rsp, err := cli.ShellCommand(cmd)
 	if err != nil {
-		StatusMessage(VERBOSITY_QUIET, string(rsp))
+		cli.StatusMessage(cli.VERBOSITY_QUIET, string(rsp))
 		return err
 	}
 
-	err = WriteCommandFile(objPath, cmd)
+	err = writeCommandFile(objPath, cmd)
 	if err != nil {
 		return err
 	}
@@ -266,19 +310,20 @@ func (c *Compiler) CompileFile(file string, compilerType int) error {
 //
 // @param match                 The file glob specifying which C files to
 //                                  compile.
-func (c *Compiler) Compile(match string) error {
-	files, _ := filepath.Glob(match)
+func (c *Compiler) CompileC() error {
+	files, _ := filepath.Glob("*.c")
 
 	wd, err := os.Getwd()
 	if err != nil {
 		return err
 	}
 
-	log.Printf("[INFO] Compiling C if outdated (%s/%s) %s", wd, match,
+	log.Printf("[INFO] Compiling C if outdated (%s/*.c) %s", wd,
 		strings.Join(files, " "))
 	for _, file := range files {
 		file = filepath.ToSlash(file)
-		compileRequired, err := c.depTracker.CompileRequired(file, 0)
+		compileRequired, err := c.depTracker.CompileRequired(file,
+			COMPILER_TYPE_C)
 		if err != nil {
 			return err
 		}
@@ -299,18 +344,19 @@ func (c *Compiler) Compile(match string) error {
 //
 // @param match                 The file glob specifying which assembly files
 //                                  to compile.
-func (c *Compiler) CompileAs(match string) error {
-	files, _ := filepath.Glob(match)
+func (c *Compiler) CompileAs() error {
+	files, _ := filepath.Glob("*.s")
 
 	wd, err := os.Getwd()
 	if err != nil {
 		return err
 	}
 
-	log.Printf("[INFO] Compiling assembly if outdated (%s/%s) %s", wd, match,
+	log.Printf("[INFO] Compiling assembly if outdated (%s/*.s) %s", wd,
 		strings.Join(files, " "))
 	for _, file := range files {
-		compileRequired, err := c.depTracker.CompileRequired(file, 1)
+		compileRequired, err := c.depTracker.CompileRequired(file,
+			COMPILER_TYPE_ASM)
 		if err != nil {
 			return err
 		}
@@ -357,7 +403,7 @@ func (c *Compiler) RecursiveClean(path string, tName string) error {
 	return nil
 }
 
-func (c *Compiler) processEntry(wd string, node os.FileInfo, match string, cType int,
+func (c *Compiler) processEntry(wd string, node os.FileInfo, cType int,
 	ignDirs []string) error {
 	// check to see if we ignore this element
 	for _, entry := range ignDirs {
@@ -368,13 +414,14 @@ func (c *Compiler) processEntry(wd string, node os.FileInfo, match string, cType
 
 	// if not, recurse into the directory
 	os.Chdir(wd + "/" + node.Name())
-	return c.RecursiveCompile(match, cType, ignDirs)
+	return c.RecursiveCompile(cType, ignDirs)
 }
 
-func (c *Compiler) RecursiveCompile(match string, cType int, ignDirs []string) error {
+func (c *Compiler) RecursiveCompile(cType int, ignDirs []string) error {
 	// Get a list of files in the current directory, and if they are a directory,
 	// and that directory is not in the ignDirs variable, then recurse into that
 	// directory and compile the files in there
+
 	wd, err := os.Getwd()
 	if err != nil {
 		return NewNewtError(err.Error())
@@ -388,20 +435,23 @@ func (c *Compiler) RecursiveCompile(match string, cType int, ignDirs []string) e
 
 	for _, node := range dirList {
 		if node.IsDir() {
-			err = c.processEntry(wd, node, match, cType, ignDirs)
+			err = c.processEntry(wd, node, cType, ignDirs)
 			if err != nil {
 				return err
 			}
 		}
 	}
 
-	os.Chdir(wd)
+	err = os.Chdir(wd)
+	if err != nil {
+		return err
+	}
 
 	switch cType {
 	case 0:
-		return c.Compile(match)
+		return c.CompileC()
 	case 1:
-		return c.CompileAs(match)
+		return c.CompileAs()
 	default:
 		return NewNewtError("Wrong compiler type specified to RecursiveCompile")
 	}
@@ -430,21 +480,21 @@ func (c *Compiler) getObjFiles(baseObjFiles []string) string {
 func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool,
 	objFiles []string) string {
 
-	objList := c.getObjFiles(UniqueStrings(objFiles))
+	objList := c.getObjFiles(cli.UniqueStrings(objFiles))
 
-	cmd := c.ccPath + " -o " + dstFile + " " + " " + c.Cflags
+	cmd := c.ccPath + " -o " + dstFile + " " + " " + c.cflagsString()
 	if c.ldResolveCircularDeps {
 		cmd += " -Wl,--start-group " + objList + " -Wl,--end-group "
 	} else {
 		cmd += " " + objList
 	}
 
-	cmd += " " + c.ldFlags
+	cmd += " " + c.lflagsString()
 
 	if c.LinkerScript != "" {
 		cmd += " -T " + c.LinkerScript
 	}
-	if checkBoolMap(options, "mapFile") {
+	if cli.CheckBoolMap(options, "mapFile") {
 		cmd += " -Wl,-Map=" + dstFile + ".map"
 	}
 
@@ -461,20 +511,20 @@ func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool,
 func (c *Compiler) CompileBinary(dstFile string, options map[string]bool,
 	objFiles []string) error {
 
-	objList := c.getObjFiles(UniqueStrings(objFiles))
+	objList := c.getObjFiles(cli.UniqueStrings(objFiles))
 
-	StatusMessage(VERBOSITY_DEFAULT, "Linking %s\n", path.Base(dstFile))
-	StatusMessage(VERBOSITY_VERBOSE, "Linking %s with input files %s\n",
+	cli.StatusMessage(cli.VERBOSITY_DEFAULT, "Linking %s\n", path.Base(dstFile))
+	cli.StatusMessage(cli.VERBOSITY_VERBOSE, "Linking %s with input files %s\n",
 		dstFile, objList)
 
 	cmd := c.CompileBinaryCmd(dstFile, options, objFiles)
-	rsp, err := ShellCommand(cmd)
+	rsp, err := cli.ShellCommand(cmd)
 	if err != nil {
-		StatusMessage(VERBOSITY_QUIET, string(rsp))
+		cli.StatusMessage(cli.VERBOSITY_QUIET, string(rsp))
 		return err
 	}
 
-	err = WriteCommandFile(dstFile, cmd)
+	err = writeCommandFile(dstFile, cmd)
 	if err != nil {
 		return err
 	}
@@ -496,17 +546,17 @@ func (c *Compiler) generateExtras(elfFilename string,
 
 	var cmd string
 
-	if checkBoolMap(options, "listFile") {
+	if cli.CheckBoolMap(options, "listFile") {
 		listFile := elfFilename + ".lst"
 		// if list file exists, remove it
-		if NodeExist(listFile) {
+		if cli.NodeExist(listFile) {
 			if err := os.RemoveAll(listFile); err != nil {
 				return err
 			}
 		}
 
 		cmd = c.odPath + " -wxdS " + elfFilename + " >> " + listFile
-		_, err := ShellCommand(cmd)
+		_, err := cli.ShellCommand(cmd)
 		if err != nil {
 			// XXX: gobjdump appears to always crash.  Until we get that sorted
 			// out, don't fail the link process if lst generation fails.
@@ -517,21 +567,21 @@ func (c *Compiler) generateExtras(elfFilename string,
 		for _, sect := range sects {
 			cmd = c.odPath + " -s -j " + sect + " " + elfFilename + " >> " +
 				listFile
-			ShellCommand(cmd)
+			cli.ShellCommand(cmd)
 		}
 
 		cmd = c.osPath + " " + elfFilename + " >> " + listFile
-		_, err = ShellCommand(cmd)
+		_, err = cli.ShellCommand(cmd)
 		if err != nil {
 			return err
 		}
 	}
 
-	if checkBoolMap(options, "binFile") {
+	if cli.CheckBoolMap(options, "binFile") {
 		binFile := elfFilename + ".bin"
 		cmd = c.ocPath + " -R .bss -R .bss.core -R .bss.core.nz -O binary " +
 			elfFilename + " " + binFile
-		_, err := ShellCommand(cmd)
+		_, err := cli.ShellCommand(cmd)
 		if err != nil {
 			return err
 		}
@@ -548,16 +598,20 @@ func (c *Compiler) generateExtras(elfFilename string,
 // @param options               Some build options specifying how the elf file
 //                                  gets generated.
 // @param objFiles              An array of the source .o and .a filenames.
-func (c *Compiler) CompileElf(binFile string, options map[string]bool,
-	objFiles []string) error {
-
+func (c *Compiler) CompileElf(binFile string, objFiles []string) error {
 	binFile += ".elf"
 
+	options := map[string]bool{"mapFile": c.ldMapFile,
+		"listFile": true, "binFile": true}
+
 	linkRequired, err := c.depTracker.LinkRequired(binFile, options, objFiles)
 	if err != nil {
 		return err
 	}
 	if linkRequired {
+		if err := os.MkdirAll(filepath.Dir(binFile), 0755); err != nil {
+			return NewNewtError(err.Error())
+		}
 		err := c.CompileBinary(binFile, options, objFiles)
 		if err != nil {
 			return err
@@ -590,7 +644,9 @@ func (c *Compiler) CompileArchiveCmd(archiveFile string,
 //
 // @param archiveFile           The filename of the library to archive.
 // @param objFiles              An array of the source .o filenames.
-func (c *Compiler) CompileArchive(archiveFile string, objFiles []string) error {
+func (c *Compiler) CompileArchive(archiveFile string) error {
+	objFiles := []string{}
+
 	arRequired, err := c.depTracker.ArchiveRequired(archiveFile, objFiles)
 	if err != nil {
 		return err
@@ -599,11 +655,11 @@ func (c *Compiler) CompileArchive(archiveFile string, objFiles []string) error {
 		return nil
 	}
 
-	objList := c.getObjFiles(objFiles)
-
-	StatusMessage(VERBOSITY_DEFAULT, "Archiving %s\n", path.Base(archiveFile))
-	StatusMessage(VERBOSITY_VERBOSE, "Archiving %s with object files %s",
-		archiveFile, objList)
+	cli.StatusMessage(cli.VERBOSITY_DEFAULT, "Archiving %s\n",
+		path.Base(archiveFile))
+	objList := c.getObjFiles([]string{})
+	cli.StatusMessage(cli.VERBOSITY_VERBOSE, "Archiving %s with object "+
+		"files %s\n", archiveFile, objList)
 
 	// Delete the old archive, if it exists.
 	err = os.Remove(archiveFile)
@@ -612,13 +668,13 @@ func (c *Compiler) CompileArchive(archiveFile string, objFiles []string) error {
 	}
 
 	cmd := c.CompileArchiveCmd(archiveFile, objFiles)
-	rsp, err := ShellCommand(cmd)
+	rsp, err := cli.ShellCommand(cmd)
 	if err != nil {
-		StatusMessage(VERBOSITY_QUIET, string(rsp))
+		cli.StatusMessage(cli.VERBOSITY_QUIET, string(rsp))
 		return err
 	}
 
-	err = WriteCommandFile(archiveFile, cmd)
+	err = writeCommandFile(archiveFile, cmd)
 	if err != nil {
 		return err
 	}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6f357ff9/newt/toolchain/deps.go
----------------------------------------------------------------------
diff --git a/newt/toolchain/deps.go b/newt/toolchain/deps.go
index ce3c128..b2ddc06 100644
--- a/newt/toolchain/deps.go
+++ b/newt/toolchain/deps.go
@@ -20,12 +20,15 @@
 package toolchain
 
 import (
+	"bytes"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
 	"time"
 
-	_ "mynewt.apache.org/newt/newt/util"
+	"mynewt.apache.org/newt/newt/cli"
+	"mynewt.apache.org/newt/util"
 )
 
 type DepTracker struct {
@@ -53,7 +56,7 @@ func NewDepTracker(c *Compiler) DepTracker {
 //
 // This function ignores all lines except for the first.
 func ParseDepsFile(filename string) ([]string, error) {
-	lines, err := ReadLines(filename)
+	lines, err := cli.ReadLines(filename)
 	if err != nil {
 		return nil, err
 	}
@@ -65,13 +68,13 @@ func ParseDepsFile(filename string) ([]string, error) {
 	// Assume only the first line is important.
 	tokens := strings.Fields(lines[0])
 	if len(tokens) == 0 {
-		return nil, NewNewtError("Invalid Makefile dependency file; first " +
+		return nil, util.NewNewtError("Invalid Makefile dependency file; first " +
 			"line is blank")
 	}
 
 	dFileTok := tokens[0]
 	if dFileTok[len(dFileTok)-1:] != ":" {
-		return nil, NewNewtError("Invalid Makefile dependency file; first " +
+		return nil, util.NewNewtError("Invalid Makefile dependency file; first " +
 			"line missing ':'")
 	}
 
@@ -82,7 +85,7 @@ func ParseDepsFile(filename string) ([]string, error) {
 // modification time of the specified file.  If the specified file is older
 // than the tracker's currently most-recent time, this function has no effect.
 func (tracker *DepTracker) ProcessFileTime(file string) error {
-	modTime, err := FileModificationTime(file)
+	modTime, err := cli.FileModificationTime(file)
 	if err != nil {
 		return err
 	}
@@ -94,6 +97,31 @@ func (tracker *DepTracker) ProcessFileTime(file string) error {
 	return nil
 }
 
+// Determines if a file was previously built with a command line invocation
+// different from the one specified.
+//
+// @param dstFile               The output file whose build invocation is being
+//                                  tested.
+// @param cmd                   The command that would be used to generate the
+//                                  specified destination file.
+//
+// @return                      true if the command has changed or if the
+//                                  destination file was never built;
+//                              false otherwise.
+func commandHasChanged(dstFile string, cmd string) bool {
+	cmdFile := dstFile + ".cmd"
+	prevCmd, err := ioutil.ReadFile(cmdFile)
+	if err != nil {
+		return true
+	}
+
+	rc := bytes.Compare(prevCmd, []byte(cmd)) != 0
+	if rc {
+		cli.StatusMessage(util.VERBOSITY_VERBOSE, "oldcmd=%s\nnewcmd=%s\n", prevCmd, cmd)
+	}
+	return rc
+}
+
 // Determines if the specified C or assembly file needs to be built.  A compile
 // is required if any of the following is true:
 //     * The destination object file does not exist.
@@ -118,17 +146,18 @@ func (tracker *DepTracker) CompileRequired(srcFile string,
 	if err != nil {
 		return false, err
 	}
-	if CommandHasChanged(objFile, cmd) {
-		StatusMessage(VERBOSITY_VERBOSE, "%s - rebuild required; different command\n", srcFile)
+	if commandHasChanged(objFile, cmd) {
+		cli.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; "+
+			"different command\n", srcFile)
 		return true, nil
 	}
 
-	srcModTime, err := FileModificationTime(srcFile)
+	srcModTime, err := cli.FileModificationTime(srcFile)
 	if err != nil {
 		return false, err
 	}
 
-	objModTime, err := FileModificationTime(objFile)
+	objModTime, err := cli.FileModificationTime(objFile)
 	if err != nil {
 		return false, err
 	}
@@ -136,14 +165,14 @@ func (tracker *DepTracker) CompileRequired(srcFile string,
 	// If the object doesn't exist or is older than the source file, a build is
 	// required; no need to check dependencies.
 	if srcModTime.After(objModTime) {
-		StatusMessage(VERBOSITY_VERBOSE, "%s - rebuild required; source newer than obj\n", srcFile)
+		cli.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; source newer than obj\n", srcFile)
 		return true, nil
 	}
 
 	// Determine if the dependency (.d) file needs to be generated.  If it
 	// doesn't exist or is older than the source file, it is out of date and
 	// needs to be created.
-	depModTime, err := FileModificationTime(depFile)
+	depModTime, err := cli.FileModificationTime(depFile)
 	if err != nil {
 		return false, err
 	}
@@ -163,7 +192,7 @@ func (tracker *DepTracker) CompileRequired(srcFile string,
 
 	// Check if any dependencies are newer than the destination object file.
 	for _, dep := range deps {
-		if NodeNotExist(dep) {
+		if cli.NodeNotExist(dep) {
 			// The dependency has been deleted; the .d file is out of date.
 			// Recreate the dependency file and repeat this entire function.
 			err := tracker.compiler.GenDepsForFile(srcFile)
@@ -173,14 +202,14 @@ func (tracker *DepTracker) CompileRequired(srcFile string,
 
 			return tracker.CompileRequired(srcFile, compilerType)
 		} else {
-			depModTime, err = FileModificationTime(dep)
+			depModTime, err = cli.FileModificationTime(dep)
 			if err != nil {
 				return false, err
 			}
 		}
 
 		if depModTime.After(objModTime) {
-			StatusMessage(VERBOSITY_VERBOSE, "%s - rebuild required; obj older than dependency (%s)\n", srcFile, dep)
+			cli.StatusMessage(util.VERBOSITY_VERBOSE, "%s - rebuild required; obj older than dependency (%s)\n", srcFile, dep)
 			return true, nil
 		}
 	}
@@ -201,13 +230,13 @@ func (tracker *DepTracker) ArchiveRequired(archiveFile string,
 	// If the archive was previously built with a different set of options, a
 	// rebuild is required.
 	cmd := tracker.compiler.CompileArchiveCmd(archiveFile, objFiles)
-	if CommandHasChanged(archiveFile, cmd) {
+	if commandHasChanged(archiveFile, cmd) {
 		return true, nil
 	}
 
 	// If the archive doesn't exist or is older than any object file, a rebuild
 	// is required.
-	aModTime, err := FileModificationTime(archiveFile)
+	aModTime, err := cli.FileModificationTime(archiveFile)
 	if err != nil {
 		return false, err
 	}
@@ -235,13 +264,13 @@ func (tracker *DepTracker) LinkRequired(dstFile string,
 	// If the elf file was previously built with a different set of options, a
 	// rebuild is required.
 	cmd := tracker.compiler.CompileBinaryCmd(dstFile, options, objFiles)
-	if CommandHasChanged(dstFile, cmd) {
+	if commandHasChanged(dstFile, cmd) {
 		return true, nil
 	}
 
 	// If the elf file doesn't exist or is older than any input file, a rebuild
 	// is required.
-	dstModTime, err := FileModificationTime(dstFile)
+	dstModTime, err := cli.FileModificationTime(dstFile)
 	if err != nil {
 		return false, err
 	}
@@ -256,7 +285,7 @@ func (tracker *DepTracker) LinkRequired(dstFile string,
 		objFiles = append(objFiles, tracker.compiler.LinkerScript)
 	}
 	for _, obj := range objFiles {
-		objModTime, err := FileModificationTime(obj)
+		objModTime, err := cli.FileModificationTime(obj)
 		if err != nil {
 			return false, err
 		}