newt/sysinit/sysinit.go (255 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 sysinit import ( "bytes" "fmt" "github.com/spf13/cast" "io" "mynewt.apache.org/newt/util" "sort" "strings" "mynewt.apache.org/newt/newt/newtutil" "mynewt.apache.org/newt/newt/pkg" "mynewt.apache.org/newt/newt/stage" "mynewt.apache.org/newt/newt/syscfg" ) type SysinitCfg struct { // Sorted in call order (stage-num,function-name). StageFuncs []stage.StageFunc // Strings describing errors encountered while parsing the sysinit config. InvalidSettings []string // Contains sets of entries with conflicting function names. // [function-name] => <slice-of-stages-with-function-name> Conflicts map[string][]stage.StageFunc } func (scfg *SysinitCfg) readOnePkg(lpkg *pkg.LocalPackage, cfg *syscfg.Cfg) { settings := cfg.AllSettingsForLpkg(lpkg) initMap := lpkg.InitFuncs(settings) for name, stageDef := range initMap { var sf stage.StageFunc var err error switch stageDef.(type) { default: stageStr := cast.ToString(stageDef) if stage.ValIsDep(stageStr) { stageDeps := strings.Split(stageStr, ",") sf, err = stage.NewStageFuncMultiDeps(name, stageDeps, lpkg, cfg) } else { sf, err = stage.NewStageFunc(name, stageStr, lpkg, cfg) } case []interface{}: var stageDeps []string for _, stageDepIf := range stageDef.([]interface{}) { stageDeps = append(stageDeps, cast.ToString(stageDepIf)) } sf, err = stage.NewStageFuncMultiDeps(name, stageDeps, lpkg, cfg) } if err != nil { scfg.InvalidSettings = append(scfg.InvalidSettings, err.Error()) } else { scfg.StageFuncs = append(scfg.StageFuncs, sf) } } } // Searches the sysinit configuration for entries with identical function // names. The sysinit configuration object is populated with the results. func (scfg *SysinitCfg) detectConflicts() { m := map[string][]stage.StageFunc{} for _, sf := range scfg.StageFuncs { m[sf.Name] = append(m[sf.Name], sf) } for name, sfs := range m { if len(sfs) > 1 { scfg.Conflicts[name] = sfs } } } func stageFuncResolve(sfRef *stage.StageFunc, stack *[]stage.StageFunc) error { if sfRef.Resolved { return nil } if sfRef.Resolving { return util.FmtNewtError("Circular dependency detected while resolving \"%s (%s)\".", sfRef.Name, sfRef.Pkg.FullName()) } sfRef.Resolving = true for _, sfDepRef := range sfRef.Deps { err := stageFuncResolve(sfDepRef, stack) if err != nil { return err } } sfRef.Resolving = false sfRef.Resolved = true *stack = append([]stage.StageFunc{*sfRef}, *stack...) return nil } func ResolveStageFuncsOrder(sfs []stage.StageFunc) ([]stage.StageFunc, error) { var nodes []*stage.StageFunc var nodesQ []*stage.StageFunc nodesByStage := make(map[int][]*stage.StageFunc) nodesByName := make(map[string]*stage.StageFunc) for idx, _ := range sfs { sfRef := &sfs[idx] nodesByName[sfRef.Name] = sfRef if len(sfRef.Stage.Befores) == 0 && len(sfRef.Stage.Afters) == 0 { stage, _ := sfRef.Stage.IntVal() nodesByStage[stage] = append(nodesByStage[stage], sfRef) } else { nodesQ = append(nodesQ, sfRef) } } var stages []int for stage := range nodesByStage { stages = append(stages, stage) } sort.Ints(stages) // Lexicographically sort nodes in each stage, then build the nodes stack. // This helps ensure that sysinit order is reproducable and deterministic for stageIndex, _ := range stages { lsfs := nodesByStage[stages[stageIndex]] sort.Slice(lsfs, func(i int, j int) bool { a := lsfs[i] b := lsfs[j] if strings.Compare(a.Name, b.Name) == -1 { return false } return true }) nodes = append(nodes, nodesByStage[stages[stageIndex]]...) } sort.Slice(nodesQ, func(i int, j int) bool { a := nodesQ[i] b := nodesQ[j] if strings.Compare(a.Name, b.Name) == -1 { return false } return true }) // Put nodes without stages first, so they are resolved and put to // stack first - we do not want them to precede all nodes with stages. // While technically correct, it's better not to start sysinit with // nodes that do not have any stage since this will likely happen // before os init packages. nodes = append(nodesQ, nodes...) // Add implicit dependencies for nodes with stages. We need to add // direct dependencies between each node of stage X to each node of // stage Y to make sure they can be resolved properly and reordered // if needed due to other dependencies. sfsPrev := nodesByStage[stages[0]] stages = stages[1:] for _, stage := range stages { sfsCurr := nodesByStage[stage] for _, sfsP := range sfsPrev { for _, sfsC := range sfsCurr { sfsP.Deps = append(sfsP.Deps, sfsC) // Keep separate list of implicit dependencies // This is only used for Graphviz output sfsP.DepsI = append(sfsP.DepsI, sfsC) } } sfsPrev = sfsCurr } // Now add other dependencies, i.e. $after and $before for _, sf := range nodesQ { for _, depStr := range sf.Stage.Afters { depSf := nodesByName[depStr] if depSf == nil { return []stage.StageFunc{}, util.FmtNewtError("Unknown depdendency (\"%s\") for \"%s (%s)\".", depStr, sf.Name, sf.Pkg.FullName()) } depSf.Deps = append(depSf.Deps, sf) } for _, depStr := range sf.Stage.Befores { depSf := nodesByName[depStr] if depSf == nil { return []stage.StageFunc{}, util.FmtNewtError("Unknown depdendency (\"%s\") for \"%s (%s)\".", depStr, sf.Name, sf.Pkg.FullName()) } sf.Deps = append(sf.Deps, depSf) } } // Now we can resolve order of functions by sorting dependency graph // topologically. This will also detect circular dependencies. sfs = []stage.StageFunc{} for _, sfRef := range nodes { err := stageFuncResolve(sfRef, &sfs) if err != nil { return []stage.StageFunc{}, err } } return sfs, nil } func Read(lpkgs []*pkg.LocalPackage, cfg *syscfg.Cfg) SysinitCfg { scfg := SysinitCfg{ Conflicts: map[string][]stage.StageFunc{}, } for _, lpkg := range lpkgs { scfg.readOnePkg(lpkg, cfg) } scfg.detectConflicts() // Don't try to resolve order if there are name conflicts since that // process depends on unique names for each function and could return // other confusing errors. if len(scfg.Conflicts) > 0 { return scfg } var err error scfg.StageFuncs, err = ResolveStageFuncsOrder(scfg.StageFuncs) if err != nil { scfg.InvalidSettings = append(scfg.InvalidSettings, err.Error()) } return scfg } func (scfg *SysinitCfg) filter(lpkgs []*pkg.LocalPackage) []stage.StageFunc { m := make(map[*pkg.LocalPackage]struct{}, len(lpkgs)) for _, lpkg := range lpkgs { m[lpkg] = struct{}{} } filtered := []stage.StageFunc{} for _, sf := range scfg.StageFuncs { if _, ok := m[sf.Pkg]; ok { filtered = append(filtered, sf) } } return filtered } // If any errors were encountered while parsing sysinit definitions, this // function returns a string indicating the errors. If no errors were // encountered, "" is returned. func (scfg *SysinitCfg) ErrorText() string { str := "" if len(scfg.InvalidSettings) > 0 { str += "Invalid sysinit definitions detected:" for _, e := range scfg.InvalidSettings { str += "\n " + e } } if len(scfg.Conflicts) > 0 { str += "Sysinit function name conflicts detected:\n" for name, sfs := range scfg.Conflicts { for _, sf := range sfs { str += fmt.Sprintf(" Function=%s Package=%s\n", name, sf.Pkg.FullName()) } } str += "\nResolve the problem by assigning unique function names " + "to each entry." } return str } func (scfg *SysinitCfg) write(lpkgs []*pkg.LocalPackage, isLoader bool, w io.Writer) error { var sfs []stage.StageFunc if lpkgs == nil { sfs = scfg.StageFuncs } else { sfs = scfg.filter(lpkgs) } fmt.Fprintf(w, newtutil.GeneratedPreamble()) if isLoader { fmt.Fprintf(w, "#if SPLIT_LOADER\n\n") } else { fmt.Fprintf(w, "#if !SPLIT_LOADER\n\n") } stage.WritePrototypes(sfs, w) var fnName string if isLoader { fnName = "sysinit_loader" } else { fnName = "sysinit_app" } fmt.Fprintf(w, "\n") fmt.Fprintf(w, "void\n%s(void)\n{\n", fnName) stage.WriteCalls(sfs, "", w) fmt.Fprintf(w, "}\n\n") fmt.Fprintf(w, "#endif\n") return nil } func (scfg *SysinitCfg) EnsureWritten(lpkgs []*pkg.LocalPackage, srcDir string, targetName string, isLoader bool) error { buf := bytes.Buffer{} if err := scfg.write(lpkgs, isLoader, &buf); err != nil { return err } var path string if isLoader { path = fmt.Sprintf("%s/%s-sysinit-loader.c", srcDir, targetName) } else { path = fmt.Sprintf("%s/%s-sysinit-app.c", srcDir, targetName) } return stage.EnsureWritten(path, buf.Bytes()) }