MYNEWT-655 newt - Detect proj/newt ver incompat. Projects can specify newt version restrictions as follows (project.yml):
project.newt_compatibility: 1.1.0: error 1.0.0: good 0.9.99: warn 0.9.9: 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/b1ecb9f8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/tree/b1ecb9f8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/diff/b1ecb9f8 Branch: refs/heads/master Commit: b1ecb9f835549ee275c4e50c03a22b9c9fa7cc6d Parents: f264e67 Author: Christopher Collins <ccoll...@apache.org> Authored: Tue Mar 7 13:16:04 2017 -0800 Committer: Christopher Collins <ccoll...@apache.org> Committed: Tue Mar 7 13:16:04 2017 -0800 ---------------------------------------------------------------------- newt/compat/compat.go | 275 +++++++++++++++++++++++++++++++++++++++++ newt/project/project.go | 48 ++++++-- newt/repo/compat.go | 284 ------------------------------------------- newt/repo/repo.go | 20 +-- 4 files changed, 325 insertions(+), 302 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/b1ecb9f8/newt/compat/compat.go ---------------------------------------------------------------------- diff --git a/newt/compat/compat.go b/newt/compat/compat.go new file mode 100644 index 0000000..2e4355f --- /dev/null +++ b/newt/compat/compat.go @@ -0,0 +1,275 @@ +/** + * 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 compat + +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 +} + +// Sorted in ascending order by newt version number. +type NewtCompatTable []NewtCompatEntry + +type NewtCompatMap 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 v, c := range strMap { + entry, err := parseNcEntry(v, c) + if err != nil { + return tbl, err + } + + tbl = append(tbl, entry) + } + + sortEntries(tbl) + + return tbl, nil +} + +func ReadNcMap(v *viper.Viper) (NewtCompatMap, error) { + mp := NewtCompatMap{} + 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[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[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); i++ { + idx := len(tbl) - i - 1 + entry := &tbl[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) { + return []int{j, i} + } + + if j >= len(tbl) { + return []int{i, j} + } + + e1 := tbl[i] + e2 := tbl[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 { + 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))) + } + return ranges +} + +func (tbl NewtCompatTable) minMaxTgtVers(goodRange []int) ( + newtutil.Version, newtutil.Version, newtutil.Version) { + + minVer := tbl[goodRange[0]].minNewtVer + + var maxVer newtutil.Version + if goodRange[1] < len(tbl) { + maxVer = tbl[goodRange[1]].minNewtVer + } else { + maxVer = newtutil.Version{math.MaxInt64, math.MaxInt64, math.MaxInt64} + } + + targetVer := tbl[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[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, fmt.Sprintf("Please upgrade your project "+ + "or downgrade newt to %s", tgtVer.String()) + } + } + + 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/b1ecb9f8/newt/project/project.go ---------------------------------------------------------------------- diff --git a/newt/project/project.go b/newt/project/project.go index bf4b00e..6778c8b 100644 --- a/newt/project/project.go +++ b/newt/project/project.go @@ -29,6 +29,7 @@ import ( log "github.com/Sirupsen/logrus" + "mynewt.apache.org/newt/newt/compat" "mynewt.apache.org/newt/newt/downloader" "mynewt.apache.org/newt/newt/interfaces" "mynewt.apache.org/newt/newt/newtutil" @@ -100,6 +101,7 @@ func TryGetProject() (*Project, error) { } return globalProject, nil } + func GetProject() *Project { if _, err := TryGetProject(); err != nil { panic(err.Error()) @@ -418,19 +420,15 @@ func (proj *Project) loadRepo(rname string, v *viper.Viper) error { // 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()) - } + r.ReadDesc() rvers := proj.projState.GetInstalledVersion(rname) code, msg := r.CheckNewtCompatibility(rvers, newtutil.NewtVersion) switch code { - case repo.NEWT_COMPAT_GOOD: - case repo.NEWT_COMPAT_WARN: + case compat.NEWT_COMPAT_GOOD: + case compat.NEWT_COMPAT_WARN: util.StatusMessage(util.VERBOSITY_QUIET, "WARNING: %s.\n", msg) - case repo.NEWT_COMPAT_ERROR: + case compat.NEWT_COMPAT_ERROR: return util.NewNewtError(msg) } @@ -441,6 +439,36 @@ func (proj *Project) loadRepo(rname string, v *viper.Viper) error { return nil } +func (proj *Project) checkNewtVer() error { + compatSms := proj.v.GetStringMapString("project.newt_compatibility") + // If this project doesn't have a newt compatibility map, just assume there + // is no incompatibility. + if compatSms == nil { + return nil + } + + tbl, err := compat.ParseNcTable(compatSms) + if err != nil { + return util.FmtNewtError("Error reading project.yml: %s", err.Error()) + } + + code, msg := tbl.CheckNewtVer(newtutil.NewtVersion) + msg = fmt.Sprintf("This version of newt (%s) is incompatible with "+ + "your project; %s", newtutil.NewtVersion.String(), msg) + + switch code { + case compat.NEWT_COMPAT_GOOD: + return nil + case compat.NEWT_COMPAT_WARN: + util.StatusMessage(util.VERBOSITY_QUIET, "WARNING: %s.\n", msg) + return nil + case compat.NEWT_COMPAT_ERROR: + return util.NewNewtError(msg) + default: + return nil + } +} + func (proj *Project) loadConfig() error { v, err := util.ReadConfig(proj.BasePath, strings.TrimSuffix(PROJECT_FILE_NAME, ".yml")) @@ -496,6 +524,10 @@ func (proj *Project) loadConfig() error { r.AddIgnoreDir(dirName) } + if err := proj.checkNewtVer(); err != nil { + return err + } + return nil } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/b1ecb9f8/newt/repo/compat.go ---------------------------------------------------------------------- diff --git a/newt/repo/compat.go b/newt/repo/compat.go deleted file mode 100644 index cd9fc82..0000000 --- a/newt/repo/compat.go +++ /dev/null @@ -1,284 +0,0 @@ -/** - * 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/b1ecb9f8/newt/repo/repo.go ---------------------------------------------------------------------- diff --git a/newt/repo/repo.go b/newt/repo/repo.go index a534ea3..a2b98c9 100644 --- a/newt/repo/repo.go +++ b/newt/repo/repo.go @@ -29,6 +29,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/spf13/cast" + "mynewt.apache.org/newt/newt/compat" "mynewt.apache.org/newt/newt/downloader" "mynewt.apache.org/newt/newt/interfaces" "mynewt.apache.org/newt/newt/newtutil" @@ -52,7 +53,7 @@ type Repo struct { ignDirs []string updated bool local bool - ncMap *NewtCompatMap + ncMap compat.NewtCompatMap } type RepoDesc struct { @@ -551,7 +552,7 @@ func (r *Repo) ReadDesc() (*RepoDesc, []*Repo, error) { } // Read the newt version compatibility map. - r.ncMap, err = readNcMap(v) + r.ncMap, err = compat.ReadNcMap(v) if err != nil { return nil, nil, err } @@ -582,23 +583,24 @@ func (r *Repo) Init(repoName string, rversreq string, d downloader.Downloader) e } func (r *Repo) CheckNewtCompatibility(rvers *Version, nvers newtutil.Version) ( - NewtCompatCode, string) { + compat.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, "" + if len(r.ncMap) == 0 { + return compat.NEWT_COMPAT_GOOD, "" } rnuver := rvers.ToNuVersion() - tbl, ok := r.ncMap.verTableMap[rnuver] + tbl, ok := r.ncMap[rnuver] if !ok { // Unknown repo version. - return NEWT_COMPAT_WARN, "Repo version missing from compatibility map" + return compat.NEWT_COMPAT_WARN, + "Repo version missing from compatibility map" } code, text := tbl.CheckNewtVer(nvers) - if code == NEWT_COMPAT_GOOD { + if code == compat.NEWT_COMPAT_GOOD { return code, text } @@ -610,7 +612,6 @@ func (r *Repo) CheckNewtCompatibility(rvers *Version, nvers newtutil.Version) ( 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 { @@ -623,7 +624,6 @@ 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 {