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 2017/03/07 20:22:28 UTC

incubator-mynewt-newt git commit: MYNEWT-655 newt - Detect repo/newt ver incompat.

Repository: incubator-mynewt-newt
Updated Branches:
  refs/heads/develop 1fb9697cc -> f264e6767


MYNEWT-655 newt - Detect repo/newt ver incompat.

This is done with a separate map "repo.newt_compatibility" in the repo's
repository.yml file.

Example:

repo.newt_compatibility:
    1.0.0:
        1.1.0: error
        1.0.0: good
        0.9.99: warn
        0.9.9: error

This says that this version 1.0.0 of this repo is compatible with newt
versions [1.0.0, 1.1.0). Newt versions in the range [0.9.99, 1.0.0)
elicit a warning message. All other newt versions generate an error.


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

Branch: refs/heads/develop
Commit: f264e6767fbfd26ff24f31b55e63fd8b189daa9f
Parents: 1fb9697
Author: Christopher Collins <cc...@apache.org>
Authored: Tue Mar 7 12:11:12 2017 -0800
Committer: Christopher Collins <cc...@apache.org>
Committed: Tue Mar 7 12:22:06 2017 -0800

----------------------------------------------------------------------
 newt/newtutil/newtutil.go |  71 ++++++++---
 newt/project/project.go   |  18 +++
 newt/repo/compat.go       | 284 +++++++++++++++++++++++++++++++++++++++++
 newt/repo/repo.go         | 114 +++++------------
 newt/repo/version.go      |   9 ++
 5 files changed, 397 insertions(+), 99 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/f264e676/newt/newtutil/newtutil.go
----------------------------------------------------------------------
diff --git a/newt/newtutil/newtutil.go b/newt/newtutil/newtutil.go
index 48ef4a0..98307ec 100644
--- a/newt/newtutil/newtutil.go
+++ b/newt/newtutil/newtutil.go
@@ -34,6 +34,7 @@ import (
 	"mynewt.apache.org/newt/viper"
 )
 
+var NewtVersion Version = Version{1, 0, 0}
 var NewtVersionStr string = "Apache Newt (incubating) version: 1.0.0-dev"
 var NewtBlinkyTag string = "develop"
 var NewtNumJobs int
@@ -45,27 +46,59 @@ const REPOS_FILENAME string = "repos.yml"
 const CORE_REPO_NAME string = "apache-mynewt-core"
 const ARDUINO_ZERO_REPO_NAME string = "mynewt_arduino_zero"
 
-type RepoCommitEntry struct {
-	Version     string
-	Hash        string
-	Description string
+type Version struct {
+	Major    int64
+	Minor    int64
+	Revision int64
 }
 
-// A warning is displayed if newt requires a newer version of a repo.
-var RepoMinCommits = map[string]*RepoCommitEntry{
-	// Newt no longer cd's to a source directory when it compiles its contents.
-	// Consequently, package include flags need to be relative to the project
-	// directory, not the package source directory.
-	CORE_REPO_NAME: &RepoCommitEntry{
-		Version:     "develop",
-		Hash:        "cd99344df197d5b9e372b93142184a39ec078f69",
-		Description: "Include paths now relative to project base.",
-	},
-	ARDUINO_ZERO_REPO_NAME: &RepoCommitEntry{
-		Version:     "develop",
-		Hash:        "a6348961fef56dbfe09a1b9418d3add3ad22eaf2",
-		Description: "Include paths now relative to project base.",
-	},
+func ParseVersion(s string) (Version, error) {
+	v := Version{}
+	parseErr := util.FmtNewtError("Invalid version string: %s", s)
+
+	parts := strings.Split(s, ".")
+	if len(parts) != 3 {
+		return v, parseErr
+	}
+
+	var err error
+
+	v.Major, err = strconv.ParseInt(parts[0], 10, 64)
+	if err != nil {
+		return v, parseErr
+	}
+
+	v.Minor, err = strconv.ParseInt(parts[1], 10, 64)
+	if err != nil {
+		return v, parseErr
+	}
+
+	v.Revision, err = strconv.ParseInt(parts[2], 10, 64)
+	if err != nil {
+		return v, parseErr
+	}
+
+	return v, nil
+}
+
+func (v *Version) String() string {
+	return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Revision)
+}
+
+func VerCmp(v1 Version, v2 Version) int64 {
+	if r := v1.Major - v2.Major; r != 0 {
+		return r
+	}
+
+	if r := v1.Minor - v2.Minor; r != 0 {
+		return r
+	}
+
+	if r := v1.Revision - v2.Revision; r != 0 {
+		return r
+	}
+
+	return 0
 }
 
 // Contains general newt settings read from $HOME/.newt

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/f264e676/newt/project/project.go
----------------------------------------------------------------------
diff --git a/newt/project/project.go b/newt/project/project.go
index cc68c30..bf4b00e 100644
--- a/newt/project/project.go
+++ b/newt/project/project.go
@@ -416,6 +416,24 @@ func (proj *Project) loadRepo(rname string, v *viper.Viper) error {
 
 	proj.localRepo.AddDependency(rd)
 
+	// Read the repo's descriptor file so that we have its newt version
+	// compatibility map.
+	_, _, err = r.ReadDesc()
+	if err != nil {
+		return util.FmtNewtError("Failed to read repo descriptor; %s",
+			err.Error())
+	}
+
+	rvers := proj.projState.GetInstalledVersion(rname)
+	code, msg := r.CheckNewtCompatibility(rvers, newtutil.NewtVersion)
+	switch code {
+	case repo.NEWT_COMPAT_GOOD:
+	case repo.NEWT_COMPAT_WARN:
+		util.StatusMessage(util.VERBOSITY_QUIET, "WARNING: %s.\n", msg)
+	case repo.NEWT_COMPAT_ERROR:
+		return util.NewNewtError(msg)
+	}
+
 	log.Debugf("Loaded repository %s (type: %s, user: %s, repo: %s)", rname,
 		repoVars["type"], repoVars["user"], repoVars["repo"])
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/f264e676/newt/repo/compat.go
----------------------------------------------------------------------
diff --git a/newt/repo/compat.go b/newt/repo/compat.go
new file mode 100644
index 0000000..cd9fc82
--- /dev/null
+++ b/newt/repo/compat.go
@@ -0,0 +1,284 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package repo
+
+import (
+	"fmt"
+	"math"
+	"sort"
+
+	"github.com/spf13/cast"
+
+	"mynewt.apache.org/newt/newt/newtutil"
+	"mynewt.apache.org/newt/util"
+	"mynewt.apache.org/newt/viper"
+)
+
+type NewtCompatCode int
+
+const (
+	NEWT_COMPAT_GOOD NewtCompatCode = iota
+	NEWT_COMPAT_WARN
+	NEWT_COMPAT_ERROR
+)
+
+var NewtCompatCodeNames = map[NewtCompatCode]string{
+	NEWT_COMPAT_GOOD:  "good",
+	NEWT_COMPAT_WARN:  "warn",
+	NEWT_COMPAT_ERROR: "error",
+}
+
+type NewtCompatEntry struct {
+	code       NewtCompatCode
+	minNewtVer newtutil.Version
+}
+
+type NewtCompatTable struct {
+	// Sorted in ascending order by newt version number.
+	entries []NewtCompatEntry
+}
+
+type NewtCompatMap struct {
+	verTableMap map[newtutil.Version]NewtCompatTable
+}
+
+func newNewtCompatMap() *NewtCompatMap {
+	return &NewtCompatMap{
+		verTableMap: map[newtutil.Version]NewtCompatTable{},
+	}
+}
+
+func newtCompatCodeToString(code NewtCompatCode) string {
+	return NewtCompatCodeNames[code]
+}
+
+func newtCompatCodeFromString(codeStr string) (NewtCompatCode, error) {
+	for c, s := range NewtCompatCodeNames {
+		if codeStr == s {
+			return c, nil
+		}
+	}
+
+	return NewtCompatCode(0),
+		util.FmtNewtError("Invalid newt compatibility code: %s", codeStr)
+}
+
+func parseNcEntry(verStr string, codeStr string) (NewtCompatEntry, error) {
+	entry := NewtCompatEntry{}
+	var err error
+
+	entry.minNewtVer, err = newtutil.ParseVersion(verStr)
+	if err != nil {
+		return entry, err
+	}
+
+	entry.code, err = newtCompatCodeFromString(codeStr)
+	if err != nil {
+		return entry, err
+	}
+
+	return entry, nil
+}
+
+func parseNcTable(strMap map[string]string) (NewtCompatTable, error) {
+	tbl := NewtCompatTable{}
+
+	for c, v := range strMap {
+		entry, err := parseNcEntry(c, v)
+		if err != nil {
+			return tbl, err
+		}
+
+		tbl.entries = append(tbl.entries, entry)
+	}
+
+	sortEntries(tbl.entries)
+
+	return tbl, nil
+}
+
+func readNcMap(v *viper.Viper) (*NewtCompatMap, error) {
+	mp := newNewtCompatMap()
+	ncMap := v.GetStringMap("repo.newt_compatibility")
+
+	for k, v := range ncMap {
+		repoVer, err := newtutil.ParseVersion(k)
+		if err != nil {
+			return nil, util.FmtNewtError("Newt compatibility table contains " +
+				"invalid repo version \"%s\"")
+		}
+
+		if _, ok := mp.verTableMap[repoVer]; ok {
+			return nil, util.FmtNewtError("Newt compatibility table contains "+
+				"duplicate version specifier: %s", repoVer.String())
+		}
+
+		strMap := cast.ToStringMapString(v)
+		tbl, err := parseNcTable(strMap)
+		if err != nil {
+			return nil, err
+		}
+
+		mp.verTableMap[repoVer] = tbl
+	}
+
+	return mp, nil
+}
+
+func (tbl *NewtCompatTable) matchIdx(newtVer newtutil.Version) int {
+	// Iterate the table backwards.  The first entry whose version is less than
+	// or equal to the specified version is the match.
+	for i := 0; i < len(tbl.entries); i++ {
+		idx := len(tbl.entries) - i - 1
+		entry := &tbl.entries[idx]
+		cmp := newtutil.VerCmp(entry.minNewtVer, newtVer)
+		if cmp <= 0 {
+			return idx
+		}
+	}
+
+	return -1
+}
+
+func (tbl *NewtCompatTable) newIdxRange(i int, j int) []int {
+	if i >= len(tbl.entries) {
+		return []int{j, i}
+	}
+
+	if j >= len(tbl.entries) {
+		return []int{i, j}
+	}
+
+	e1 := tbl.entries[i]
+	e2 := tbl.entries[j]
+
+	if newtutil.VerCmp(e1.minNewtVer, e2.minNewtVer) < 0 {
+		return []int{i, j}
+	} else {
+		return []int{j, i}
+	}
+}
+
+func (tbl *NewtCompatTable) idxRangesWithCode(c NewtCompatCode) [][]int {
+	ranges := [][]int{}
+
+	curi := -1
+	for i, e := range tbl.entries {
+		if curi == -1 {
+			if e.code == c {
+				curi = i
+			}
+		} else {
+			if e.code != c {
+				ranges = append(ranges, tbl.newIdxRange(curi, i))
+				curi = -1
+			}
+		}
+	}
+
+	if curi != -1 {
+		ranges = append(ranges, tbl.newIdxRange(curi, len(tbl.entries)))
+	}
+	return ranges
+}
+
+func (tbl *NewtCompatTable) minMaxTgtVers(goodRange []int) (
+	newtutil.Version, newtutil.Version, newtutil.Version) {
+
+	minVer := tbl.entries[goodRange[0]].minNewtVer
+
+	var maxVer newtutil.Version
+	if goodRange[1] < len(tbl.entries) {
+		maxVer = tbl.entries[goodRange[1]].minNewtVer
+	} else {
+		maxVer = newtutil.Version{math.MaxInt64, math.MaxInt64, math.MaxInt64}
+	}
+
+	targetVer := tbl.entries[goodRange[1]-1].minNewtVer
+
+	return minVer, maxVer, targetVer
+}
+
+// @return NewtCompatCode       The severity of the newt incompatibility
+//         string               The warning or error message to display in case
+//                                  of incompatibility.
+func (tbl *NewtCompatTable) CheckNewtVer(
+	newtVer newtutil.Version) (NewtCompatCode, string) {
+
+	var code NewtCompatCode
+	idx := tbl.matchIdx(newtVer)
+	if idx == -1 {
+		// This version of newt is older than every entry in the table.
+		code = NEWT_COMPAT_ERROR
+	} else {
+		code = tbl.entries[idx].code
+		if code == NEWT_COMPAT_GOOD {
+			return NEWT_COMPAT_GOOD, ""
+		}
+	}
+
+	goodRanges := tbl.idxRangesWithCode(NEWT_COMPAT_GOOD)
+	for i := 0; i < len(goodRanges); i++ {
+		minVer, maxVer, tgtVer := tbl.minMaxTgtVers(goodRanges[i])
+
+		if newtutil.VerCmp(newtVer, minVer) < 0 {
+			return code, fmt.Sprintf("Please upgrade your newt tool to "+
+				"version %s", tgtVer.String())
+		}
+
+		if newtutil.VerCmp(newtVer, maxVer) >= 0 {
+			return code, "Please upgrade your repos with \"newt upgrade\""
+		}
+	}
+
+	return code, ""
+}
+
+type entrySorter struct {
+	entries []NewtCompatEntry
+}
+
+func (s entrySorter) Len() int {
+	return len(s.entries)
+}
+func (s entrySorter) Swap(i, j int) {
+	s.entries[i], s.entries[j] = s.entries[j], s.entries[i]
+}
+func (s entrySorter) Less(i, j int) bool {
+	e1 := s.entries[i]
+	e2 := s.entries[j]
+
+	cmp := newtutil.VerCmp(e1.minNewtVer, e2.minNewtVer)
+	if cmp < 0 {
+		return true
+	} else if cmp > 0 {
+		return false
+	}
+
+	return false
+}
+
+func sortEntries(entries []NewtCompatEntry) {
+	sorter := entrySorter{
+		entries: entries,
+	}
+
+	sort.Sort(sorter)
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/f264e676/newt/repo/repo.go
----------------------------------------------------------------------
diff --git a/newt/repo/repo.go b/newt/repo/repo.go
index a2331f7..a534ea3 100644
--- a/newt/repo/repo.go
+++ b/newt/repo/repo.go
@@ -52,10 +52,7 @@ type Repo struct {
 	ignDirs    []string
 	updated    bool
 	local      bool
-
-	// The minimim git commit the repo must have to interoperate with this
-	// version of newt.
-	minCommit *newtutil.RepoCommitEntry
+	ncMap      *NewtCompatMap
 }
 
 type RepoDesc struct {
@@ -475,8 +472,8 @@ func (r *Repo) DownloadDesc() error {
 	}
 
 	dl.SetBranch("master")
-	if err := dl.FetchFile("repository.yml",
-		cpath+"/"+"repository.yml"); err != nil {
+	if err := dl.FetchFile(REPO_FILE_NAME,
+		cpath+"/"+REPO_FILE_NAME); err != nil {
 		util.StatusMessage(util.VERBOSITY_VERBOSE, " failed\n")
 		return err
 	}
@@ -553,60 +550,13 @@ func (r *Repo) ReadDesc() (*RepoDesc, []*Repo, error) {
 		return nil, nil, err
 	}
 
-	return rdesc, repos, nil
-}
-
-// Checks if the specified repo is compatible with this version of newt.  This
-// function only verifies that the repo is new enough; it doesn't check that
-// newt is new enough.
-func (r *Repo) HasMinCommit() (bool, error) {
-	if r.minCommit == nil {
-		return true, nil
-	}
-
-	// Change back to the initial directory when this function returns.
-	cwd, err := os.Getwd()
-	if err != nil {
-		return false, util.ChildNewtError(err)
-	}
-	defer os.Chdir(cwd)
-
-	if err := os.Chdir(r.localPath); err != nil {
-		return false, util.ChildNewtError(err)
-	}
-
-	cmd := []string{
-		"git",
-		"merge-base",
-		r.minCommit.Hash,
-		"HEAD",
-	}
-	mergeBase, err := util.ShellCommand(cmd, nil)
-	if err != nil {
-		if strings.Contains(err.Error(), "Not a valid commit name") {
-			return false, nil
-		} else {
-			return false, util.ChildNewtError(err)
-		}
-	}
-	if len(mergeBase) == 0 {
-		// No output means the commit does not exist in the current branch.
-		return false, nil
-	}
-
-	cmd = []string{
-		"git",
-		"rev-parse",
-		"--verify",
-		r.minCommit.Hash,
-	}
-	revParse, err := util.ShellCommand(cmd, nil)
+	// Read the newt version compatibility map.
+	r.ncMap, err = readNcMap(v)
 	if err != nil {
-		return false, util.ChildNewtError(err)
+		return nil, nil, err
 	}
 
-	// The commit exists in HEAD if the two commands produced identical output.
-	return string(mergeBase) == string(revParse), nil
+	return rdesc, repos, nil
 }
 
 func (r *Repo) Init(repoName string, rversreq string, d downloader.Downloader) error {
@@ -626,38 +576,41 @@ func (r *Repo) Init(repoName string, rversreq string, d downloader.Downloader) e
 		r.localPath = filepath.ToSlash(filepath.Clean(path))
 	} else {
 		r.localPath = filepath.ToSlash(filepath.Clean(path + "/" + REPOS_DIR + "/" + r.name))
-		r.minCommit = newtutil.RepoMinCommits[repoName]
+	}
 
-		upToDate, err := r.HasMinCommit()
-		if err != nil {
-			// If there is an error checking the repo's commit log, just abort
-			// the check.  An error could have many possible causes: repo not
-			// installed, network issue, etc.  In none of these cases does it
-			// makes sense to warn about an out of date repo.  If the issue is
-			// a real one, it will be handled when it prevents forward
-			// progress.
-			return nil
-		}
+	return nil
+}
 
-		if !upToDate {
-			util.ErrorMessage(util.VERBOSITY_QUIET,
-				"Warning: repo \"%s\" is out of date for this version of "+
-					"newt.  Please upgrade the repo to meet these "+
-					"requirements:\n"+
-					"    * Version: %s\n"+
-					"    * Commit: %s\n"+
-					"    * Change: %s\n",
-				r.name, r.minCommit.Version, r.minCommit.Hash,
-				r.minCommit.Description)
-		}
+func (r *Repo) CheckNewtCompatibility(rvers *Version, nvers newtutil.Version) (
+	NewtCompatCode, string) {
+
+	// If this repo doesn't have a newt compatibility map, just assume there is
+	// no incompatibility.
+	if len(r.ncMap.verTableMap) == 0 {
+		return NEWT_COMPAT_GOOD, ""
 	}
 
-	return nil
+	rnuver := rvers.ToNuVersion()
+	tbl, ok := r.ncMap.verTableMap[rnuver]
+	if !ok {
+		// Unknown repo version.
+		return NEWT_COMPAT_WARN, "Repo version missing from compatibility map"
+	}
+
+	code, text := tbl.CheckNewtVer(nvers)
+	if code == NEWT_COMPAT_GOOD {
+		return code, text
+	}
+
+	return code, fmt.Sprintf("This version of newt (%s) is incompatible with "+
+		"your version of the %s repo (%s); %s",
+		nvers.String(), r.name, rnuver.String(), text)
 }
 
 func NewRepo(repoName string, rversreq string, d downloader.Downloader) (*Repo, error) {
 	r := &Repo{
 		local: false,
+		ncMap: newNewtCompatMap(),
 	}
 
 	if err := r.Init(repoName, rversreq, d); err != nil {
@@ -670,6 +623,7 @@ func NewRepo(repoName string, rversreq string, d downloader.Downloader) (*Repo,
 func NewLocalRepo(repoName string) (*Repo, error) {
 	r := &Repo{
 		local: true,
+		ncMap: newNewtCompatMap(),
 	}
 
 	if err := r.Init(repoName, "", nil); err != nil {

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/f264e676/newt/repo/version.go
----------------------------------------------------------------------
diff --git a/newt/repo/version.go b/newt/repo/version.go
index ce81ef2..bbf5b56 100644
--- a/newt/repo/version.go
+++ b/newt/repo/version.go
@@ -26,6 +26,7 @@ import (
 	"strings"
 
 	"mynewt.apache.org/newt/newt/interfaces"
+	"mynewt.apache.org/newt/newt/newtutil"
 	"mynewt.apache.org/newt/util"
 )
 
@@ -137,6 +138,14 @@ func (vers *Version) String() string {
 	return fmt.Sprintf(VERSION_FORMAT, vers.Major(), vers.Minor(), vers.Revision(), vers.Stability())
 }
 
+func (vers *Version) ToNuVersion() newtutil.Version {
+	return newtutil.Version{
+		Major:    vers.major,
+		Minor:    vers.minor,
+		Revision: vers.revision,
+	}
+}
+
 func LoadVersion(versStr string) (*Version, error) {
 	var err error