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 2015/11/21 01:42:27 UTC

[02/42] incubator-mynewt-newt git commit: Move newt source into a "newt" subdirectory.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/compiler.go
----------------------------------------------------------------------
diff --git a/newt/cli/compiler.go b/newt/cli/compiler.go
new file mode 100644
index 0000000..94cea3a
--- /dev/null
+++ b/newt/cli/compiler.go
@@ -0,0 +1,619 @@
+/*
+ Copyright 2015 Runtime Inc.
+ Licensed 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 cli
+
+import (
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+	"sort"
+	"strings"
+	"time"
+)
+
+const (
+	COMPILER_TYPE_C   = 0
+	COMPILER_TYPE_ASM = 1
+)
+
+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
+	arPath                string
+	odPath                string
+	osPath                string
+	ocPath                string
+	ldFlags               string
+	ldResolveCircularDeps bool
+	ldMapFile             bool
+}
+
+func NewCompiler(ccPath string, cDef string, tName string, includes []string) (
+	*Compiler, error) {
+
+	c := &Compiler{
+		ConfigPath:   ccPath,
+		TargetName:   tName,
+		BaseIncludes: map[string]bool{},
+		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)
+	if err != nil {
+		return nil, err
+	}
+
+	return c, nil
+}
+
+func (c *Compiler) ReadSettings(cDef string) error {
+	v, err := ReadConfig(c.ConfigPath, "compiler")
+	if err != nil {
+		return err
+	}
+
+	c.ccPath = v.GetString("compiler.path.cc")
+	c.asPath = v.GetString("compiler.path.as")
+	c.arPath = v.GetString("compiler.path.archive")
+	c.odPath = v.GetString("compiler.path.objdump")
+	c.osPath = v.GetString("compiler.path.objsize")
+	c.ocPath = v.GetString("compiler.path.objcopy")
+
+	cflags := v.GetStringSlice("compiler.flags." + cDef)
+	for _, flag := range cflags {
+		if strings.HasPrefix(flag, "compiler.flags") {
+			c.Cflags += " " + strings.Trim(v.GetString(flag), "\n")
+		} else {
+			c.Cflags += " " + strings.Trim(flag, "\n")
+		}
+	}
+
+	c.ldFlags = v.GetString("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)
+
+	return nil
+}
+
+// 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
+// date, so no compilation is necessary.  The name of the object file should
+// still be remembered so that it gets linked in to the final library or
+// executable.
+func (c *Compiler) SkipSourceFile(srcFile string) error {
+	wd, _ := os.Getwd()
+	objDir := wd + "/obj/" + c.TargetName + "/"
+	objFile := objDir + strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) +
+		".o"
+	c.ObjPathList[filepath.ToSlash(objFile)] = true
+
+	// Update the dependency tracker with the object file's modification time.
+	// This is necessary later for determining if the library / executable
+	// needs to be rebuilt.
+	err := c.depTracker.ProcessFileTime(objFile)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// 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))
+	}
+
+	sort.Strings(includes)
+
+	return "-I" + strings.Join(includes, " -I")
+}
+
+// Calculates the command-line invocation necessary to compile the specified C
+// or assembly file.
+//
+// @param file                  The filename of the source file to compile.
+// @param compilerType          One of the COMPILER_TYPE_[...] constants.
+//
+// @return                      (success) The command string.
+func (c *Compiler) CompileFileCmd(file string,
+	compilerType int) (string, error) {
+
+	wd, _ := os.Getwd()
+	objDir := wd + "/obj/" + c.TargetName + "/"
+	objFile := strings.TrimSuffix(file, filepath.Ext(file)) + ".o"
+	objPath := filepath.ToSlash(objDir + objFile)
+
+	var cmd string
+
+	switch compilerType {
+	case COMPILER_TYPE_C:
+		cmd = c.ccPath
+	case COMPILER_TYPE_ASM:
+		cmd = c.asPath
+	default:
+		return "", NewNewtError("Unknown compiler type")
+	}
+
+	cmd += " -c " + "-o " + objPath + " " + file +
+		" " + c.Cflags + " " + c.IncludesString()
+
+	return cmd, nil
+}
+
+// Generates a dependency Makefile (.d) for the specified source C file.
+//
+// @param file                  The name of the source file.
+func (c *Compiler) GenDepsForFile(file string) error {
+	wd, _ := os.Getwd()
+	objDir := wd + "/obj/" + c.TargetName + "/"
+
+	if 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)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Compile the specified C or assembly file.
+//
+// @param file                  The filename of the source file to compile.
+// @param compilerType          One of the COMPILER_TYPE_[...] constants.
+func (c *Compiler) CompileFile(file string, compilerType int) error {
+	wd, _ := os.Getwd()
+	objDir := wd + "/obj/" + c.TargetName + "/"
+
+	if NodeNotExist(objDir) {
+		os.MkdirAll(objDir, 0755)
+	}
+
+	objFile := strings.TrimSuffix(file, filepath.Ext(file)) + ".o"
+
+	objPath := objDir + objFile
+	c.ObjPathList[filepath.ToSlash(objPath)] = true
+
+	cmd, err := c.CompileFileCmd(file, compilerType)
+	if err != nil {
+		return err
+	}
+
+	switch compilerType {
+	case COMPILER_TYPE_C:
+		StatusMessage(VERBOSITY_DEFAULT, "Compiling %s\n", file)
+	case COMPILER_TYPE_ASM:
+		StatusMessage(VERBOSITY_DEFAULT, "Assembling %s\n", file)
+	default:
+		return NewNewtError("Unknown compiler type")
+	}
+
+	rsp, err := ShellCommand(cmd)
+	if err != nil {
+		StatusMessage(VERBOSITY_QUIET, string(rsp))
+		return err
+	}
+
+	err = WriteCommandFile(objPath, cmd)
+	if err != nil {
+		return err
+	}
+
+	// Tell the dependency tracker that an object file was just rebuilt.
+	c.depTracker.MostRecent = time.Now()
+
+	return nil
+}
+
+// Compiles all C files matching the specified file glob.
+//
+// @param match                 The file glob specifying which C files to
+//                                  compile.
+func (c *Compiler) Compile(match string) error {
+	files, _ := filepath.Glob(match)
+
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	log.Printf("[INFO] Compiling C if outdated (%s/%s) %s", wd, match,
+		strings.Join(files, " "))
+	for _, file := range files {
+		file = filepath.ToSlash(file)
+		compileRequired, err := c.depTracker.CompileRequired(file, 0)
+		if err != nil {
+			return err
+		}
+		if compileRequired {
+			err = c.CompileFile(file, COMPILER_TYPE_C)
+		} else {
+			err = c.SkipSourceFile(file)
+		}
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Compiles all assembly files matching the specified file glob.
+//
+// @param match                 The file glob specifying which assembly files
+//                                  to compile.
+func (c *Compiler) CompileAs(match string) error {
+	files, _ := filepath.Glob(match)
+
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	log.Printf("[INFO] Compiling assembly if outdated (%s/%s) %s", wd, match,
+		strings.Join(files, " "))
+	for _, file := range files {
+		compileRequired, err := c.depTracker.CompileRequired(file, 1)
+		if err != nil {
+			return err
+		}
+		if compileRequired {
+			err = c.CompileFile(file, COMPILER_TYPE_ASM)
+		} else {
+			err = c.SkipSourceFile(file)
+		}
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (c *Compiler) RecursiveClean(path string, tName string) error {
+	// Find all the subdirectories of path that contain an "obj/" directory,
+	// and remove that directory either altogether, or just the arch specific
+	// directory.
+	dirList, err := ioutil.ReadDir(path)
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	for _, node := range dirList {
+		if node.IsDir() {
+			if node.Name() == "obj" || node.Name() == "bin" {
+				if tName == "" {
+					os.RemoveAll(path + "/" + node.Name() + "/")
+				} else {
+					os.RemoveAll(path + "/" + node.Name() + "/" + tName + "/")
+				}
+			} else {
+				// recurse into the directory.
+				err = c.RecursiveClean(path+"/"+node.Name(), tName)
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func (c *Compiler) processEntry(wd string, node os.FileInfo, match string, cType int,
+	ignDirs []string) error {
+	// check to see if we ignore this element
+	for _, entry := range ignDirs {
+		if entry == node.Name() {
+			return nil
+		}
+	}
+
+	// if not, recurse into the directory
+	os.Chdir(wd + "/" + node.Name())
+	return c.RecursiveCompile(match, cType, ignDirs)
+}
+
+func (c *Compiler) RecursiveCompile(match string, 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())
+	}
+	wd = filepath.ToSlash(wd)
+
+	dirList, err := ioutil.ReadDir(wd)
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	for _, node := range dirList {
+		if node.IsDir() {
+			err = c.processEntry(wd, node, match, cType, ignDirs)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	os.Chdir(wd)
+
+	switch cType {
+	case 0:
+		return c.Compile(match)
+	case 1:
+		return c.CompileAs(match)
+	default:
+		return NewNewtError("Wrong compiler type specified to RecursiveCompile")
+	}
+}
+
+func (c *Compiler) getObjFiles(baseObjFiles []string) string {
+	for objName, _ := range c.ObjPathList {
+		baseObjFiles = append(baseObjFiles, objName)
+	}
+
+	sort.Strings(baseObjFiles)
+	objList := strings.Join(baseObjFiles, " ")
+	return objList
+}
+
+// Calculates the command-line invocation necessary to link the specified elf
+// file.
+//
+// @param dstFile               The filename of the destination elf file to
+//                                  link.
+// @param options               Some build options specifying how the elf file
+//                                  gets generated.
+// @param objFiles              An array of the source .o and .a filenames.
+//
+// @return                      (success) The command string.
+func (c *Compiler) CompileBinaryCmd(dstFile string, options map[string]bool,
+	objFiles []string) string {
+
+	objList := c.getObjFiles(UniqueStrings(objFiles))
+
+	cmd := c.ccPath + " -o " + dstFile + " " + c.ldFlags + " " + c.Cflags
+	if c.ldResolveCircularDeps {
+		cmd += " -Wl,--start-group " + objList + " -Wl,--end-group "
+	} else {
+		cmd += " " + objList
+	}
+
+	if c.LinkerScript != "" {
+		cmd += " -T " + c.LinkerScript
+	}
+	if checkBoolMap(options, "mapFile") {
+		cmd += " -Wl,-Map=" + dstFile + ".map"
+	}
+
+	return cmd
+}
+
+// Links the specified elf file.
+//
+// @param dstFile               The filename of the destination elf file to
+//                                  link.
+// @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) CompileBinary(dstFile string, options map[string]bool,
+	objFiles []string) error {
+
+	objList := c.getObjFiles(UniqueStrings(objFiles))
+
+	StatusMessage(VERBOSITY_DEFAULT, "Linking %s\n", path.Base(dstFile))
+	StatusMessage(VERBOSITY_VERBOSE, "Linking %s with input files %s\n",
+		dstFile, objList)
+
+	cmd := c.CompileBinaryCmd(dstFile, options, objFiles)
+	rsp, err := ShellCommand(cmd)
+	if err != nil {
+		StatusMessage(VERBOSITY_QUIET, string(rsp))
+		return err
+	}
+
+	err = WriteCommandFile(dstFile, cmd)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Generates the following build artifacts:
+//    * lst file
+//    * map file
+//    * bin file
+//
+// @param elfFilename           The filename of the elf file corresponding to
+//                                  the artifacts to be generated.
+// @param options               Some build options specifying which artifacts
+//                                  get generated.
+func (c *Compiler) generateExtras(elfFilename string,
+	options map[string]bool) error {
+
+	var cmd string
+
+	if checkBoolMap(options, "listFile") {
+		listFile := elfFilename + ".lst"
+		// if list file exists, remove it
+		if NodeExist(listFile) {
+			if err := os.RemoveAll(listFile); err != nil {
+				return err
+			}
+		}
+
+		cmd = c.odPath + " -wxdS " + elfFilename + " >> " + listFile
+		_, err := 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.
+			return nil
+		}
+
+		sects := []string{".text", ".rodata", ".data"}
+		for _, sect := range sects {
+			cmd = c.odPath + " -s -j " + sect + " " + elfFilename + " >> " +
+				listFile
+			ShellCommand(cmd)
+		}
+
+		cmd = c.osPath + " " + elfFilename + " >> " + listFile
+		_, err = ShellCommand(cmd)
+		if err != nil {
+			return err
+		}
+	}
+
+	if 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)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Links the specified elf file and generates some associated artifacts (lst,
+// bin, and map files).
+//
+// @param binFile               The filename of the destination elf file to
+//                                  link.
+// @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 {
+
+	binFile += ".elf"
+
+	linkRequired, err := c.depTracker.LinkRequired(binFile, options, objFiles)
+	if err != nil {
+		return err
+	}
+	if linkRequired {
+		err := c.CompileBinary(binFile, options, objFiles)
+		if err != nil {
+			return err
+		}
+	}
+
+	err = c.generateExtras(binFile, options)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Calculates the command-line invocation necessary to archive the specified
+// static library.
+//
+// @param archiveFile           The filename of the library to archive.
+// @param objFiles              An array of the source .o filenames.
+//
+// @return                      The command string.
+func (c *Compiler) CompileArchiveCmd(archiveFile string,
+	objFiles []string) string {
+
+	objList := c.getObjFiles(objFiles)
+	return c.arPath + " rcs " + archiveFile + " " + objList
+}
+
+// Archives the specified static library.
+//
+// @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 {
+	arRequired, err := c.depTracker.ArchiveRequired(archiveFile, objFiles)
+	if err != nil {
+		return err
+	}
+	if !arRequired {
+		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)
+
+	// Delete the old archive, if it exists.
+	err = os.Remove(archiveFile)
+	if err != nil && !os.IsNotExist(err) {
+		return NewNewtError(err.Error())
+	}
+
+	cmd := c.CompileArchiveCmd(archiveFile, objFiles)
+	rsp, err := ShellCommand(cmd)
+	if err != nil {
+		StatusMessage(VERBOSITY_QUIET, string(rsp))
+		return err
+	}
+
+	err = WriteCommandFile(archiveFile, cmd)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/deps.go
----------------------------------------------------------------------
diff --git a/newt/cli/deps.go b/newt/cli/deps.go
new file mode 100644
index 0000000..23dd4b9
--- /dev/null
+++ b/newt/cli/deps.go
@@ -0,0 +1,239 @@
+package cli
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+type DepTracker struct {
+	// Most recent .o modification time.
+	MostRecent time.Time
+
+	compiler *Compiler
+}
+
+func NewDepTracker(c *Compiler) DepTracker {
+	tracker := DepTracker{
+		MostRecent: time.Unix(0, 0),
+		compiler:   c,
+	}
+
+	return tracker
+}
+
+// Parses a dependency (.d) file generated by gcc.  On success, the returned
+// string array is populated with the dependency filenames.  This function
+// expects the first line of a dependency file to have the following format:
+//
+// <file>.d: <file>.c a.h b.h c.h \
+//  d.h e.h f.h
+//
+// This function ignores all lines except for the first.
+func ParseDepsFile(filename string) ([]string, error) {
+	lines, err := ReadLines(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(lines) == 0 {
+		return []string{}, nil
+	}
+
+	// Assume only the first line is important.
+	tokens := strings.Fields(lines[0])
+	if len(tokens) == 0 {
+		return nil, 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 " +
+			"line missing ':'")
+	}
+
+	return tokens[1:], nil
+}
+
+// Updates the dependency tracker's most recent timestamp according to the
+// 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)
+	if err != nil {
+		return err
+	}
+
+	if modTime.After(tracker.MostRecent) {
+		tracker.MostRecent = modTime
+	}
+
+	return nil
+}
+
+// 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.
+//     * The existing object file was built with a different compiler
+//       invocation.
+//     * The source file has a newer modification time than the object file.
+//     * One or more included header files has a newer modification time than
+//       the object file.
+func (tracker *DepTracker) CompileRequired(srcFile string,
+	compilerType int) (bool, error) {
+	wd, _ := os.Getwd()
+	objDir := wd + "/obj/" + tracker.compiler.TargetName + "/"
+
+	objFile := objDir + strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) +
+		".o"
+	depFile := objDir + strings.TrimSuffix(srcFile, filepath.Ext(srcFile)) +
+		".d"
+
+	// If the object was previously built with a different set of options, a
+	// rebuild is necessary.
+	cmd, err := tracker.compiler.CompileFileCmd(srcFile, compilerType)
+	if err != nil {
+		return false, err
+	}
+	if CommandHasChanged(objFile, cmd) {
+		return true, nil
+	}
+
+	srcModTime, err := FileModificationTime(srcFile)
+	if err != nil {
+		return false, err
+	}
+
+	objModTime, err := FileModificationTime(objFile)
+	if err != nil {
+		return false, err
+	}
+
+	// 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) {
+		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)
+	if err != nil {
+		return false, err
+	}
+
+	if srcModTime.After(depModTime) {
+		err := tracker.compiler.GenDepsForFile(srcFile)
+		if err != nil {
+			return false, err
+		}
+	}
+
+	// Extract the dependency filenames from the dependency file.
+	deps, err := ParseDepsFile(depFile)
+	if err != nil {
+		return false, err
+	}
+
+	// Check if any dependencies are newer than the destination object file.
+	for _, dep := range deps {
+		if NodeNotExist(dep) {
+			depModTime = time.Now()
+		} else {
+			depModTime, err = FileModificationTime(dep)
+			if err != nil {
+				return false, err
+			}
+		}
+
+		if depModTime.After(objModTime) {
+			return true, nil
+		}
+	}
+
+	return false, nil
+}
+
+// Determines if the specified static library needs to be rearchived.  The
+// library needs to be archived if any of the following is true:
+//     * The destination library file does not exist.
+//     * The existing library file was built with a different compiler
+//       invocation.
+//     * One or more source object files has a newer modification time than the
+//       library file.
+func (tracker *DepTracker) ArchiveRequired(archiveFile string,
+	objFiles []string) (bool, error) {
+
+	// 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) {
+		return true, nil
+	}
+
+	// If the archive doesn't exist or is older than any object file, a rebuild
+	// is required.
+	aModTime, err := FileModificationTime(archiveFile)
+	if err != nil {
+		return false, err
+	}
+	if tracker.MostRecent.After(aModTime) {
+		return true, nil
+	}
+
+	// The library is up to date.
+	return false, nil
+}
+
+// Determines if the specified elf file needs to be linked.  Linking is
+// necessary if the elf file does not exist or has an older modification time
+// than any source object or library file.
+// Determines if the specified static library needs to be rearchived.  The
+// library needs to be archived if any of the following is true:
+//     * The destination library file does not exist.
+//     * The existing library file was built with a different compiler
+//       invocation.
+//     * One or more source object files has a newer modification time than the
+//       library file.
+func (tracker *DepTracker) LinkRequired(dstFile string,
+	options map[string]bool, objFiles []string) (bool, error) {
+
+	// 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) {
+		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)
+	if err != nil {
+		return false, err
+	}
+
+	// Check timestamp of each .o file in the project.
+	if tracker.MostRecent.After(dstModTime) {
+		return true, nil
+	}
+
+	// Check timestamp of the linker script and all input libraries.
+	if tracker.compiler.LinkerScript != "" {
+		objFiles = append(objFiles, tracker.compiler.LinkerScript)
+	}
+	for _, obj := range objFiles {
+		objModTime, err := FileModificationTime(obj)
+		if err != nil {
+			return false, err
+		}
+
+		if objModTime.After(dstModTime) {
+			return true, nil
+		}
+	}
+
+	return false, nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/downloader.go
----------------------------------------------------------------------
diff --git a/newt/cli/downloader.go b/newt/cli/downloader.go
new file mode 100644
index 0000000..b743fea
--- /dev/null
+++ b/newt/cli/downloader.go
@@ -0,0 +1,90 @@
+/*
+ Copyright 2015 Runtime Inc.
+ Licensed 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 cli
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+)
+
+type Downloader struct {
+	Repos map[string]string
+}
+
+func NewDownloader() (*Downloader, error) {
+	dl := &Downloader{}
+
+	dl.Repos = map[string]string{}
+
+	return dl, nil
+}
+
+func (dl *Downloader) gitClone(url string, branch string, dest string) error {
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Git cloning URL %s branch %s into dest %s\n", branch, url, dest)
+
+	_, err := ShellCommand(fmt.Sprintf("git clone --depth 1 -b %s %s %s", branch, url, dest))
+	if err != nil {
+		return NewNewtError(fmt.Sprintf("Command git clone %s branch %s failed",
+			url, branch))
+	}
+
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Git clone successful, removing .git directory\n")
+
+	if err := os.RemoveAll(dest + "/.git/"); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (dl *Downloader) GetRepo(repoUrl string, branch string) (string, error) {
+	// If repo already exists, return the temporary directory where it exists
+	dir, ok := dl.Repos[repoUrl+branch]
+	if ok {
+		return dir, nil
+	}
+
+	dir, err := ioutil.TempDir("", "newtrepo")
+	if err != nil {
+		return "", err
+	}
+
+	// Otherwise, get a temporary directory and place the repo there.
+	if err := dl.gitClone(repoUrl, branch, dir); err != nil {
+		return "", err
+	}
+
+	dl.Repos[repoUrl+branch] = dir
+
+	return dir, nil
+}
+
+func (dl *Downloader) DownloadFile(repoUrl string, branch string,
+	filePath string, destPath string) error {
+	repoDir, err := dl.GetRepo(repoUrl, branch)
+	if err != nil {
+		return err
+	}
+
+	if err := CopyFile(repoDir+"/"+filePath, destPath); err != nil {
+		return err
+	}
+
+	return nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/egg.go
----------------------------------------------------------------------
diff --git a/newt/cli/egg.go b/newt/cli/egg.go
new file mode 100644
index 0000000..0758a51
--- /dev/null
+++ b/newt/cli/egg.go
@@ -0,0 +1,775 @@
+/*
+ Copyright 2015 Runtime Inc.
+ Licensed 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 cli
+
+import (
+	"crypto/sha1"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+type VersMatch struct {
+	CompareType string
+	Vers        *Version
+}
+
+type Version struct {
+	Major    int64
+	Minor    int64
+	Revision int64
+}
+
+type DependencyRequirement struct {
+	Name        string
+	Stability   string
+	VersMatches []*VersMatch
+}
+
+type Egg struct {
+	// Base directory of the egg
+	BasePath string
+	// Name of the egg
+	Name string
+	// Full Name of the egg include prefix dir
+	FullName string
+	// Nest this egg belongs to
+	Nest *Nest
+	// Egg version
+	Version *Version
+	// Type of egg
+	LinkerScript string
+
+	// For BSP egg, how to download
+	DownloadScript string
+	// For BSP egg, how to start debugger and attach it to target board
+	DebugScript string
+
+	// Has the dependencies been loaded for this egg
+	DepLoaded bool
+
+	// Has the configuration been loaded for this egg
+	CfgLoaded bool
+
+	// Egg sources
+	Sources []string
+	// Egg include directories
+	Includes []string
+
+	// Egg compiler flags
+	Cflags string
+
+	// Egg linker flags
+	Lflags string
+
+	// Egg assembler flags
+	Aflags string
+
+	// Whether or not this egg is a BSP
+	IsBsp bool
+
+	// Capabilities that this egg exports
+	Capabilities []*DependencyRequirement
+
+	// Capabilities that this egg requires
+	ReqCapabilities []*DependencyRequirement
+
+	// Whether or not we've already compiled this egg
+	Built bool
+
+	// Whether or not we've already cleaned this egg
+	Clean bool
+
+	// Eggs that this egg depends on
+	Deps []*DependencyRequirement
+}
+
+type EggShell struct {
+	FullName string
+	Version  *Version
+	/* Clutch this eggshell belongs to */
+	Clutch  *Clutch
+	Hash    string
+	Deps    []*DependencyRequirement
+	Caps    []*DependencyRequirement
+	ReqCaps []*DependencyRequirement
+}
+
+func NewEggShell(clutch *Clutch) (*EggShell, error) {
+	eShell := &EggShell{
+		Clutch: clutch,
+	}
+
+	return eShell, nil
+}
+
+func (es *EggShell) serializeDepReq(name string,
+	drList []*DependencyRequirement, indent string) string {
+	drStr := ""
+	if len(drList) > 0 {
+		drStr += fmt.Sprintf("%s%s:\n", indent, name)
+		for _, dr := range drList {
+			drStr += fmt.Sprintf("%s    - %s\n", indent, dr)
+		}
+	}
+
+	return drStr
+}
+
+func (es *EggShell) Serialize(indent string) string {
+	esStr := fmt.Sprintf("%s%s:\n", indent, es.FullName)
+	indent += "    "
+	if es.Version == nil {
+		es.Version = &Version{0, 0, 0}
+	}
+	esStr += fmt.Sprintf("%svers: %s\n", indent, es.Version)
+	esStr += fmt.Sprintf("%shash: %s\n", indent, es.Hash)
+	esStr += es.serializeDepReq("deps", es.Deps, indent)
+	esStr += es.serializeDepReq("caps", es.Caps, indent)
+	esStr += es.serializeDepReq("req_caps", es.ReqCaps, indent)
+
+	return esStr
+}
+
+func (v *Version) compareVersions(vers1 *Version, vers2 *Version) int64 {
+	log.Printf("[DEBUG] Comparing %s to %s (%d %d %d)", vers1, vers2,
+		vers1.Major-vers2.Major, vers1.Minor-vers2.Minor,
+		vers1.Revision-vers2.Revision)
+
+	if r := vers1.Major - vers2.Major; r != 0 {
+		return r
+	}
+
+	if r := vers1.Minor - vers2.Minor; r != 0 {
+		return r
+	}
+
+	if r := vers1.Revision - vers2.Revision; r != 0 {
+		return r
+	}
+
+	return 0
+}
+
+func (v *Version) SatisfiesVersion(versMatches []*VersMatch) bool {
+	if versMatches == nil {
+		return true
+	}
+
+	for _, match := range versMatches {
+		r := v.compareVersions(match.Vers, v)
+		switch match.CompareType {
+		case "<":
+			if r <= 0 {
+				return false
+			}
+		case "<=":
+			if r < 0 {
+				return false
+			}
+		case ">":
+			if r >= 0 {
+				return false
+			}
+		case ">=":
+			if r > 0 {
+				return false
+			}
+		case "==":
+			if r != 0 {
+				return false
+			}
+		}
+	}
+
+	return true
+}
+
+func (vers *Version) String() string {
+	return fmt.Sprintf("%d.%d.%d", vers.Major, vers.Minor, vers.Revision)
+}
+
+func NewVersParseString(versStr string) (*Version, error) {
+	var err error
+
+	parts := strings.Split(versStr, ".")
+	if len(parts) > 3 {
+		return nil, NewNewtError(fmt.Sprintf("Invalid version string: %s", versStr))
+	}
+
+	if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[0], " ") == "none" {
+		return nil, nil
+	}
+
+	v := &Version{}
+
+	// convert first string to an int
+	if v.Major, err = strconv.ParseInt(parts[0], 0, 64); err != nil {
+		return nil, NewNewtError(err.Error())
+	}
+	if len(parts) >= 2 {
+		if v.Minor, err = strconv.ParseInt(parts[1], 0, 64); err != nil {
+			return nil, NewNewtError(err.Error())
+		}
+	}
+	if len(parts) == 3 {
+		if v.Revision, err = strconv.ParseInt(parts[2], 0, 64); err != nil {
+			return nil, NewNewtError(err.Error())
+		}
+	}
+
+	return v, nil
+}
+
+//
+// Set the version comparison constraints on a dependency requirement.
+// The version string contains a list of version constraints in the following format:
+//    - <comparison><version>
+// Where <comparison> can be any one of the following comparison
+//   operators: <=, <, >, >=, ==
+// And <version> is specified in the form: X.Y.Z where X, Y and Z are all
+// int64 types in decimal form
+func (dr *DependencyRequirement) SetVersStr(versStr string) error {
+	var err error
+
+	re, err := regexp.Compile(`(<=|>=|==|>|<)([\d\.]+)`)
+	if err != nil {
+		return err
+	}
+
+	matches := re.FindAllStringSubmatch(versStr, -1)
+	if matches != nil {
+		dr.VersMatches = make([]*VersMatch, 0, len(matches))
+		for _, match := range matches {
+			vm := &VersMatch{}
+			vm.CompareType = match[1]
+			if vm.Vers, err = NewVersParseString(match[2]); err != nil {
+				return err
+			}
+
+			if vm.Vers != nil {
+				dr.VersMatches = append(dr.VersMatches, vm)
+			}
+		}
+	} else {
+		dr.VersMatches = make([]*VersMatch, 0)
+		vm := &VersMatch{}
+		vm.CompareType = "=="
+		if vm.Vers, err = NewVersParseString(versStr); err != nil {
+			return err
+		}
+		if vm.Vers != nil {
+			dr.VersMatches = append(dr.VersMatches, vm)
+		}
+	}
+
+	if len(dr.VersMatches) == 0 {
+		dr.VersMatches = nil
+	}
+
+	return nil
+}
+
+// Convert the array of version matches into a string for display
+func (dr *DependencyRequirement) VersMatchesString() string {
+	if dr.VersMatches != nil {
+		str := ""
+		for _, match := range dr.VersMatches {
+			str += fmt.Sprintf("%s%s", match.CompareType, match.Vers)
+		}
+		return str
+	} else {
+		return "none"
+	}
+}
+
+// Convert the dependency requirement to a string for display
+func (dr *DependencyRequirement) String() string {
+	return fmt.Sprintf("%s@%s#%s", dr.Name, dr.VersMatchesString(), dr.Stability)
+}
+
+func (dr *DependencyRequirement) SatisfiesCapability(
+	capability *DependencyRequirement) error {
+	if dr.Name != capability.Name {
+		return NewNewtError(fmt.Sprintf("Required capability name %s doesn't match "+
+			"specified capability name %s", dr.Name, capability.Name))
+	}
+
+	for _, versMatch := range dr.VersMatches {
+		if !versMatch.Vers.SatisfiesVersion(capability.VersMatches) {
+			return NewNewtError(fmt.Sprintf("Capability %s doesn't satisfy version "+
+				"requirement %s", capability, versMatch.Vers))
+		}
+	}
+
+	return nil
+}
+
+// Check whether the passed in egg satisfies the current dependency requirement
+func (dr *DependencyRequirement) SatisfiesDependency(egg *Egg) bool {
+	if egg.FullName != dr.Name {
+		return false
+	}
+
+	if egg.Version.SatisfiesVersion(dr.VersMatches) {
+		return true
+	}
+
+	return false
+}
+
+// Convert the dependency requirement to branch name to look for
+func (dr *DependencyRequirement) BranchName() string {
+	if dr.Stability != "stable" {
+		// XXX should compare to latest
+		return dr.Stability
+	}
+	for _, versMatch := range dr.VersMatches {
+		if versMatch.CompareType == "==" || versMatch.CompareType == "<=" {
+			if versMatch.Vers.Minor == 0 && versMatch.Vers.Revision == 0 {
+				return fmt.Sprintf("%d", versMatch.Vers.Major)
+			} else if versMatch.Vers.Revision == 0 {
+				return fmt.Sprintf("%d.%d", versMatch.Vers.Major,
+					versMatch.Vers.Minor)
+			} else {
+				return fmt.Sprintf("%d.%d.%d", versMatch.Vers.Major,
+					versMatch.Vers.Minor, versMatch.Vers.Revision)
+			}
+		}
+		// XXX What to do with other version comparisons?
+	}
+	return "master"
+}
+
+// Create a New DependencyRequirement structure from the contents of the depReq
+// string that has been passed in as an argument.
+func NewDependencyRequirementParseString(depReq string) (*DependencyRequirement,
+	error) {
+	// Allocate dependency requirement
+	dr := &DependencyRequirement{}
+	// Split string into multiple parts, @#
+	// first, get dependency name
+	parts := strings.Split(depReq, "@")
+	if len(parts) == 1 {
+		parts = strings.Split(depReq, "#")
+		dr.Name = parts[0]
+		if len(parts) > 1 {
+			dr.Stability = parts[1]
+		} else {
+			dr.Stability = "stable"
+		}
+	} else if len(parts) == 2 {
+		dr.Name = parts[0]
+		verParts := strings.Split(parts[1], "#")
+
+		if err := dr.SetVersStr(verParts[0]); err != nil {
+			return nil, err
+		}
+		if len(verParts) == 2 {
+			dr.Stability = verParts[1]
+		} else {
+			dr.Stability = "stable"
+		}
+	}
+
+	return dr, nil
+}
+
+// Get a map of egg capabilities.  The returned map contains the name of the
+// capability, and its version as the key, and a pointer to the
+// Capability structure associated with that name.
+func (egg *Egg) GetCapabilities() ([]*DependencyRequirement, error) {
+	return egg.Capabilities, nil
+}
+
+// Return the egg dependencies for this egg.
+func (egg *Egg) GetDependencies() ([]*DependencyRequirement, error) {
+	return egg.Deps, nil
+}
+
+func (egg *Egg) GetReqCapabilities() ([]*DependencyRequirement, error) {
+	return egg.ReqCapabilities, nil
+}
+
+func (eggShell *EggShell) GetCapabilities() ([]*DependencyRequirement, error) {
+	return eggShell.Caps, nil
+}
+
+// Return the egg dependencies for this eggShell.
+func (eggShell *EggShell) GetDependencies() ([]*DependencyRequirement, error) {
+	return eggShell.Deps, nil
+}
+
+func (eggShell *EggShell) GetReqCapabilities() ([]*DependencyRequirement, error) {
+	return eggShell.ReqCaps, nil
+}
+
+// Load a egg's configuration information from the egg config
+// file.
+func (egg *Egg) GetIncludes(t *Target) ([]string, error) {
+	// Return the include directories for just this egg
+	incls := []string{
+		egg.BasePath + "/include/",
+		egg.BasePath + "/include/" + egg.Name + "/arch/" + t.Arch + "/",
+	}
+
+	return incls, nil
+}
+
+// Load capabilities from a string containing a list of capabilities.
+// The capability format is expected to be one of:
+//   name@version
+//   name
+// @param capList An array of capability strings
+// @return On success error is nil, and a list of capabilities is returned,
+// on failure error is non-nil
+func (egg *Egg) loadCaps(capList []string) ([]*DependencyRequirement, error) {
+	if len(capList) == 0 {
+		return nil, nil
+	}
+
+	// Allocate an array of capabilities
+	caps := make([]*DependencyRequirement, 0)
+
+	StatusMessage(VERBOSITY_VERBOSE, "Loading capabilities %s\n",
+		strings.Join(capList, " "))
+	for _, capItem := range capList {
+		dr, err := NewDependencyRequirementParseString(capItem)
+		if err != nil {
+			return nil, err
+		}
+
+		caps = append(caps, dr)
+		log.Printf("[DEBUG] Appending new capability egg: %s, cap:%s",
+			egg.Name, dr)
+	}
+
+	return caps, nil
+}
+
+// Create a dependency requirement out of an egg
+//
+func (egg *Egg) MakeDependency() (*DependencyRequirement, error) {
+	return NewDependencyRequirementParseString(egg.FullName)
+}
+
+// Generate a hash of the contents of an egg.  This function recursively
+// processes the contents of a directory, ignoring hidden files and the
+// bin and obj directories.  It returns a hash of all the files, their
+// contents.
+func (egg *Egg) GetHash() (string, error) {
+	hash := sha1.New()
+
+	err := filepath.Walk(egg.BasePath,
+		func(path string, info os.FileInfo, err error) error {
+			name := info.Name()
+			if name == "bin" || name == "obj" || name[0] == '.' {
+				return filepath.SkipDir
+			}
+
+			if info.IsDir() {
+				// SHA the directory name into the hash
+				hash.Write([]byte(name))
+			} else {
+				// SHA the file name & contents into the hash
+				contents, err := ioutil.ReadFile(path)
+				if err != nil {
+					return err
+				}
+				hash.Write(contents)
+			}
+			return nil
+		})
+	if err != nil && err != filepath.SkipDir {
+		return "", NewNewtError(err.Error())
+	}
+
+	hashStr := fmt.Sprintf("%x", hash.Sum(nil))
+
+	return hashStr, nil
+}
+
+// Load egg's configuration, and collect required capabilities, dependencies, and
+// identities it provides, so we'll have this available when target is being built.
+func (egg *Egg) LoadDependencies(identities map[string]string,
+	capabilities map[string]string) error {
+
+	if egg.DepLoaded {
+		return nil
+	}
+
+	log.Printf("[DEBUG] Loading dependencies for egg %s", egg.BasePath)
+
+	v, err := ReadConfig(egg.BasePath, "egg")
+	if err != nil {
+		return err
+	}
+
+	// Append all identities that this egg exposes.
+	idents := GetStringSliceIdentities(v, identities, "egg.identities")
+
+	// Add these to project identities
+	for _, item := range idents {
+		StatusMessage(VERBOSITY_VERBOSE, "    Adding identity %s - %s\n", item,
+			egg.FullName)
+		identities[item] = egg.FullName
+	}
+
+	// Load the list of capabilities that this egg exposes
+	egg.Capabilities, err = egg.loadCaps(GetStringSliceIdentities(v, identities,
+		"egg.caps"))
+	if err != nil {
+		return err
+	}
+
+	// Add these to project capabilities
+	for _, cap := range egg.Capabilities {
+		if capabilities[cap.String()] != "" &&
+			capabilities[cap.String()] != egg.FullName {
+
+			return NewNewtError(fmt.Sprintf("Multiple eggs with "+
+				"capability %s (%s and %s)",
+				cap.String(), capabilities[cap.String()], egg.FullName))
+		}
+		capabilities[cap.String()] = egg.FullName
+		StatusMessage(VERBOSITY_VERBOSE, "    Adding capability %s - %s\n",
+			cap.String(), egg.FullName)
+	}
+
+	// Load the list of capabilities that this egg requires
+	egg.ReqCapabilities, err = egg.loadCaps(GetStringSliceIdentities(v, identities,
+		"egg.req_caps"))
+	if err != nil {
+		return err
+	}
+
+	// Load egg dependencies
+	depList := GetStringSliceIdentities(v, identities, "egg.deps")
+	if len(depList) > 0 {
+		egg.Deps = make([]*DependencyRequirement, 0, len(depList))
+		for _, depStr := range depList {
+			log.Printf("[DEBUG] Loading dependency %s from egg %s", depStr,
+				egg.FullName)
+			dr, err := NewDependencyRequirementParseString(depStr)
+			if err != nil {
+				return err
+			}
+
+			egg.Deps = append(egg.Deps, dr)
+		}
+	}
+	for _, cap := range egg.ReqCapabilities {
+		eggName := capabilities[cap.String()]
+		if eggName == "" {
+			continue
+		}
+		dr, err := NewDependencyRequirementParseString(eggName)
+		if err != nil {
+			return err
+		}
+		egg.Deps = append(egg.Deps, dr)
+	}
+
+	// Check these as well
+	egg.LinkerScript = GetStringIdentities(v, identities, "egg.linkerscript")
+	egg.DownloadScript = GetStringIdentities(v, identities, "egg.downloadscript")
+	egg.DebugScript = GetStringIdentities(v, identities, "egg.debugscript")
+
+	egg.Cflags = GetStringIdentities(v, identities, "egg.cflags")
+	egg.Lflags = GetStringIdentities(v, identities, "egg.lflags")
+	egg.Aflags = GetStringIdentities(v, identities, "egg.aflags")
+
+	egg.DepLoaded = true
+
+	return nil
+}
+
+// Collect identities and capabilities that egg and it's dependencies provide
+func (egg *Egg) collectDependencies(clutch *Clutch,
+	identities map[string]string,
+	capabilities map[string]string) error {
+
+	if egg.DepLoaded {
+		return nil
+	}
+
+	StatusMessage(VERBOSITY_VERBOSE, "  Collecting egg %s dependencies\n", egg.Name)
+
+	err := egg.LoadDependencies(identities, capabilities)
+	if err != nil {
+		return err
+	}
+
+	for _, dep := range egg.Deps {
+		egg, err := clutch.ResolveEggName(dep.Name)
+		if err != nil {
+			return err
+		}
+		err = egg.collectDependencies(clutch, identities, capabilities)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Clear the var which says that dependencies have been checked for this egg and
+// it's dependencies
+func (egg *Egg) clearDependencyMarker(clutch *Clutch) {
+
+	if egg.DepLoaded == false {
+		return
+	}
+	egg.DepLoaded = false
+
+	for _, dep := range egg.Deps {
+		egg, err := clutch.ResolveEggName(dep.Name)
+		if err == nil {
+			egg.clearDependencyMarker(clutch)
+		}
+	}
+}
+
+// Load a egg's configuration.  This allocates & initializes a fair number of
+// the main data structures within the egg.
+func (egg *Egg) LoadConfig(t *Target, force bool) error {
+	if egg.CfgLoaded && !force {
+		return nil
+	}
+
+	log.Printf("[DEBUG] Loading configuration for egg %s", egg.BasePath)
+
+	v, err := ReadConfig(egg.BasePath, "egg")
+	if err != nil {
+		return err
+	}
+
+	egg.FullName = v.GetString("egg.name")
+	egg.Name = filepath.Base(egg.FullName)
+
+	egg.Version, err = NewVersParseString(v.GetString("egg.vers"))
+	if err != nil {
+		return err
+	}
+
+	// Append all the identities that this egg exposes to sub-eggs.  This must
+	// be done before the remainder of the settings, as some settings depend on
+	// identity.
+	identities := map[string]string{}
+	if t != nil {
+		identities = t.Identities;
+		idents := GetStringSliceIdentities(v, identities, "egg.identities")
+		for _, item := range idents {
+		    identities[item] = egg.FullName;
+		}
+	}
+
+	egg.LinkerScript = GetStringIdentities(v, identities, "egg.linkerscript")
+	egg.DownloadScript = GetStringIdentities(v, identities, "egg.downloadscript")
+	egg.DebugScript = GetStringIdentities(v, identities, "egg.debugscript")
+
+	egg.Cflags += GetStringIdentities(v, identities, "egg.cflags")
+	egg.Lflags += GetStringIdentities(v, identities, "egg.lflags")
+	egg.Aflags += GetStringIdentities(v, identities, "egg.aflags")
+
+	// Load egg dependencies
+	depList := GetStringSliceIdentities(v, identities, "egg.deps")
+	if len(depList) > 0 {
+		egg.Deps = make([]*DependencyRequirement, 0, len(depList))
+		for _, depStr := range depList {
+			log.Printf("[DEBUG] Loading dependency %s from egg %s", depStr,
+				egg.FullName)
+			dr, err := NewDependencyRequirementParseString(depStr)
+			if err != nil {
+				return err
+			}
+
+			egg.Deps = append(egg.Deps, dr)
+		}
+	}
+
+	// Load the list of capabilities that this egg exposes
+	egg.Capabilities, err = egg.loadCaps(GetStringSliceIdentities(v, identities,
+		"egg.caps"))
+	if err != nil {
+		return err
+	}
+
+	// Load the list of capabilities that this egg requires
+	egg.ReqCapabilities, err = egg.loadCaps(GetStringSliceIdentities(v, identities,
+		"egg.req_caps"))
+	if err != nil {
+		return err
+	}
+	if len(egg.ReqCapabilities) > 0 {
+		for _, reqStr := range egg.ReqCapabilities {
+			log.Printf("[DEBUG] Loading reqCap %s from egg %s", reqStr,
+				egg.FullName)
+		}
+	}
+	egg.CfgLoaded = true
+
+	return nil
+}
+
+// Initialize a egg: loads the egg configuration, and sets up egg data
+// structures.  Should only be called from NewEgg
+func (egg *Egg) Init() error {
+	return nil
+}
+
+// Allocate and initialize a new egg, and return a fully initialized Egg
+//     structure.
+// @param nest The Nest this egg is located in
+// @param basePath The path to this egg, within the specified nest
+// @return On success, error is nil, and a Egg is returned.  on failure,
+//         error is not nil.
+func NewEgg(nest *Nest, basePath string) (*Egg, error) {
+	egg := &Egg{
+		BasePath: basePath,
+		Nest:     nest,
+	}
+
+	if err := egg.Init(); err != nil {
+		return nil, err
+	}
+
+	return egg, nil
+}
+
+func (egg *Egg) TestBinName() string {
+	return "test_" + egg.Name
+}
+
+/*
+ * Download egg from a clutch and stick it to nest.
+ */
+func (eggShell *EggShell) Install(eggMgr *Clutch, branch string) error {
+	downloaded, err := eggMgr.InstallEgg(eggShell.FullName, branch, nil)
+	for _, remoteNest := range downloaded {
+		remoteNest.Remove()
+	}
+	return err
+}
+
+func (egg *Egg) Remove() error {
+	return os.RemoveAll(egg.BasePath)
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/nest.go
----------------------------------------------------------------------
diff --git a/newt/cli/nest.go b/newt/cli/nest.go
new file mode 100644
index 0000000..abc2bce
--- /dev/null
+++ b/newt/cli/nest.go
@@ -0,0 +1,467 @@
+/*
+ Copyright 2015 Runtime Inc.
+ Licensed 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 cli
+
+import (
+	"database/sql"
+	"fmt"
+	_ "github.com/mattn/go-sqlite3"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+)
+
+type Nest struct {
+	// Name of the Nest
+	Name string
+
+	// Path to the Nest Store
+	StorePath string
+
+	// Path to the Nest Clutches
+	ClutchPath string
+
+	// Nest File
+	NestFile string
+
+	// Base path of the nest
+	BasePath string
+
+	// Store of Clutches
+	Clutches map[string]*Clutch
+
+	// Configuration
+	Config map[string]map[string]string
+
+	// The database handle for the nest configuration database
+	db *sql.DB
+}
+
+// Create a new Nest object and initialize it
+func NewNest() (*Nest, error) {
+	n := &Nest{}
+
+	err := n.Init()
+	if err != nil {
+		return nil, err
+	}
+
+	return n, nil
+}
+
+// Create a Nest object constructed out of repo in given path
+func NewNestWithDir(srcDir string) (*Nest, error) {
+	n := &Nest{}
+
+	err := n.InitPath(srcDir)
+	if err != nil {
+		return nil, err
+	}
+
+	return n, nil
+}
+
+func CreateNest(nestName string, destDir string, tadpoleUrl string) error {
+	if tadpoleUrl == "" {
+		tadpoleUrl = "https://git-wip-us.apache.org/repos/asf/incubator-mynewt-tadpole.git"
+	}
+
+	if NodeExist(destDir) {
+		return NewNewtError(fmt.Sprintf("Directory %s already exists, "+
+			" cannot create new newt nest", destDir))
+	}
+
+	dl, err := NewDownloader()
+	if err != nil {
+		return err
+	}
+
+	StatusMessage(VERBOSITY_DEFAULT, "Downloading nest skeleton from %s...",
+		tadpoleUrl)
+	if err := dl.DownloadFile(tadpoleUrl, "master", "/",
+		destDir); err != nil {
+		return err
+	}
+	StatusMessage(VERBOSITY_DEFAULT, OK_STRING)
+
+	// Overwrite nest.yml
+	contents := []byte(fmt.Sprintf("nest.name: %s\n", nestName))
+	if err := ioutil.WriteFile(destDir+"/nest.yml",
+		contents, 0644); err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	// DONE!
+
+	return nil
+}
+
+// Get a temporary directory to stick stuff in
+func (nest *Nest) GetTmpDir(dirName string, prefix string) (string, error) {
+	tmpDir := dirName
+	if NodeNotExist(tmpDir) {
+		if err := os.MkdirAll(tmpDir, 0700); err != nil {
+			return "", err
+		}
+	}
+
+	name, err := ioutil.TempDir(tmpDir, prefix)
+	if err != nil {
+		return "", err
+	}
+
+	return name, nil
+}
+
+// Find the repo file.  Searches the current directory, and then recurses
+// parent directories until it finds a file named .repo.yml
+// if no repo file found in the directory heirarchy, an error is returned
+func (nest *Nest) getNestFile() (string, error) {
+	rFile := ""
+
+	curDir, err := os.Getwd()
+	if err != nil {
+		return rFile, NewNewtError(err.Error())
+	}
+
+	for {
+		rFile = curDir + "/nest.yml"
+		log.Printf("[DEBUG] Searching for nest file at %s", rFile)
+		if _, err := os.Stat(rFile); err == nil {
+			log.Printf("[DEBUG] Found nest file at %s!", rFile)
+			break
+		}
+
+		curDir = path.Clean(curDir + "../../")
+		if curDir == "/" {
+			rFile = ""
+			err = NewNewtError("No repo file found!")
+			break
+		}
+	}
+
+	return rFile, err
+}
+
+// Create the contents of the configuration database
+func (nest *Nest) createDb(db *sql.DB) error {
+	query := `
+	CREATE TABLE IF NOT EXISTS newt_cfg (
+		cfg_name VARCHAR(255) NOT NULL,
+		key VARCHAR(255) NOT NULL,
+		value TEXT
+	)
+	`
+	_, err := db.Exec(query)
+	if err != nil {
+		return NewNewtError(err.Error())
+	} else {
+		return nil
+	}
+}
+
+// Initialize the configuration database specified by dbName.  If the database
+// doesn't exist, create it.
+func (nest *Nest) initDb(dbName string) error {
+	db, err := sql.Open("sqlite3", dbName)
+	if err != nil {
+		return err
+	}
+	nest.db = db
+
+	err = nest.createDb(db)
+	if err != nil {
+		return err
+	}
+
+	// Populate repo configuration
+	log.Printf("[DEBUG] Populating Nest configuration from %s", dbName)
+
+	rows, err := db.Query("SELECT * FROM newt_cfg")
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		var cfgName sql.NullString
+		var cfgKey sql.NullString
+		var cfgVal sql.NullString
+
+		err := rows.Scan(&cfgName, &cfgKey, &cfgVal)
+		if err != nil {
+			return NewNewtError(err.Error())
+		}
+
+		log.Printf("[DEBUG] Setting sect %s, key %s to val %s", cfgName.String,
+			cfgKey.String, cfgVal.String)
+
+		_, ok := nest.Config[cfgName.String]
+		if !ok {
+			nest.Config[cfgName.String] = make(map[string]string)
+		}
+
+		nest.Config[cfgName.String][cfgKey.String] = cfgVal.String
+	}
+
+	return nil
+}
+
+// Get a configuration variable in section sect, with key
+// error is populated if variable doesn't exist
+func (nest *Nest) GetConfig(sect string, key string) (string, error) {
+	sectMap, ok := nest.Config[sect]
+	if !ok {
+		return "", NewNewtError("No configuration section exists")
+	}
+
+	val, ok := sectMap[key]
+	if !ok {
+		return "", NewNewtError("No configuration variable exists")
+	}
+
+	return val, nil
+}
+
+func (nest *Nest) GetConfigSect(sect string) (map[string]string, error) {
+	sm, ok := nest.Config[sect]
+	if !ok {
+		return nil, NewNewtError("No configuration section exists")
+	}
+
+	return sm, nil
+}
+
+// Delete a configuration variable in section sect with key and val
+// Returns an error if configuration variable cannot be deleted
+// (most likely due to database error or key not existing)
+func (nest *Nest) DelConfig(sect string, key string) error {
+	db := nest.db
+
+	log.Printf("[DEBUG] Deleting sect %s, key %s", sect, key)
+
+	tx, err := db.Begin()
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	stmt, err := tx.Prepare("DELETE FROM newt_cfg WHERE cfg_name=? AND key=?")
+	if err != nil {
+		return err
+	}
+	defer stmt.Close()
+
+	res, err := stmt.Exec(sect, key)
+	if err != nil {
+		return err
+	}
+
+	tx.Commit()
+
+	if affected, err := res.RowsAffected(); affected > 0 && err == nil {
+		log.Printf("[DEBUG] sect %s, key %s successfully deleted from database",
+			sect, key)
+	} else {
+		log.Printf("[DEBUG] sect %s, key %s not found, considering \"delete\" successful",
+			sect, key)
+	}
+
+	return nil
+}
+
+// Set a configuration variable in section sect with key, and val
+// Returns an error if configuration variable cannot be set
+// (most likely not able to set it in database.)
+func (nest *Nest) SetConfig(sect string, key string, val string) error {
+	_, ok := nest.Config[sect]
+	if !ok {
+		nest.Config[sect] = make(map[string]string)
+	}
+	nest.Config[sect][key] = val
+
+	// Store config
+	log.Printf("[DEBUG] Storing value %s into key %s for section %s",
+		val, sect, key)
+	db := nest.db
+
+	tx, err := db.Begin()
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	stmt, err := tx.Prepare(
+		"UPDATE newt_cfg SET value=? WHERE cfg_name=? AND key=?")
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+	defer stmt.Close()
+
+	res, err := stmt.Exec(val, sect, key)
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	// Value already existed, and we updated it.  Mission accomplished!
+	// Exit
+	if affected, err := res.RowsAffected(); affected > 0 && err == nil {
+		tx.Commit()
+		log.Printf("[DEBUG] Key %s, sect %s successfully updated to %s", key, sect, val)
+		return nil
+	}
+
+	// Otherwise, insert a new row
+	stmt1, err := tx.Prepare("INSERT INTO newt_cfg VALUES (?, ?, ?)")
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+	defer stmt1.Close()
+
+	_, err = stmt1.Exec(sect, key, val)
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	tx.Commit()
+
+	log.Printf("[DEBUG] Key %s, sect %s successfully create, value set to %s",
+		key, sect, val)
+
+	return nil
+}
+
+// Load the repo configuration file
+func (nest *Nest) loadConfig() error {
+	v, err := ReadConfig(nest.BasePath, "nest")
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	nest.Name = v.GetString("nest.name")
+	if nest.Name == "" {
+		return NewNewtError("Nest file must specify nest name")
+	}
+
+	return nil
+}
+
+func (nest *Nest) LoadClutches() error {
+	files, err := ioutil.ReadDir(nest.ClutchPath)
+	if err != nil {
+		return err
+	}
+	for _, fileInfo := range files {
+		file := fileInfo.Name()
+		if filepath.Ext(file) == ".yml" {
+			name := file[:len(filepath.Base(file))-len(".yml")]
+			log.Printf("[DEBUG] Loading Clutch %s", name)
+			clutch, err := NewClutch(nest)
+			if err != nil {
+				return err
+			}
+			if err := clutch.Load(name); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (nest *Nest) InitPath(nestPath string) error {
+	cwd, err := os.Getwd()
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	if err = os.Chdir(nestPath); err != nil {
+		return NewNewtError(err.Error())
+	}
+
+	log.Printf("[DEBUG] Searching for repository, starting in directory %s", cwd)
+
+	if nest.NestFile, err = nest.getNestFile(); err != nil {
+		return err
+	}
+
+	log.Printf("[DEBUG] Nest file found, directory %s, loading configuration...",
+		nest.NestFile)
+
+	nest.BasePath = filepath.ToSlash(path.Dir(nest.NestFile))
+
+	if err = nest.loadConfig(); err != nil {
+		return err
+	}
+
+	if err = os.Chdir(cwd); err != nil {
+		return NewNewtError(err.Error())
+	}
+	return nil
+}
+
+// Initialze the repository
+// returns a NewtError on failure, and nil on success
+func (nest *Nest) Init() error {
+	var err error
+
+	cwd, err := os.Getwd()
+	if err != nil {
+		return NewNewtError(err.Error())
+	}
+	if err := nest.InitPath(cwd); err != nil {
+		return err
+	}
+
+	log.Printf("[DEBUG] Configuration loaded!  Initializing .nest database")
+
+	// Create Nest store directory
+	nest.StorePath = nest.BasePath + "/.nest/"
+	if NodeNotExist(nest.StorePath) {
+		if err := os.MkdirAll(nest.StorePath, 0755); err != nil {
+			return NewNewtError(err.Error())
+		}
+	}
+
+	// Create Nest configuration database
+	nest.Config = make(map[string]map[string]string)
+
+	dbName := nest.StorePath + "/nest.db"
+	if err := nest.initDb(dbName); err != nil {
+		return err
+	}
+
+	log.Printf("[DEBUG] Database initialized.")
+
+	// Load Clutches for the current Nest
+	nest.ClutchPath = nest.StorePath + "/clutches/"
+	if NodeNotExist(nest.ClutchPath) {
+		if err := os.MkdirAll(nest.ClutchPath, 0755); err != nil {
+			return NewNewtError(err.Error())
+		}
+	}
+
+	nest.Clutches = map[string]*Clutch{}
+
+	if err := nest.LoadClutches(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (nest *Nest) GetClutches() (map[string]*Clutch, error) {
+	return nest.Clutches, nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/project.go
----------------------------------------------------------------------
diff --git a/newt/cli/project.go b/newt/cli/project.go
new file mode 100644
index 0000000..e7aa0da
--- /dev/null
+++ b/newt/cli/project.go
@@ -0,0 +1,484 @@
+/*
+ Copyright 2015 Runtime Inc.
+ Licensed 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 cli
+
+import (
+	"log"
+	"os"
+)
+
+// Structure representing a project
+type Project struct {
+	// Project name
+	Name string
+
+	// Base path of project
+	BasePath string
+
+	// Eggs
+	Eggs []string
+
+	// Capabilities
+	Capabilities []string
+
+	// Assembler compiler flags
+	Aflags string
+
+	// Compiler flags
+	Cflags string
+
+	// Linker flags
+	Lflags string
+
+	// The repository the project is located in
+	Nest *Nest
+
+	// The target associated with this project
+	Target *Target
+}
+
+// Load and initialize a project specified by name
+// nest & t are the nest and target to associate the project with
+func LoadProject(nest *Nest, t *Target, name string) (*Project, error) {
+	p := &Project{
+		Name:   name,
+		Nest:   nest,
+		Target: t,
+	}
+
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Loading project %s for repo %s, target %s\n",
+		name, nest.BasePath, t.Name)
+
+	if err := p.Init(); err != nil {
+		return nil, err
+	} else {
+		return p, nil
+	}
+}
+
+// Get the packages associated with the project
+func (p *Project) GetEggs() []string {
+	return p.Eggs
+}
+
+// Load project configuration
+func (p *Project) loadConfig() error {
+	log.Printf("[DEBUG] Reading Project configuration for %s in %s",
+		p.Name, p.BasePath)
+
+	v, err := ReadConfig(p.BasePath, p.Name)
+	if err != nil {
+		return err
+	}
+
+	t := p.Target
+
+	p.Eggs = GetStringSliceIdentities(v, t.Identities, "project.eggs")
+
+	idents := GetStringSliceIdentities(v, t.Identities, "project.identities")
+	for _, ident := range idents {
+		t.Identities[ident] = p.Name
+	}
+	p.Capabilities = GetStringSliceIdentities(v, t.Identities, "project.caps")
+
+	p.Cflags = GetStringIdentities(v, t.Identities, "project.cflags")
+	p.Lflags = GetStringIdentities(v, t.Identities, "project.lflags")
+	p.Aflags = GetStringIdentities(v, t.Identities, "project.aflags")
+
+	return nil
+}
+
+// Clean the project build, and all packages that were built with the
+// project, if cleanAll is true, then clean everything, not just the current
+// architecture
+func (p *Project) BuildClean(cleanAll bool) error {
+	clutch, err := NewClutch(p.Nest)
+	if err != nil {
+		return err
+	}
+
+	// first, clean packages
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Cleaning all the packages associated with project %s", p.Name)
+	for _, eggName := range p.GetEggs() {
+		err = clutch.BuildClean(p.Target, eggName, cleanAll)
+		if err != nil {
+			return err
+		}
+	}
+
+	// clean the BSP, if it exists
+	if p.Target.Bsp != "" {
+		if err := clutch.BuildClean(p.Target, p.Target.Bsp, cleanAll); err != nil {
+			return err
+		}
+	}
+
+	c, err := NewCompiler(p.Target.GetCompiler(), p.Target.Cdef, p.Target.Name, []string{})
+	if err != nil {
+		return err
+	}
+
+	tName := p.Target.Name
+	if cleanAll {
+		tName = ""
+	}
+
+	if err := c.RecursiveClean(p.BasePath, tName); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Collect all identities and capabilities that project has
+func (p *Project) collectAllDeps(clutch *Clutch, identities map[string]string,
+	capabilities map[string]string) error {
+
+	eggList := p.GetEggs()
+	if eggList == nil {
+		return nil
+	}
+
+	StatusMessage(VERBOSITY_VERBOSE, " Collecting all project dependencies\n")
+
+	t := p.Target
+
+	eggList = append(eggList, t.Dependencies...)
+	if t.Bsp != "" {
+		eggList = append(eggList, t.Bsp)
+	}
+
+	for _, eggName := range eggList {
+		if eggName == "" {
+			continue
+		}
+
+		egg, err := clutch.ResolveEggName(eggName)
+		if err != nil {
+			return err
+		}
+
+		err = egg.collectDependencies(clutch, identities, capabilities)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (p *Project) clearAllDeps(clutch *Clutch) {
+	eggList := p.GetEggs()
+	if eggList == nil {
+		return
+	}
+
+	t := p.Target
+
+	eggList = append(eggList, t.Dependencies...)
+	if t.Bsp != "" {
+		eggList = append(eggList, t.Bsp)
+	}
+
+	for _, eggName := range eggList {
+		if eggName == "" {
+			continue
+		}
+		egg, err := clutch.ResolveEggName(eggName)
+		if err != nil {
+			return
+		}
+		egg.clearDependencyMarker(clutch)
+	}
+}
+
+// Collect project identities and capabilities, and make target ready for
+// building.
+func (p *Project) collectDeps(clutch *Clutch) error {
+
+	identCount := 0
+	capCount := 0
+
+	t := p.Target
+
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Collecting egg dependencies for project %s\n", p.Name)
+
+	// Need to do this multiple times, until there are no new identities,
+	// capabilities which show up.
+	identities := t.Identities
+	capabilities := map[string]string{}
+	for {
+		err := p.collectAllDeps(clutch, identities, capabilities)
+		if err != nil {
+			return err
+		}
+		newIdentCount := len(identities)
+		newCapCount := len(capabilities)
+		StatusMessage(VERBOSITY_VERBOSE, "Collected idents %d caps %d\n",
+			newIdentCount, newCapCount)
+		if identCount == newIdentCount && capCount == newCapCount {
+			break
+		}
+		p.clearAllDeps(clutch)
+		identCount = newIdentCount
+		capCount = newCapCount
+	}
+
+	return nil
+}
+
+// Build the packages that this project depends on
+// clutch is an initialized package manager, incls is an array of includes to
+// append to (package includes get append as they are built)
+// libs is an array of archive files to append to (package libraries get
+// appended as they are built)
+func (p *Project) buildDeps(clutch *Clutch, incls *[]string,
+	libs *[]string) (map[string]string, error) {
+	eggList := p.GetEggs()
+	if eggList == nil {
+		return nil, nil
+	}
+
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Building egg dependencies for project %s\n", p.Name)
+
+	t := p.Target
+
+	// Append project variables to target variables, so that all package builds
+	// inherit from them
+	eggList = append(eggList, t.Dependencies...)
+	t.Capabilities = append(t.Capabilities, p.Capabilities...)
+	t.Cflags += " " + p.Cflags
+	t.Lflags += " " + p.Lflags
+	t.Aflags += " " + p.Aflags
+
+	deps := map[string]*DependencyRequirement{}
+	reqcaps := map[string]*DependencyRequirement{}
+	caps := map[string]*DependencyRequirement{}
+	capEggs := map[string]string{}
+
+	// inherit project capabilities, mark these capabilities as supported.
+	for _, cName := range t.Capabilities {
+		dr, err := NewDependencyRequirementParseString(cName)
+		if err != nil {
+			return nil, err
+		}
+
+		caps[dr.String()] = dr
+	}
+
+	for _, eggName := range eggList {
+		if eggName == "" {
+			continue
+		}
+
+		egg, err := clutch.ResolveEggName(eggName)
+		if err != nil {
+			return nil, err
+		}
+
+		if err := clutch.CheckEggDeps(egg, deps, reqcaps, caps, capEggs); err != nil {
+			return nil, err
+		}
+	}
+
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Reporting required capabilities for project %s\n", p.Name)
+	for dname, dep := range reqcaps {
+		StatusMessage(VERBOSITY_VERBOSE,
+			"	%s - %s\n", dname, dep.Name)
+	}
+	StatusMessage(VERBOSITY_VERBOSE,
+		"Reporting actual capabilities for project %s\n", p.Name)
+	for dname, dep := range caps {
+		StatusMessage(VERBOSITY_VERBOSE,
+			"	%s - %s ", dname, dep.Name)
+		if capEggs[dname] != "" {
+			StatusMessage(VERBOSITY_VERBOSE,
+				"- %s\n", capEggs[dname])
+		} else {
+			StatusMessage(VERBOSITY_VERBOSE, "\n")
+		}
+	}
+
+	// After processing all the dependencies, verify that the package's
+	// capability requirements are satisfied as well
+	if err := clutch.VerifyCaps(reqcaps, caps); err != nil {
+		return nil, err
+	}
+
+	// now go through and build everything
+	for _, eggName := range eggList {
+		if eggName == "" {
+			continue
+		}
+
+		egg, err := clutch.ResolveEggName(eggName)
+		if err != nil {
+			return nil, err
+		}
+
+		if err = clutch.Build(p.Target, eggName, *incls, libs); err != nil {
+			return nil, err
+		}
+
+		// Don't fail if package did not produce a library file; some packages
+		// are header-only.
+		if lib := clutch.GetEggLib(p.Target, egg); NodeExist(lib) {
+			*libs = append(*libs, lib)
+		}
+
+		*incls = append(*incls, egg.Includes...)
+	}
+
+	return capEggs, nil
+}
+
+// Build the BSP for this project.
+// The BSP is specified by the Target attached to the project.
+// clutch is an initialized egg mgr, containing all the packages
+// incls and libs are pointers to an array of includes and libraries, when buildBsp()
+// builds the BSP, it appends the include directories for the BSP, and the archive file
+// to these variables.
+func (p *Project) buildBsp(clutch *Clutch, incls *[]string,
+	libs *[]string, capEggs map[string]string) (string, error) {
+
+	StatusMessage(VERBOSITY_VERBOSE, "Building BSP %s for project %s\n",
+		p.Target.Bsp, p.Name)
+
+	if p.Target.Bsp == "" {
+		return "", NewNewtError("Must specify a BSP to build project")
+	}
+
+	return buildBsp(p.Target, clutch, incls, libs, capEggs)
+}
+
+// Build the project
+func (p *Project) Build() error {
+	clutch, err := NewClutch(p.Nest)
+	if err != nil {
+		return err
+	}
+
+	// Load the configuration for this target
+	if err := clutch.LoadConfigs(nil, false); err != nil {
+		return err
+	}
+
+	incls := []string{}
+	libs := []string{}
+	linkerScript := ""
+
+	// Collect target identities, libraries to include
+	err = p.collectDeps(clutch)
+	if err != nil {
+		return err
+	}
+
+	// If there is a BSP:
+	//     1. Calculate the include paths that it and its dependencies export.
+	//        This set of include paths is accessible during all subsequent
+	//        builds.
+	//     2. Build the BSP package.
+	if p.Target.Bsp != "" {
+		incls, err = BspIncludePaths(clutch, p.Target)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Build the project dependencies.
+	capEggs, err := p.buildDeps(clutch, &incls, &libs)
+	if err != nil {
+		return err
+	}
+
+	if p.Target.Bsp != "" {
+		linkerScript, err = p.buildBsp(clutch, &incls, &libs, capEggs)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Append project includes
+	projIncls := []string{
+		p.BasePath + "/include/",
+		p.BasePath + "/arch/" + p.Target.Arch + "/include/",
+	}
+
+	incls = append(incls, projIncls...)
+
+	c, err := NewCompiler(p.Target.GetCompiler(), p.Target.Cdef, p.Target.Name,
+		incls)
+	if err != nil {
+		return err
+	}
+
+	c.LinkerScript = linkerScript
+
+	// Add target C flags
+	c.Cflags = CreateCflags(clutch, c, p.Target, p.Cflags)
+
+	os.Chdir(p.BasePath + "/src/")
+	if err = c.Compile("*.c"); err != nil {
+		return err
+	}
+
+	if !NodeNotExist(p.BasePath + "/src/arch/" + p.Target.Arch + "/") {
+		os.Chdir(p.BasePath + "/src/arch/" + p.Target.Arch + "/")
+		if err = c.Compile("*.c"); err != nil {
+			return err
+		}
+	}
+
+	StatusMessage(VERBOSITY_DEFAULT, "Building project %s\n", p.Name)
+
+	// Create binaries in the project bin/ directory, under:
+	// bin/<arch>/
+	binDir := p.BinPath()
+	if NodeNotExist(binDir) {
+		os.MkdirAll(binDir, 0755)
+	}
+
+	options := map[string]bool{"mapFile": c.ldMapFile,
+		"listFile": true, "binFile": true}
+	err = c.CompileElf(binDir+p.Name, options, libs)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Initialize the project, and project definition
+func (p *Project) Init() error {
+	p.BasePath = p.Nest.BasePath + "/project/" + p.Name + "/"
+	if NodeNotExist(p.BasePath) {
+		return NewNewtError("Project directory does not exist")
+	}
+
+	if err := p.loadConfig(); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Return path to target binary
+func (p *Project) BinPath() string {
+	return p.BasePath + "/bin/" + p.Target.Name + "/"
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/e95057f4/newt/cli/remotenest.go
----------------------------------------------------------------------
diff --git a/newt/cli/remotenest.go b/newt/cli/remotenest.go
new file mode 100644
index 0000000..7bd51d7
--- /dev/null
+++ b/newt/cli/remotenest.go
@@ -0,0 +1,121 @@
+/*
+ Copyright 2015 Runtime Inc.
+ Licensed 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 cli
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+)
+
+type RemoteNest struct {
+	// Nestsitory associated with the Eggs
+	Nest *Nest
+
+	Clutch *Clutch
+
+	Name string
+
+	RemoteLoc string
+
+	LocalLoc string
+}
+
+// Allocate a new  structure, and initialize it.
+func NewRemoteNest(clutch *Clutch, branch string) (*RemoteNest, error) {
+	remoteNest := &RemoteNest{
+		Name : clutch.Name,
+		RemoteLoc: clutch.RemoteUrl,
+		LocalLoc: "",
+	}
+
+	err := remoteNest.Download(branch)
+	if err != nil {
+		return nil, err
+	}
+	return remoteNest, nil
+}
+
+// Download it
+func (remoteNest *RemoteNest) Download(branch string) error {
+	dl, err := NewDownloader()
+	if err != nil {
+		return err
+	}
+
+	StatusMessage(VERBOSITY_DEFAULT, "Downloading %s from %s/"+
+		"%s...", remoteNest.Name, remoteNest.RemoteLoc, branch)
+
+	dir, err := dl.GetRepo(remoteNest.RemoteLoc, branch)
+	if err != nil {
+		return err
+	}
+
+	StatusMessage(VERBOSITY_DEFAULT, OK_STRING)
+
+	remoteNest.LocalLoc = dir
+
+	nest, err := NewNestWithDir(dir)
+	if err != nil {
+		return err
+	}
+	remoteNest.Nest = nest
+
+	clutch, err := NewClutch(nest)
+	if err != nil {
+		return err
+	}
+
+	err = clutch.LoadConfigs(nil, false)
+	if err != nil {
+		return err
+	}
+	remoteNest.Clutch = clutch
+
+	return nil
+}
+
+func (remoteNest *RemoteNest) ResolveEggName(eggName string) (*Egg, error) {
+	if remoteNest.Clutch == nil {
+		return nil, NewNewtError(fmt.Sprintf("RemoteNest %s not downloaded yet!",
+					remoteNest.Name))
+	}
+	return remoteNest.Clutch.ResolveEggName(eggName)
+}
+
+func (remoteNest *RemoteNest) fetchEgg(eggName string, tgtBase string) error {
+	egg, err := remoteNest.ResolveEggName(eggName)
+	if err != nil {
+		return err
+	}
+
+	StatusMessage(VERBOSITY_DEFAULT, "Installing %s\n", egg.FullName)
+
+	srcDir := filepath.Join(remoteNest.LocalLoc, egg.FullName)
+	tgtDir := filepath.Join(tgtBase, egg.FullName)
+
+	err = CopyDir(srcDir, tgtDir)
+	return err
+}
+
+// Remove local copy
+func (remoteNest *RemoteNest) Remove() error {
+	if remoteNest.LocalLoc != "" {
+		err := os.RemoveAll(remoteNest.LocalLoc)
+		return err
+	}
+	return nil
+}