blob: 4071a09608f97345b89304ce4c1caff3994e7f27 [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"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
log "github.com/Sirupsen/logrus"
"mynewt.apache.org/newt/newt/newtutil"
"mynewt.apache.org/newt/newt/pkg"
"mynewt.apache.org/newt/util"
)
type initFunc struct {
stage int
name string
pkg *pkg.LocalPackage
}
func buildStageMap(pkgs []*pkg.LocalPackage) map[int][]*initFunc {
sm := map[int][]*initFunc{}
for _, p := range pkgs {
for name, stage := range p.Init() {
initFunc := &initFunc{
stage: stage,
name: name,
pkg: p,
}
sm[stage] = append(sm[stage], initFunc)
}
}
return sm
}
type initFuncSorter struct {
fns []*initFunc
}
func (s initFuncSorter) Len() int {
return len(s.fns)
}
func (s initFuncSorter) Swap(i, j int) {
s.fns[i], s.fns[j] = s.fns[j], s.fns[i]
}
func (s initFuncSorter) Less(i, j int) bool {
a := s.fns[i]
b := s.fns[j]
// 1: Sort by stage number.
if a.stage < b.stage {
return true
} else if b.stage < a.stage {
return false
}
// 2: Sort by function 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 sysinit functions detected: %s", a.name)
return true
}
func sortedInitFuncs(pkgs []*pkg.LocalPackage) []*initFunc {
sorter := initFuncSorter{
fns: make([]*initFunc, 0, len(pkgs)),
}
for _, p := range pkgs {
initMap := p.Init()
for name, stage := range initMap {
fn := &initFunc{
name: name,
stage: stage,
pkg: p,
}
sorter.fns = append(sorter.fns, fn)
}
}
sort.Sort(sorter)
return sorter.fns
}
func writePrototypes(pkgs []*pkg.LocalPackage, w io.Writer) {
sortedFns := sortedInitFuncs(pkgs)
for _, f := range sortedFns {
fmt.Fprintf(w, "void %s(void);\n", f.name)
}
}
func writeCalls(sortedInitFuncs []*initFunc, w io.Writer) {
prevStage := -1
dupCount := 0
for i, f := range sortedInitFuncs {
if f.stage != prevStage {
prevStage = f.stage
dupCount = 0
if i != 0 {
fmt.Fprintf(w, "\n")
}
fmt.Fprintf(w, " /*** Stage %d */\n", f.stage)
} else {
dupCount += 1
}
fmt.Fprintf(w, " /* %d.%d: %s (%s) */\n",
f.stage, dupCount, f.name, f.pkg.Name())
fmt.Fprintf(w, " %s();\n", f.name)
}
}
func write(pkgs []*pkg.LocalPackage, isLoader bool,
w io.Writer) {
fmt.Fprintf(w, newtutil.GeneratedPreamble())
if isLoader {
fmt.Fprintf(w, "#if SPLIT_LOADER\n\n")
} else {
fmt.Fprintf(w, "#if !SPLIT_LOADER\n\n")
}
writePrototypes(pkgs, 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)
writeCalls(sortedInitFuncs(pkgs), w)
fmt.Fprintf(w, "}\n\n")
fmt.Fprintf(w, "#endif\n")
}
func writeRequired(contents []byte, path string) (bool, error) {
oldSrc, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
// File doesn't exist; write required.
return true, nil
}
return true, util.NewNewtError(err.Error())
}
rc := bytes.Compare(oldSrc, contents)
return rc != 0, nil
}
func EnsureWritten(pkgs []*pkg.LocalPackage, srcDir string, targetName string,
isLoader bool) error {
buf := bytes.Buffer{}
write(pkgs, isLoader, &buf)
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)
}
writeReqd, err := writeRequired(buf.Bytes(), path)
if err != nil {
return err
}
if !writeReqd {
log.Debugf("sysinit unchanged; not writing src file (%s).", path)
return nil
}
log.Debugf("sysinit 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, buf.Bytes(), 0644); err != nil {
return util.NewNewtError(err.Error())
}
return nil
}