This is an automated email from the ASF dual-hosted git repository. ccollins pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 484bbb4729ea39ff616f0758358d6fe833d7fb50 Author: Christopher Collins <ccoll...@apache.org> AuthorDate: Thu Sep 27 15:18:51 2018 -0700 Genericize sysinit functionality Create a `stage` package. This package emits C code consisting of a sequence of function calls. --- newt/stage/stage.go | 204 ++++++++++++++++++++++++++++++++++++++++++++++++ newt/sysinit/sysinit.go | 163 +++++--------------------------------- util/util.go | 17 ++++ 3 files changed, 240 insertions(+), 144 deletions(-) diff --git a/newt/stage/stage.go b/newt/stage/stage.go new file mode 100644 index 0000000..ee2dead --- /dev/null +++ b/newt/stage/stage.go @@ -0,0 +1,204 @@ +/** + * 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. + */ + +// stage - utility for generating C code consisting of a sequence of function +// calls ordered by stage number. +// +// This package is used by sysinit and sysdown. + +package stage + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + log "github.com/Sirupsen/logrus" + + "mynewt.apache.org/newt/newt/pkg" + "mynewt.apache.org/newt/util" +) + +type StageFunc struct { + Stage int + Name string + ReturnType string + ArgList string + Pkg *pkg.LocalPackage +} + +type stageFuncSorter struct { + // Used in logging; either "sysinit" or "sysdown". + funcType string + // Array of functions to be sorted. + fns []StageFunc +} + +func (s stageFuncSorter) Len() int { + return len(s.fns) +} + +func (s stageFuncSorter) Swap(i, j int) { + s.fns[i], s.fns[j] = s.fns[j], s.fns[i] +} + +func (s stageFuncSorter) Less(i, j int) bool { + a := s.fns[i] + b := s.fns[j] + + // 1: Sort by stage number. + if a.Stage < b.Stage { + return true + } else if b.Stage < a.Stage { + return false + } + + // 2: Sort by function name. + switch strings.Compare(a.Name, b.Name) { + case -1: + return true + case 1: + return false + } + + // Same stage and function name? + log.Warnf("Warning: Identical %s functions detected: %s", + s.funcType, a.Name) + + return true +} + +// SortStageFuncs performs an in-place sort of the provided StageFunc slice. +func SortStageFuncs(unsorted []StageFunc, funcType string) { + s := stageFuncSorter{ + funcType: funcType, + fns: unsorted, + } + + sort.Sort(s) +} + +func (f *StageFunc) ReturnTypeString() string { + if f.ReturnType == "" { + return "void" + } else { + return f.ReturnType + } +} + +func (f *StageFunc) ArgListString() string { + if f.ArgList == "" { + return "void" + } else { + return f.ArgList + } +} + +// WriteCalls emits C code: a list of function prototypes corresponding to the +// provided slice of stage functions. +func WritePrototypes(sortedFns []StageFunc, w io.Writer) { + for _, f := range sortedFns { + fmt.Fprintf(w, "%s %s(%s);\n", + f.ReturnTypeString(), f.Name, f.ArgListString()) + } +} + +// WriteCalls emits C code: a sequence of function calls corresponding to the +// provided slice of stage functions. +func WriteCalls(sortedFuncs []StageFunc, argList string, w io.Writer) { + prevStage := -1 + dupCount := 0 + + for i, f := range sortedFuncs { + if f.Stage != prevStage { + prevStage = f.Stage + dupCount = 0 + + if i != 0 { + fmt.Fprintf(w, "\n") + } + fmt.Fprintf(w, " /*** Stage %d */\n", f.Stage) + } else { + dupCount += 1 + } + + fmt.Fprintf(w, " /* %d.%d: %s (%s) */\n", + f.Stage, dupCount, f.Name, f.Pkg.Name()) + fmt.Fprintf(w, " %s(%s);\n", f.Name, argList) + } +} + +// WriteArr emits C code: an array body of function pointers represented by the +// supplied slice. The caller must 1) write the array declaration before +// calling this function, and 2) write "};" afterwards. +func WriteArr(sortedFuncs []StageFunc, w io.Writer) { + prevStage := -1 + dupCount := 0 + + for i, f := range sortedFuncs { + if f.Stage != prevStage { + prevStage = f.Stage + dupCount = 0 + + if i != 0 { + fmt.Fprintf(w, "\n") + } + fmt.Fprintf(w, " /*** Stage %d */\n", f.Stage) + } else { + dupCount += 1 + } + + fmt.Fprintf(w, " /* %d.%d: %s (%s) */\n", + f.Stage, dupCount, f.Name, f.Pkg.Name()) + fmt.Fprintf(w, " %s,\n", f.Name) + } + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, " /*** Array terminator. */\n") + fmt.Fprintf(w, " 0\n") +} + +// EnsureWritten writes the specified file if its contents differ from those of +// the supplied byte slice. +func EnsureWritten(path string, contents []byte) error { + unchanged, err := util.FileContains(contents, path) + if err != nil { + return err + } + + if unchanged { + log.Debugf("file unchanged; not writing src file (%s).", path) + return nil + } + + log.Debugf("file changed; writing src file (%s).", path) + + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return util.NewNewtError(err.Error()) + } + + if err := ioutil.WriteFile(path, contents, 0644); err != nil { + return util.NewNewtError(err.Error()) + } + + return nil +} diff --git a/newt/sysinit/sysinit.go b/newt/sysinit/sysinit.go index 4071a09..98e7fc6 100644 --- a/newt/sysinit/sysinit.go +++ b/newt/sysinit/sysinit.go @@ -23,125 +23,33 @@ import ( "bytes" "fmt" "io" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" - - log "github.com/Sirupsen/logrus" "mynewt.apache.org/newt/newt/newtutil" "mynewt.apache.org/newt/newt/pkg" - "mynewt.apache.org/newt/util" + "mynewt.apache.org/newt/newt/stage" ) -type initFunc struct { - stage int - name string - pkg *pkg.LocalPackage -} - -func buildStageMap(pkgs []*pkg.LocalPackage) map[int][]*initFunc { - sm := map[int][]*initFunc{} - - for _, p := range pkgs { - for name, stage := range p.Init() { - initFunc := &initFunc{ - stage: stage, - name: name, - pkg: p, - } - sm[stage] = append(sm[stage], initFunc) - } - } - - return sm -} - -type initFuncSorter struct { - fns []*initFunc -} - -func (s initFuncSorter) Len() int { - return len(s.fns) -} -func (s initFuncSorter) Swap(i, j int) { - s.fns[i], s.fns[j] = s.fns[j], s.fns[i] -} -func (s initFuncSorter) Less(i, j int) bool { - a := s.fns[i] - b := s.fns[j] - - // 1: Sort by stage number. - if a.stage < b.stage { - return true - } else if b.stage < a.stage { - return false - } - - // 2: Sort by function name. - switch strings.Compare(a.name, b.name) { - case -1: - return true - case 1: - return false - } - - // Same stage and function name? - log.Warnf("Warning: Identical sysinit functions detected: %s", a.name) - return true -} - -func sortedInitFuncs(pkgs []*pkg.LocalPackage) []*initFunc { - sorter := initFuncSorter{ - fns: make([]*initFunc, 0, len(pkgs)), - } - +func initFuncs(pkgs []*pkg.LocalPackage) []stage.StageFunc { + fns := make([]stage.StageFunc, 0, len(pkgs)) for _, p := range pkgs { initMap := p.Init() - for name, stage := range initMap { - fn := &initFunc{ - name: name, - stage: stage, - pkg: p, + for name, stageNum := range initMap { + fn := stage.StageFunc{ + Name: name, + Stage: stageNum, + Pkg: p, } - sorter.fns = append(sorter.fns, fn) + fns = append(fns, fn) } } - sort.Sort(sorter) - return sorter.fns -} - -func writePrototypes(pkgs []*pkg.LocalPackage, w io.Writer) { - sortedFns := sortedInitFuncs(pkgs) - for _, f := range sortedFns { - fmt.Fprintf(w, "void %s(void);\n", f.name) - } + return fns } -func writeCalls(sortedInitFuncs []*initFunc, w io.Writer) { - prevStage := -1 - dupCount := 0 - - for i, f := range sortedInitFuncs { - if f.stage != prevStage { - prevStage = f.stage - dupCount = 0 - - if i != 0 { - fmt.Fprintf(w, "\n") - } - fmt.Fprintf(w, " /*** Stage %d */\n", f.stage) - } else { - dupCount += 1 - } - - fmt.Fprintf(w, " /* %d.%d: %s (%s) */\n", - f.stage, dupCount, f.name, f.pkg.Name()) - fmt.Fprintf(w, " %s();\n", f.name) - } +func sortedInitFuncs(pkgs []*pkg.LocalPackage) []stage.StageFunc { + fns := initFuncs(pkgs) + stage.SortStageFuncs(fns, "sysinit") + return fns } func write(pkgs []*pkg.LocalPackage, isLoader bool, @@ -155,7 +63,9 @@ func write(pkgs []*pkg.LocalPackage, isLoader bool, fmt.Fprintf(w, "#if !SPLIT_LOADER\n\n") } - writePrototypes(pkgs, w) + fns := sortedInitFuncs(pkgs) + + stage.WritePrototypes(fns, w) var fnName string if isLoader { @@ -167,27 +77,12 @@ func write(pkgs []*pkg.LocalPackage, isLoader bool, fmt.Fprintf(w, "\n") fmt.Fprintf(w, "void\n%s(void)\n{\n", fnName) - writeCalls(sortedInitFuncs(pkgs), w) + stage.WriteCalls(fns, "", w) fmt.Fprintf(w, "}\n\n") fmt.Fprintf(w, "#endif\n") } -func writeRequired(contents []byte, path string) (bool, error) { - oldSrc, err := ioutil.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - // File doesn't exist; write required. - return true, nil - } - - return true, util.NewNewtError(err.Error()) - } - - rc := bytes.Compare(oldSrc, contents) - return rc != 0, nil -} - func EnsureWritten(pkgs []*pkg.LocalPackage, srcDir string, targetName string, isLoader bool) error { @@ -201,25 +96,5 @@ func EnsureWritten(pkgs []*pkg.LocalPackage, srcDir string, targetName string, path = fmt.Sprintf("%s/%s-sysinit-app.c", srcDir, targetName) } - writeReqd, err := writeRequired(buf.Bytes(), path) - if err != nil { - return err - } - - if !writeReqd { - log.Debugf("sysinit unchanged; not writing src file (%s).", path) - return nil - } - - log.Debugf("sysinit changed; writing src file (%s).", path) - - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return util.NewNewtError(err.Error()) - } - - if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil { - return util.NewNewtError(err.Error()) - } - - return nil + return stage.EnsureWritten(path, buf.Bytes()) } diff --git a/util/util.go b/util/util.go index e848642..d3abfc3 100644 --- a/util/util.go +++ b/util/util.go @@ -696,3 +696,20 @@ func StringMapStringToItfMapItf( return imi } + +// FileContains indicates whether the specified file's contents are equal to +// the provided byte slice. +func FileContains(contents []byte, path string) (bool, error) { + oldSrc, err := ioutil.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + // File doesn't exist; contents aren't equal. + return false, nil + } + + return false, NewNewtError(err.Error()) + } + + rc := bytes.Compare(oldSrc, contents) + return rc == 0, nil +}