blob: bcbb94d8cf5f209d3e220295c04fd7455da8208e [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.
*/
// This file contains functionality for loading mfg definitions from `mfg.yml`
// files.
package mfg
import (
"github.com/spf13/cast"
"mynewt.apache.org/newt/newt/ycfg"
"mynewt.apache.org/newt/util"
)
// Indicates that an element is located at the end of a flash area.
const OFFSET_END = -1
type DecodedTarget struct {
Name string
Area string
Offset int
ExtraManifest map[string]interface{}
}
type DecodedRaw struct {
Filename string
Area string
Offset int
ExtraFiles []string
ExtraManifest map[string]interface{}
}
type DecodedMmrRef struct {
Area string
}
type DecodedMeta struct {
Area string
Hash bool
FlashMap bool
Mmrs []DecodedMmrRef
}
type DecodedMfg struct {
Targets []DecodedTarget
Raws []DecodedRaw
Meta *DecodedMeta
// Only required if no targets present.
Bsp string
}
func decodeOffsetStr(offsetStr string) (int, error) {
if offsetStr == "end" {
return OFFSET_END, nil
}
offsetInt, err := cast.ToIntE(offsetStr)
if err != nil {
return 0, util.FmtNewtError("invalid offset value: \"%s\"", offsetStr)
}
return offsetInt, nil
}
func decodeBool(kv map[string]interface{}, key string) (*bool, error) {
var bp *bool
val := kv[key]
if val != nil {
b, err := cast.ToBoolE(val)
if err != nil {
return nil, util.FmtNewtError(
"invalid `%s` value \"%v\"; "+
"value must be either \"true\" or \"false\"", key, val)
}
bp = &b
}
return bp, nil
}
func decodeBoolDflt(kv map[string]interface{}, key string,
dflt bool) (bool, error) {
bp, err := decodeBool(kv, key)
if err != nil {
return false, err
}
if bp == nil {
return dflt, nil
} else {
return *bp, nil
}
}
func decodeExtra(kv map[string]interface{},
key string) (map[string]interface{}, error) {
extra := kv[key]
if extra == nil {
return nil, nil
}
if _, ok := extra.(map[interface{}]interface{}); !ok {
return nil, util.FmtNewtError(
"invalid \"%s\" field: must be a map", key)
}
// If `extra` contains any maps, they were unmarshalled as
// `map[interface{}]interface{}`. These need to be converted to
// `map[string]interface{}` to be JSON compatible.
var fixMap func(m map[string]interface{})
fixMap = func(m map[string]interface{}) {
for k, v := range m {
if _, ok := v.(map[interface{}]interface{}); ok {
sm := cast.ToStringMap(v)
fixMap(sm)
m[k] = sm
}
}
}
extraMap := cast.ToStringMap(extra)
fixMap(extraMap)
return extraMap, nil
}
func decodeTarget(yamlTarget interface{}) (DecodedTarget, error) {
dt := DecodedTarget{}
kv, err := cast.ToStringMapE(yamlTarget)
if err != nil {
return dt, util.FmtNewtError(
"mfg contains invalid `mfg.targets` map: %s", err.Error())
}
nameVal := kv["name"]
if nameVal == nil {
return dt, util.FmtNewtError(
"mfg target entry missing required field \"name\"")
}
dt.Name = cast.ToString(nameVal)
areaVal := kv["area"]
if areaVal == nil {
return dt, util.FmtNewtError(
"target entry \"%s\" missing required field \"area\"", dt.Name)
}
dt.Area = cast.ToString(areaVal)
offsetVal := kv["offset"]
if offsetVal == nil {
return dt, util.FmtNewtError(
"target entry \"%s\" missing required field \"offset\"", dt.Name)
}
offsetStr := cast.ToString(offsetVal)
offsetInt, err := decodeOffsetStr(offsetStr)
if err != nil {
return dt, util.FmtNewtError(
"in target entry \"%s\": %s", dt.Name, err.Error())
}
dt.Offset = offsetInt
extra, err := decodeExtra(kv, "extra_manifest")
if err != nil {
return dt, util.FmtNewtError(
"in target entry %s: %s", dt.Name, err.Error())
}
dt.ExtraManifest = extra
return dt, nil
}
func decodeRaw(yamlRaw interface{}, entryIdx int) (DecodedRaw, error) {
dr := DecodedRaw{}
kv, err := cast.ToStringMapE(yamlRaw)
if err != nil {
return dr, util.FmtNewtError(
"mfg contains invalid `mfg.raw` map: %s", err.Error())
}
areaVal := kv["area"]
if areaVal == nil {
return dr, util.FmtNewtError(
"raw entry missing required field \"area\"")
}
dr.Area = cast.ToString(areaVal)
offsetVal := kv["offset"]
if offsetVal == nil {
return dr, util.FmtNewtError(
"mfg raw entry missing required field \"offset\"")
}
offsetStr := cast.ToString(offsetVal)
offsetInt, err := decodeOffsetStr(offsetStr)
if err != nil {
return dr, util.FmtNewtError(
"in raw entry %d: %s", entryIdx, err.Error())
}
dr.Offset = offsetInt
filenameVal := kv["name"]
if filenameVal == nil {
return dr, util.FmtNewtError(
"mfg raw entry missing required field \"filename\"")
}
dr.Filename = cast.ToString(filenameVal)
if itf, ok := kv["extra_files"]; ok {
extraFiles, err := cast.ToStringSliceE(itf)
if err != nil {
return dr, util.FmtNewtError(
"mfg raw entry contains invalid \"extra_files\" field: "+
"have=%T want=[]string", itf)
}
dr.ExtraFiles = extraFiles
}
extra, err := decodeExtra(kv, "extra_manifest")
if err != nil {
return dr, util.FmtNewtError(
"in raw entry %d: %s", entryIdx, err.Error())
}
dr.ExtraManifest = extra
return dr, nil
}
func decodeMmr(yamlMmr interface{}) (DecodedMmrRef, error) {
dm := DecodedMmrRef{}
kv, err := cast.ToStringMapE(yamlMmr)
if err != nil {
return dm, util.FmtNewtError(
"mfg meta contains invalid `mmrs` sequence: %s", err.Error())
}
areaVal := kv["area"]
if areaVal == nil {
return dm, util.FmtNewtError(
"mmr entry missing required field \"area\"")
}
dm.Area = cast.ToString(areaVal)
return dm, nil
}
func decodeMmrs(yamlMmrs interface{}) ([]DecodedMmrRef, error) {
yamlSlice, err := cast.ToSliceE(yamlMmrs)
if err != nil {
return nil, util.FmtNewtError(
"mfg meta contains invalid `mmrs` sequence: %s", err.Error())
}
mmrs := []DecodedMmrRef{}
for _, yamlMmr := range yamlSlice {
mmr, err := decodeMmr(yamlMmr)
if err != nil {
return nil, err
}
mmrs = append(mmrs, mmr)
}
return mmrs, nil
}
func decodeMeta(
kv map[string]interface{}) (DecodedMeta, error) {
dm := DecodedMeta{}
areaVal := kv["area"]
if areaVal == nil {
return dm, util.FmtNewtError(
"meta map missing required field \"area\"")
}
dm.Area = cast.ToString(areaVal)
hash, err := decodeBoolDflt(kv, "hash", false)
if err != nil {
return dm, err
}
dm.Hash = hash
fm, err := decodeBoolDflt(kv, "flash_map", false)
if err != nil {
return dm, err
}
dm.FlashMap = fm
yamlMmrs := kv["mmrs"]
if yamlMmrs != nil {
mmrs, err := decodeMmrs(yamlMmrs)
if err != nil {
return dm, err
}
dm.Mmrs = mmrs
}
return dm, nil
}
func decodeMfg(yc ycfg.YCfg) (DecodedMfg, error) {
dm := DecodedMfg{}
yamlTargets, err := yc.GetValSlice("mfg.targets", nil)
util.OneTimeWarningError(err)
if yamlTargets != nil {
for _, yamlTarget := range yamlTargets {
t, err := decodeTarget(yamlTarget)
if err != nil {
return dm, err
}
dm.Targets = append(dm.Targets, t)
}
}
dm.Bsp, err = yc.GetValString("mfg.bsp", nil)
util.OneTimeWarningError(err)
if len(dm.Targets) == 0 && dm.Bsp == "" {
return dm, util.FmtNewtError(
"\"mfg.bsp\" field required for mfg images without any targets")
}
itf, err := yc.GetValSlice("mfg.raw", nil)
util.OneTimeWarningError(err)
slice := cast.ToSlice(itf)
if slice != nil {
for i, yamlRaw := range slice {
raw, err := decodeRaw(yamlRaw, i)
if err != nil {
return dm, err
}
dm.Raws = append(dm.Raws, raw)
}
}
yamlMeta, err := yc.GetValStringMap("mfg.meta", nil)
util.OneTimeWarningError(err)
if yamlMeta != nil {
meta, err := decodeMeta(yamlMeta)
if err != nil {
return dm, err
}
dm.Meta = &meta
}
return dm, nil
}