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