newt/stage/stage.go (189 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. */ // 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/newt/syscfg" "mynewt.apache.org/newt/newt/val" "mynewt.apache.org/newt/util" ) type StageFunc struct { Stage val.ValSetting Name string ReturnType string ArgList string Pkg *pkg.LocalPackage Deps []*StageFunc DepsI []*StageFunc Resolved bool Resolving bool } func NewStageFunc(name string, textVal string, p *pkg.LocalPackage, cfg *syscfg.Cfg) (StageFunc, error) { vs, err := val.ResolveValSetting(textVal, cfg) if err != nil { return StageFunc{}, err } // Ensure setting resolves to an integer. if _, err := vs.IntVal(); err != nil { return StageFunc{}, util.FmtNewtError("Invalid setting: \"%s: %s\"; "+ "value \"%s\" does not resolve to an integer", name, textVal, textVal) } sf := StageFunc{ Name: name, Stage: vs, Pkg: p, } return sf, nil } func NewStageFuncMultiDeps(name string, stageDeps []string, p *pkg.LocalPackage, cfg *syscfg.Cfg) (StageFunc, error) { sf := StageFunc{ Name: name, Pkg: p, Stage: val.ValSetting{}, } for _, stageDep := range stageDeps { s := strings.TrimPrefix(stageDep, "$before:") if s != stageDep { sf.Stage.Befores = append(sf.Stage.Befores, s) } else { s = strings.TrimPrefix(stageDep, "$after:") if s != stageDep { sf.Stage.Afters = append(sf.Stage.Afters, s) } else { return StageFunc{}, util.FmtNewtError("Invalid setting: \"%s: %s\"; "+ "value should specify a $before or $after dependency.", name, stageDep) } } } return sf, nil } func ValIsDep(textVal string) bool { return strings.HasPrefix(textVal, "$before:") || strings.HasPrefix(textVal, "$after:") } // SortStageFuncs performs an in-place sort of the provided StageFunc slice. func SortStageFuncs(sfs []StageFunc, entryType string) { sort.Slice(sfs, func(i int, j int) bool { a := sfs[i] b := sfs[j] inta, _ := a.Stage.IntVal() intb, _ := b.Stage.IntVal() // 1: Sort by stage number. if inta < intb { return true } else if intb < inta { return false } // 2: Sort by entry 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 entries detected: %s", entryType, a.Name) return true }) } 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 { intStage, _ := f.Stage.IntVal() noStage := len(f.Stage.Afters) > 0 || len(f.Stage.Befores) > 0 if intStage != prevStage { prevStage = intStage dupCount = 0 if noStage { fmt.Fprintf(w, "\n") } else { if i != 0 { fmt.Fprintf(w, "\n") } fmt.Fprintf(w, " /*** Stage %d */\n", intStage) } } else { dupCount += 1 } if noStage { for _, s := range f.Stage.Afters { fmt.Fprintf(w, " /* [$after:%s]: %s (%s) */\n", s, f.Name, f.Pkg.Name()) } for _, s := range f.Stage.Befores { fmt.Fprintf(w, " /* [$before:%s]: %s (%s) */\n", s, f.Name, f.Pkg.Name()) } } else { fmt.Fprintf(w, " /* %d.%d: %s (%s) */\n", intStage, 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 { intStage, _ := f.Stage.IntVal() if intStage != prevStage { prevStage = intStage dupCount = 0 if i != 0 { fmt.Fprintf(w, "\n") } fmt.Fprintf(w, " /*** Stage %d */\n", intStage) } else { dupCount += 1 } fmt.Fprintf(w, " /* %d.%d: %s (%s) */\n", intStage, 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 }