blob: dd270de59a6299bcf92e75f68731f4d2f31c85c3 [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"
"io"
"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, stageStr := range initMap {
sf, err := stage.NewStageFunc(name, stageStr, 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 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()
stage.SortStageFuncs(scfg.StageFuncs, "sysinit")
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())
}