newt/compat/compat.go (196 lines of code) (raw):

/** * 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/newt/ycfg" "mynewt.apache.org/newt/util" ) 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(yc ycfg.YCfg) (NewtCompatMap, error) { mp := NewtCompatMap{} ncMap, _ := yc.GetValStringMap("repo.newt_compatibility", nil) 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) }