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 2018/10/23 17:47:58 UTC

[mynewt-newt] 01/02: Genericize sysinit functionality

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 <cc...@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
+}