blob: b240edf07492d3df1ba642470898f71f03d30c75 [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 sysdown
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 SysdownCfg struct {
// Sorted in call order (stage-num,function-name).
StageFuncs []stage.StageFunc
// Strings describing errors encountered while parsing the sysdown 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 *SysdownCfg) readOnePkg(lpkg *pkg.LocalPackage, cfg *syscfg.Cfg) {
settings := cfg.AllSettingsForLpkg(lpkg)
initMap := lpkg.DownFuncs(settings)
for name, stageStr := range initMap {
sf, err := stage.NewStageFunc(name, stageStr, lpkg, cfg)
if err != nil {
scfg.InvalidSettings = append(scfg.InvalidSettings, err.Error())
}
sf.ReturnType = "int"
sf.ArgList = "int reason"
scfg.StageFuncs = append(scfg.StageFuncs, sf)
}
}
// Searches the sysdown configuration for entries with identical function
// names. The sysdown configuration object is populated with the results.
func (scfg *SysdownCfg) 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) SysdownCfg {
scfg := SysdownCfg{}
for _, lpkg := range lpkgs {
scfg.readOnePkg(lpkg, cfg)
}
scfg.detectConflicts()
stage.SortStageFuncs(scfg.StageFuncs, "sysdown")
return scfg
}
func (scfg *SysdownCfg) 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 sysdown definitions, this
// function returns a string indicating the errors. If no errors were
// encountered, "" is returned.
func (scfg *SysdownCfg) ErrorText() string {
str := ""
if len(scfg.InvalidSettings) > 0 {
str += "Invalid sysdown definitions detected:"
for _, e := range scfg.InvalidSettings {
str += "\n " + e
}
}
if len(scfg.Conflicts) > 0 {
str += "Sysdown 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 *SysdownCfg) 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 arrName string
// XXX: Assign a different array name depending on isLoader.
arrName = "sysdown_cbs"
fmt.Fprintf(w, "\nint (* const %s[])(int reason) = {\n", arrName)
stage.WriteArr(sfs, w)
fmt.Fprintf(w, "};\n")
fmt.Fprintf(w, "#endif\n")
return nil
}
func (scfg *SysdownCfg) 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-sysdown-loader.c", srcDir, targetName)
} else {
path = fmt.Sprintf("%s/%s-sysdown-app.c", srcDir, targetName)
}
return stage.EnsureWritten(path, buf.Bytes())
}