blob: ee1caf3cf2429ffd413b6b08af2af0e232377d69 [file] [log] [blame]
/**
* 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())
}