blob: 1c2f8ba75b837265fe543456414009ce59693d9e [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 builder
import (
"bufio"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"time"
log "github.com/Sirupsen/logrus"
"mynewt.apache.org/newt/newt/flash"
"mynewt.apache.org/newt/newt/image"
"mynewt.apache.org/newt/newt/interfaces"
"mynewt.apache.org/newt/newt/newtutil"
"mynewt.apache.org/newt/newt/pkg"
"mynewt.apache.org/newt/newt/project"
"mynewt.apache.org/newt/newt/resolve"
"mynewt.apache.org/newt/newt/symbol"
"mynewt.apache.org/newt/newt/syscfg"
"mynewt.apache.org/newt/newt/sysinit"
"mynewt.apache.org/newt/newt/target"
"mynewt.apache.org/newt/newt/toolchain"
"mynewt.apache.org/newt/util"
)
type TargetBuilder struct {
target *target.Target
bspPkg *pkg.BspPackage
compilerPkg *pkg.LocalPackage
appPkg *pkg.LocalPackage
loaderPkg *pkg.LocalPackage
testPkg *pkg.LocalPackage
AppBuilder *Builder
AppList interfaces.PackageList
LoaderBuilder *Builder
LoaderList interfaces.PackageList
keyFile string
injectedSettings map[string]string
res *resolve.Resolution
}
func NewTargetTester(target *target.Target,
testPkg *pkg.LocalPackage) (*TargetBuilder, error) {
if err := target.Validate(testPkg == nil); err != nil {
return nil, err
}
bspPkg, err := pkg.NewBspPackage(target.Bsp())
if err != nil {
return nil, err
}
compilerPkg, err := project.GetProject().ResolvePackage(
bspPkg.Repo(), bspPkg.CompilerName)
if err != nil {
return nil, err
}
t := &TargetBuilder{
target: target,
bspPkg: bspPkg,
compilerPkg: compilerPkg,
appPkg: target.App(),
loaderPkg: target.Loader(),
keyFile: target.KeyFile,
testPkg: testPkg,
injectedSettings: map[string]string{},
}
return t, nil
}
func NewTargetBuilder(target *target.Target) (*TargetBuilder, error) {
return NewTargetTester(target, nil)
}
func (t *TargetBuilder) NewCompiler(dstDir string, buildProfile string) (
*toolchain.Compiler, error) {
if buildProfile == "" {
buildProfile = t.target.BuildProfile
}
c, err := toolchain.NewCompiler(
t.compilerPkg.BasePath(), dstDir, buildProfile)
return c, err
}
func (t *TargetBuilder) ensureResolved() error {
if t.res != nil {
return nil
}
var loaderSeeds []*pkg.LocalPackage
if t.loaderPkg != nil {
loaderSeeds = []*pkg.LocalPackage{
t.target.LoaderYml(),
t.target.BspYml(),
t.compilerPkg,
t.target.Package(),
}
// For split images, inject the SPLIT_[...] settings into the
// corresponding app packages. This ensures that:
// * The app packages know they are part of a split image during
// dependency resolution.
// * The app source files receive "-DSPLIT_[...]=1" command line
// arguments during compilation.
t.loaderPkg.InjectedSettings()["SPLIT_LOADER"] = "1"
if t.appPkg != nil {
t.appPkg.InjectedSettings()["SPLIT_APPLICATION"] = "1"
}
// Inject the SPLIT_IMAGE setting into the entire target. All packages
// now know that they are part of a split image build.
t.injectedSettings["SPLIT_IMAGE"] = "1"
}
appSeeds := []*pkg.LocalPackage{
t.target.BspYml(),
t.compilerPkg,
t.target.Package(),
}
if t.appPkg != nil {
appSeeds = append(appSeeds, t.target.AppYml())
}
if t.testPkg != nil {
// A few features are automatically supported when the test command is
// used:
// * TEST: lets packages know that this is a test app
// * SELFTEST: indicates that the "newt test" command is used;
// causes a package to define a main() function.
t.injectedSettings["TEST"] = "1"
t.injectedSettings["SELFTEST"] = "1"
appSeeds = append(appSeeds, t.testPkg)
}
var err error
t.res, err = resolve.ResolveFull(
loaderSeeds, appSeeds, t.injectedSettings, t.bspPkg.FlashMap)
if err != nil {
return err
}
return nil
}
func (t *TargetBuilder) Resolve() (*resolve.Resolution, error) {
if err := t.ensureResolved(); err != nil {
return nil, err
}
return t.res, nil
}
func (t *TargetBuilder) validateAndWriteCfg() error {
if err := t.ensureResolved(); err != nil {
return err
}
if errText := t.res.ErrorText(); errText != "" {
return util.NewNewtError(errText)
}
warningText := strings.TrimSpace(t.res.WarningText())
if warningText != "" {
log.Debug(warningText)
}
for _, line := range t.res.DeprecatedWarning() {
log.Warn(line)
}
if err := syscfg.EnsureWritten(t.res.Cfg,
GeneratedIncludeDir(t.target.Name())); err != nil {
return err
}
return nil
}
func (t *TargetBuilder) generateSysinit() error {
if err := t.ensureResolved(); err != nil {
return err
}
srcDir := GeneratedSrcDir(t.target.Name())
if t.res.LoaderSet != nil {
lpkgs := resolve.RpkgSliceToLpkgSlice(t.res.LoaderSet.Rpkgs)
sysinit.EnsureWritten(lpkgs, srcDir,
pkg.ShortName(t.target.Package()), true)
}
lpkgs := resolve.RpkgSliceToLpkgSlice(t.res.AppSet.Rpkgs)
sysinit.EnsureWritten(lpkgs, srcDir,
pkg.ShortName(t.target.Package()), false)
return nil
}
func (t *TargetBuilder) generateFlashMap() error {
return t.bspPkg.FlashMap.EnsureWritten(
GeneratedSrcDir(t.target.Name()),
GeneratedIncludeDir(t.target.Name()),
pkg.ShortName(t.target.Package()))
}
func (t *TargetBuilder) generateCode() error {
if err := t.generateSysinit(); err != nil {
return err
}
if err := t.generateFlashMap(); err != nil {
return err
}
return nil
}
func (t *TargetBuilder) PrepBuild() error {
if err := t.ensureResolved(); err != nil {
return err
}
flashErrText := t.bspPkg.FlashMap.ErrorText()
if flashErrText != "" {
return util.NewNewtError(flashErrText)
}
if err := t.validateAndWriteCfg(); err != nil {
return err
}
var err error
if t.res.LoaderSet != nil {
t.LoaderBuilder, err = NewBuilder(t, BUILD_NAME_LOADER,
t.res.LoaderSet.Rpkgs, t.res.ApiMap, t.res.Cfg)
if err != nil {
return err
}
if err := t.LoaderBuilder.PrepBuild(); err != nil {
return err
}
loaderFlags := toolchain.NewCompilerInfo()
loaderFlags.Cflags = append(loaderFlags.Cflags, "-DSPLIT_LOADER")
t.LoaderBuilder.AddCompilerInfo(loaderFlags)
t.LoaderList = project.ResetDeps(nil)
}
t.AppBuilder, err = NewBuilder(t, BUILD_NAME_APP, t.res.AppSet.Rpkgs,
t.res.ApiMap, t.res.Cfg)
if err != nil {
return err
}
if err := t.AppBuilder.PrepBuild(); err != nil {
return err
}
if t.res.LoaderSet != nil {
appFlags := toolchain.NewCompilerInfo()
appFlags.Cflags = append(appFlags.Cflags, "-DSPLIT_APPLICATION")
t.AppBuilder.AddCompilerInfo(appFlags)
}
t.AppList = project.ResetDeps(nil)
logDepInfo(t.res)
if err := t.generateCode(); err != nil {
return err
}
return nil
}
func (t *TargetBuilder) buildLoader() error {
/* Tentatively link the app (using the normal single image linker script) */
if err := t.AppBuilder.TentativeLink(t.bspPkg.LinkerScripts); err != nil {
return err
}
/* rebuild the loader */
project.ResetDeps(t.LoaderList)
if err := t.bspPkg.Reload(t.LoaderBuilder.cfg.SettingValues()); err != nil {
return err
}
if err := t.LoaderBuilder.Build(); err != nil {
return err
}
/* Tentatively link the loader */
if err := t.LoaderBuilder.TentativeLink(t.bspPkg.LinkerScripts); err != nil {
return err
}
/* re-link the loader with app dependencies */
err, commonPkgs, commonSyms := t.RelinkLoader()
if err != nil {
return err
}
/* The app can ignore these packages next time */
delete(commonPkgs, t.bspPkg.Name())
t.AppBuilder.RemovePackages(commonPkgs)
/* create the special elf to link the app against */
/* its just the elf with a set of symbols removed and renamed */
err = t.LoaderBuilder.buildRomElf(commonSyms)
if err != nil {
return err
}
/* set up the linker elf and linker script for the app */
t.AppBuilder.linkElf = t.LoaderBuilder.AppLinkerElfPath()
return nil
}
/// Generates a .c source file with public key information required by the
/// bootloader.
///
/// The input filename should be supplied by the user in the target.yml file,
/// using the `target.key_file` option. This file can be either a private key
/// in PEM format, an extracted public key in PEM format or a DER file.
///
/// To extract a PEM public key from the private key:
/// `openssl ec -in ec_pk.pem -pubout -out pubkey.pub`
/// `openssl rsa -in rsa_pk.pem -RSAPublicKey_out -out pubkey.pub`
func (t *TargetBuilder) autogenKeys() error {
keyBytes, err := ioutil.ReadFile(t.keyFile)
if err != nil {
return util.NewNewtError(fmt.Sprintf("Error reading key file: %s", err))
}
// Initially try parsing a private key in PEM format, if it fails try
// parsing as PEM public key, otherwise accepted as raw key data (DER)
privKey, err := image.ParsePrivateKey(keyBytes)
if err == nil {
switch pk := privKey.(type) {
case *rsa.PrivateKey:
keyBytes = x509.MarshalPKCS1PublicKey(&pk.PublicKey)
case *ecdsa.PrivateKey:
keyBytes, err = x509.MarshalPKIXPublicKey(&pk.PublicKey)
if err != nil {
return util.NewNewtError("Failed parsing EC public key")
}
default:
return util.NewNewtError("Unknown private key format")
}
} else {
b, _ := pem.Decode(keyBytes)
if b != nil && (b.Type == "PUBLIC KEY" || b.Type == "RSA PUBLIC KEY") {
keyBytes = b.Bytes
}
}
srcDir := GeneratedSrcDir(t.target.Name())
f, _ := os.Create(srcDir + "/pubkey-autogen.c")
w := bufio.NewWriter(f)
fmt.Fprintln(w, "/* Autogenerated, do not edit. */")
fmt.Fprintln(w, "#include <bootutil/sign_key.h>")
fmt.Fprintf(w, "const unsigned char key[] = {")
for count, b := range keyBytes {
if count%8 == 0 {
fmt.Fprintf(w, "\n ")
} else {
fmt.Fprintf(w, " ")
}
fmt.Fprintf(w, "0x%02x,", b)
}
fmt.Fprintf(w, "\n};\n")
fmt.Fprintf(w, "const unsigned int key_len = %v;\n", len(keyBytes))
fmt.Fprintln(w, "const struct bootutil_key bootutil_keys[] = {")
fmt.Fprintln(w, " [0] = {")
fmt.Fprintln(w, " .key = key,")
fmt.Fprintln(w, " .len = &key_len,")
fmt.Fprintln(w, " },")
fmt.Fprintln(w, "};")
fmt.Fprintln(w, "const int bootutil_key_cnt = 1;")
w.Flush()
return nil
}
func (t *TargetBuilder) Build() error {
if err := t.PrepBuild(); err != nil {
return err
}
/* Build the Apps */
project.ResetDeps(t.AppList)
if err := t.bspPkg.Reload(t.AppBuilder.cfg.SettingValues()); err != nil {
return err
}
if t.keyFile != "" {
err := t.autogenKeys()
if err != nil {
return err
}
}
if err := t.AppBuilder.Build(); err != nil {
return err
}
var linkerScripts []string
if t.LoaderBuilder == nil {
linkerScripts = t.bspPkg.LinkerScripts
} else {
if err := t.buildLoader(); err != nil {
return err
}
linkerScripts = t.bspPkg.Part2LinkerScripts
}
/* Link the app. */
if err := t.AppBuilder.Link(linkerScripts); err != nil {
return err
}
/* Create manifest. */
if err := t.createManifest(); err != nil {
return err
}
return nil
}
/*
* This function re-links the loader adding symbols from libraries
* shared with the app. Returns a list of the common packages shared
* by the app and loader
*/
func (t *TargetBuilder) RelinkLoader() (error, map[string]bool,
*symbol.SymbolMap) {
/* fetch symbols from the elf and from the libraries themselves */
log.Debugf("Loader packages:")
for _, rpkg := range t.LoaderBuilder.sortedRpkgs() {
log.Debugf(" * %s", rpkg.Lpkg.Name())
}
log.Debugf("App packages:")
for _, rpkg := range t.AppBuilder.sortedRpkgs() {
log.Debugf(" * %s", rpkg.Lpkg.Name())
}
err, appLibSym := t.AppBuilder.ExtractSymbolInfo()
if err != nil {
return err, nil, nil
}
/* fetch the symbol list from the app temporary elf */
err, appElfSym := t.AppBuilder.ParseObjectElf(t.AppBuilder.AppTentativeElfPath())
if err != nil {
return err, nil, nil
}
/* extract the library symbols and elf symbols from the loader */
err, loaderLibSym := t.LoaderBuilder.ExtractSymbolInfo()
if err != nil {
return err, nil, nil
}
err, loaderElfSym := t.LoaderBuilder.ParseObjectElf(
t.LoaderBuilder.AppTentativeElfPath())
if err != nil {
return err, nil, nil
}
/* create the set of matching and non-matching symbols */
err, smMatch, smNomatch := symbol.IdenticalUnion(appLibSym,
loaderLibSym, true, false)
/* which packages are shared between the two */
commonPkgs := smMatch.Packages()
uncommonPkgs := smNomatch.Packages()
/* ensure that the loader and app packages are never shared */
delete(commonPkgs, t.AppBuilder.appPkg.rpkg.Lpkg.Name())
uncommonPkgs[t.AppBuilder.appPkg.rpkg.Lpkg.Name()] = true
ma := smMatch.FilterPkg(t.AppBuilder.appPkg.rpkg.Lpkg.Name())
smMatch.RemoveMap(ma)
delete(commonPkgs, t.LoaderBuilder.appPkg.rpkg.Lpkg.Name())
uncommonPkgs[t.LoaderBuilder.appPkg.rpkg.Lpkg.Name()] = true
ml := smMatch.FilterPkg(t.LoaderBuilder.appPkg.rpkg.Lpkg.Name())
smMatch.RemoveMap(ml)
util.StatusMessage(util.VERBOSITY_VERBOSE,
"Putting %d symbols from %d packages into loader\n",
len(*smMatch), len(commonPkgs))
var badpkgs []string
var symbolStr string
for v, _ := range uncommonPkgs {
if t.AppBuilder.appPkg != nil &&
t.AppBuilder.appPkg.rpkg.Lpkg.Name() != v &&
t.LoaderBuilder.appPkg != nil &&
t.LoaderBuilder.appPkg.rpkg.Lpkg.Name() != v {
trouble := smNomatch.FilterPkg(v)
var found bool
for _, sym := range *trouble {
if !sym.IsLocal() {
found = true
}
}
if found {
symbolStr = (*trouble).String("Non Matching Symbols")
badpkgs = append(badpkgs, v)
delete(commonPkgs, v)
}
}
}
if len(badpkgs) > 0 {
errStr := fmt.Sprintf(
"Common packages with different implementation\n %s\n",
strings.Join(badpkgs, "\n "))
errStr += symbolStr
return util.NewNewtError(errStr), nil, nil
}
/* for each symbol in the elf of the app, if that symbol is in
* a common package, keep that symbol in the loader */
preserveElf := symbol.NewSymbolMap()
/* go through each symbol in the app */
for _, elfsym := range *appElfSym {
name := elfsym.Name
if libsym, ok := (*appLibSym)[name]; ok {
if _, ok := commonPkgs[libsym.Bpkg]; ok {
/* if its not in the loader elf, add it as undefined */
if _, ok := (*loaderElfSym)[name]; !ok {
preserveElf.Add(elfsym)
}
}
}
}
/* re-link loader */
project.ResetDeps(t.LoaderList)
util.StatusMessage(util.VERBOSITY_VERBOSE,
"Migrating %d unused symbols into Loader\n", len(*preserveElf))
err = t.LoaderBuilder.KeepLink(t.bspPkg.LinkerScripts, preserveElf)
if err != nil {
return err, nil, nil
}
return err, commonPkgs, smMatch
}
func (t *TargetBuilder) GetTarget() *target.Target {
return t.target
}
func (t *TargetBuilder) GetTestPkg() *pkg.LocalPackage {
return t.testPkg
}
func (t *TargetBuilder) InjectSetting(key string, value string) {
t.injectedSettings[key] = value
}
func readManifest(path string) (*image.ImageManifest, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return nil, util.ChildNewtError(err)
}
manifest := &image.ImageManifest{}
if err := json.Unmarshal(content, &manifest); err != nil {
return nil, util.FmtNewtError(
"Failure decoding manifest with path \"%s\": %s", err.Error())
}
return manifest, nil
}
func (t *TargetBuilder) createManifest() error {
manifest := &image.ImageManifest{
Date: time.Now().Format(time.RFC3339),
Name: t.GetTarget().FullName(),
}
rm := image.NewRepoManager()
for _, rpkg := range t.AppBuilder.sortedRpkgs() {
manifest.Pkgs = append(manifest.Pkgs,
rm.GetImageManifestPkg(rpkg.Lpkg))
}
if t.LoaderBuilder != nil {
for _, rpkg := range t.LoaderBuilder.sortedRpkgs() {
manifest.LoaderPkgs = append(manifest.LoaderPkgs,
rm.GetImageManifestPkg(rpkg.Lpkg))
}
}
manifest.Repos = rm.AllRepos()
vars := t.GetTarget().TargetY.AllSettingsAsStrings()
keys := make([]string, 0, len(vars))
for k := range vars {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
manifest.TgtVars = append(manifest.TgtVars, k+"="+vars[k])
}
syscfgKV := t.GetTarget().Package().SyscfgY.GetValStringMapString(
"syscfg.vals", nil)
if len(syscfgKV) > 0 {
tgtSyscfg := fmt.Sprintf("target.syscfg=%s",
syscfg.KeyValueToStr(syscfgKV))
manifest.TgtVars = append(manifest.TgtVars, tgtSyscfg)
}
c, err := t.AppBuilder.PkgSizes()
if err == nil {
manifest.PkgSizes = c.Pkgs
}
if t.LoaderBuilder != nil {
c, err = t.LoaderBuilder.PkgSizes()
if err == nil {
manifest.LoaderPkgSizes = c.Pkgs
}
}
file, err := os.Create(t.AppBuilder.ManifestPath())
if err != nil {
return util.FmtNewtError("Cannot create manifest file %s: %s",
t.AppBuilder.ManifestPath(), err.Error())
}
defer file.Close()
buffer, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return util.FmtNewtError("Cannot encode manifest: %s", err.Error())
}
_, err = file.Write(buffer)
if err != nil {
return util.FmtNewtError("Cannot write manifest file: %s",
err.Error())
}
return nil
}
// Reads an existing manifest file and augments it with image fields:
// * Image version
// * App image path
// * App image hash
// * Loader image path
// * Loader image hash
// * Build ID
func (t *TargetBuilder) augmentManifest(
appImg *image.Image,
loaderImg *image.Image,
buildId []byte) error {
manifest, err := readManifest(t.AppBuilder.ManifestPath())
if err != nil {
return err
}
manifest.Version = appImg.Version.String()
manifest.ImageHash = fmt.Sprintf("%x", appImg.Hash)
manifest.Image = filepath.Base(appImg.TargetImg)
if loaderImg != nil {
manifest.Loader = filepath.Base(loaderImg.TargetImg)
manifest.LoaderHash = fmt.Sprintf("%x", loaderImg.Hash)
}
manifest.BuildID = fmt.Sprintf("%x", buildId)
file, err := os.Create(t.AppBuilder.ManifestPath())
if err != nil {
return util.FmtNewtError("Cannot create manifest file %s: %s",
t.AppBuilder.ManifestPath(), err.Error())
}
defer file.Close()
buffer, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return util.FmtNewtError("Cannot encode manifest: %s", err.Error())
}
_, err = file.Write(buffer)
if err != nil {
return util.FmtNewtError("Cannot write manifest file: %s",
err.Error())
}
return nil
}
// Calculates the size of a single boot trailer. This is the amount of flash
// that must be reserved at the end of each image slot.
func (t *TargetBuilder) bootTrailerSize() int {
var minWriteSz int
entry, ok := t.res.Cfg.Settings["MCU_FLASH_MIN_WRITE_SIZE"]
if !ok {
util.StatusMessage(util.VERBOSITY_DEFAULT,
"* Warning: target does not define MCU_FLASH_MIN_WRITE_SIZE "+
"setting; assuming a value of 1.\n")
minWriteSz = 1
} else {
val, err := util.AtoiNoOct(entry.Value)
if err != nil {
util.StatusMessage(util.VERBOSITY_DEFAULT,
"* Warning: target specifies invalid non-integer "+
"MCU_FLASH_MIN_WRITE_SIZE setting; assuming a "+
"value of 1.\n")
minWriteSz = 1
} else {
minWriteSz = val
}
}
/* Mynewt boot trailer format:
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ MAGIC (16 octets) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ ~
* ~ Swap status (128 * min-write-size * 3) ~
* ~ ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Copy done | 0xff padding (up to min-write-sz - 1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Image OK | 0xff padding (up to min-write-sz - 1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
tsize := 16 + // Magic.
128*minWriteSz*3 + // Swap status.
minWriteSz + // Copy done.
minWriteSz // Image Ok.
log.Debugf("Min-write-size=%d; boot-trailer-size=%d", minWriteSz, tsize)
return tsize
}
// Calculates the size of the largest image that can be written to each image
// slot.
func (t *TargetBuilder) maxImgSizes() []int {
sz0 := t.bspPkg.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0].Size
sz1 := t.bspPkg.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_1].Size
trailerSz := t.bootTrailerSize()
return []int{
sz0 - trailerSz,
sz1 - trailerSz,
}
}
// Verifies that each already-built image leaves enough room for a boot trailer
// a the end of its slot.
func (t *TargetBuilder) verifyImgSizes(li *image.Image, ai *image.Image) error {
maxSizes := t.maxImgSizes()
errLines := []string{}
if li != nil {
if overflow := int(li.TotalSize) - maxSizes[0]; overflow > 0 {
errLines = append(errLines,
fmt.Sprintf("loader overflows slot-0 by %d bytes "+
"(image=%d max=%d)",
overflow, li.TotalSize, maxSizes[0]))
}
if overflow := int(ai.TotalSize) - maxSizes[1]; overflow > 0 {
errLines = append(errLines,
fmt.Sprintf("app overflows slot-1 by %d bytes "+
"(image=%d max=%d)",
overflow, ai.TotalSize, maxSizes[1]))
}
} else {
if overflow := int(ai.TotalSize) - maxSizes[0]; overflow > 0 {
errLines = append(errLines,
fmt.Sprintf("app overflows slot-0 by %d bytes "+
"(image=%d max=%d)",
overflow, ai.TotalSize, maxSizes[0]))
}
}
if len(errLines) > 0 {
if !newtutil.NewtForce {
return util.NewNewtError(strings.Join(errLines, "; "))
} else {
for _, e := range errLines {
util.StatusMessage(util.VERBOSITY_QUIET,
"* Warning: %s (ignoring due to force flag)\n", e)
}
}
}
return nil
}
// @return app-image, loader-image, error
func (t *TargetBuilder) CreateImages(version string,
keystr string, keyId uint8) (*image.Image, *image.Image, error) {
if err := t.Build(); err != nil {
return nil, nil, err
}
var err error
var appImg *image.Image
var loaderImg *image.Image
c, err := t.NewCompiler("", "")
if err != nil {
return nil, nil, err
}
if t.LoaderBuilder != nil {
loaderImg, err = t.LoaderBuilder.CreateImage(version, keystr, keyId,
nil)
if err != nil {
return nil, nil, err
}
tgtArea := t.bspPkg.FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0]
log.Debugf("Convert %s -> %s at offset 0x%x",
t.LoaderBuilder.AppImgPath(),
t.LoaderBuilder.AppHexPath(),
tgtArea.Offset)
err = c.ConvertBinToHex(t.LoaderBuilder.AppImgPath(),
t.LoaderBuilder.AppHexPath(), tgtArea.Offset)
if err != nil {
log.Errorf("Can't convert to hexfile %s\n", err.Error())
}
}
appImg, err = t.AppBuilder.CreateImage(version, keystr, keyId, loaderImg)
if err != nil {
return nil, nil, err
}
flashTargetArea := ""
if t.LoaderBuilder == nil {
flashTargetArea = flash.FLASH_AREA_NAME_IMAGE_0
} else {
flashTargetArea = flash.FLASH_AREA_NAME_IMAGE_1
}
tgtArea := t.bspPkg.FlashMap.Areas[flashTargetArea]
if tgtArea.Name != "" {
log.Debugf("Convert %s -> %s at offset 0x%x",
t.AppBuilder.AppImgPath(),
t.AppBuilder.AppHexPath(),
tgtArea.Offset)
err = c.ConvertBinToHex(t.AppBuilder.AppImgPath(),
t.AppBuilder.AppHexPath(), tgtArea.Offset)
if err != nil {
log.Errorf("Can't convert to hexfile %s\n", err.Error())
}
}
buildId := image.CreateBuildId(appImg, loaderImg)
if err := t.augmentManifest(appImg, loaderImg, buildId); err != nil {
return nil, nil, err
}
if err := t.verifyImgSizes(loaderImg, appImg); err != nil {
return nil, nil, err
}
return appImg, loaderImg, nil
}
func (t *TargetBuilder) CreateDepGraph() (DepGraph, error) {
if err := t.ensureResolved(); err != nil {
return nil, err
}
return depGraph(t.res.MasterSet)
}
func (t *TargetBuilder) CreateRevdepGraph() (DepGraph, error) {
if err := t.ensureResolved(); err != nil {
return nil, err
}
return revdepGraph(t.res.MasterSet)
}