You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2019/01/04 18:21:11 UTC
[mynewt-newt] 06/17: Add `imgprod` package - image production
This is an automated email from the ASF dual-hosted git repository.
ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 7cdcc26257367cd40590e59f3440e3058f82261f
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Tue Nov 20 17:51:53 2018 -0800
Add `imgprod` package - image production
---
newt/imgprod/imgprod.go | 286 ++++++++++++++++++++++++++++++++++++++++++++++++
newt/imgprod/v1.go | 218 ++++++++++++++++++++++++++++++++++++
2 files changed, 504 insertions(+)
diff --git a/newt/imgprod/imgprod.go b/newt/imgprod/imgprod.go
new file mode 100644
index 0000000..a858c49
--- /dev/null
+++ b/newt/imgprod/imgprod.go
@@ -0,0 +1,286 @@
+/**
+ * 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.
+ */
+
+// imgprod - Image production.
+
+package imgprod
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/newt/builder"
+ "mynewt.apache.org/newt/newt/manifest"
+ "mynewt.apache.org/newt/newt/newtutil"
+ "mynewt.apache.org/newt/util"
+)
+
+type ImageProdOpts struct {
+ LoaderSrcFilename string
+ LoaderDstFilename string
+ AppSrcFilename string
+ AppDstFilename string
+ EncKeyFilename string
+ Version image.ImageVersion
+ SigKeys []image.ImageSigKey
+}
+
+type ProducedImage struct {
+ Filename string
+ Image image.Image
+ Hash []byte
+ FileSize int
+}
+
+type ProducedImageSet struct {
+ Loader *ProducedImage
+ App ProducedImage
+}
+
+func produceLoader(opts ImageProdOpts) (ProducedImage, error) {
+ pi := ProducedImage{}
+
+ igo := image.ImageCreateOpts{
+ SrcBinFilename: opts.LoaderSrcFilename,
+ SrcEncKeyFilename: opts.EncKeyFilename,
+ Version: opts.Version,
+ SigKeys: opts.SigKeys,
+ }
+
+ ri, err := image.GenerateImage(igo)
+ if err != nil {
+ return pi, err
+ }
+
+ hash, err := ri.Hash()
+ if err != nil {
+ return pi, err
+ }
+
+ fileSize, err := ri.TotalSize()
+ if err != nil {
+ return pi, err
+ }
+
+ imgFile, err := os.OpenFile(opts.LoaderDstFilename,
+ os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ return pi, util.FmtNewtError(
+ "Can't open target image %s: %s",
+ opts.LoaderDstFilename, err.Error())
+ }
+ defer imgFile.Close()
+
+ if _, err := ri.Write(imgFile); err != nil {
+ return pi, err
+ }
+
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "Loader image successfully generated: %s\n", opts.LoaderDstFilename)
+
+ pi.Filename = opts.LoaderDstFilename
+ pi.Image = ri
+ pi.Hash = hash
+ pi.FileSize = fileSize
+
+ return pi, nil
+}
+
+func produceApp(opts ImageProdOpts, loaderHash []byte) (ProducedImage, error) {
+ pi := ProducedImage{}
+
+ igo := image.ImageCreateOpts{
+ SrcBinFilename: opts.AppSrcFilename,
+ SrcEncKeyFilename: opts.EncKeyFilename,
+ Version: opts.Version,
+ SigKeys: opts.SigKeys,
+ LoaderHash: loaderHash,
+ }
+
+ ri, err := image.GenerateImage(igo)
+ if err != nil {
+ return pi, err
+ }
+
+ hash, err := ri.Hash()
+ if err != nil {
+ return pi, err
+ }
+
+ fileSize, err := ri.TotalSize()
+ if err != nil {
+ return pi, err
+ }
+
+ imgFile, err := os.OpenFile(opts.AppDstFilename,
+ os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ return pi, util.FmtNewtError(
+ "Can't open target image %s: %s", opts.AppDstFilename, err.Error())
+ }
+ defer imgFile.Close()
+
+ if _, err := ri.Write(imgFile); err != nil {
+ return pi, err
+ }
+
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "App image successfully generated: %s\n", opts.AppDstFilename)
+
+ pi.Filename = opts.AppDstFilename
+ pi.Image = ri
+ pi.Hash = hash
+ pi.FileSize = fileSize
+
+ return pi, nil
+}
+
+// Verifies that each already-built image leaves enough room for a boot trailer
+// a the end of its slot.
+func verifyImgSizes(pset ProducedImageSet, maxSizes []int) error {
+ errLines := []string{}
+ slot := 0
+
+ if pset.Loader != nil {
+ if overflow := int(pset.Loader.FileSize) - maxSizes[0]; overflow > 0 {
+ errLines = append(errLines,
+ fmt.Sprintf("loader overflows slot-0 by %d bytes "+
+ "(image=%d max=%d)",
+ overflow, pset.Loader.FileSize, maxSizes[0]))
+ }
+ slot++
+ }
+
+ if overflow := int(pset.App.FileSize) - maxSizes[slot]; overflow > 0 {
+ errLines = append(errLines,
+ fmt.Sprintf("app overflows slot-%d by %d bytes "+
+ "(image=%d max=%d)",
+ slot, overflow, pset.App.FileSize, maxSizes[slot]))
+
+ }
+
+ 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
+}
+
+func ProduceImages(opts ImageProdOpts) (ProducedImageSet, error) {
+ pset := ProducedImageSet{}
+
+ var loaderHash []byte
+ if opts.LoaderSrcFilename != "" {
+ pi, err := produceLoader(opts)
+ if err != nil {
+ return pset, err
+ }
+ loaderHash = pi.Hash
+
+ pset.Loader = &pi
+ }
+
+ pi, err := produceApp(opts, loaderHash)
+ if err != nil {
+ return pset, err
+ }
+ pset.App = pi
+
+ return pset, nil
+}
+
+func ProduceManifest(opts manifest.ManifestCreateOpts) error {
+ m, err := manifest.CreateManifest(opts)
+ if err != nil {
+ return err
+ }
+
+ file, err := os.Create(opts.TgtBldr.AppBuilder.ManifestPath())
+ if err != nil {
+ return util.FmtNewtError("Cannot create manifest file %s: %s",
+ opts.TgtBldr.AppBuilder.ManifestPath(), err.Error())
+ }
+ defer file.Close()
+
+ if _, err := m.Write(file); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func OptsFromTgtBldr(b *builder.TargetBuilder, ver image.ImageVersion,
+ sigKeys []image.ImageSigKey, encKeyFilename string) ImageProdOpts {
+
+ opts := ImageProdOpts{
+ AppSrcFilename: b.AppBuilder.AppBinPath(),
+ AppDstFilename: b.AppBuilder.AppImgPath(),
+ EncKeyFilename: encKeyFilename,
+ Version: ver,
+ SigKeys: sigKeys,
+ }
+
+ if b.LoaderBuilder != nil {
+ opts.LoaderSrcFilename = b.LoaderBuilder.AppBinPath()
+ opts.LoaderDstFilename = b.LoaderBuilder.AppImgPath()
+ }
+
+ return opts
+}
+
+func ProduceAll(t *builder.TargetBuilder, ver image.ImageVersion,
+ sigKeys []image.ImageSigKey, encKeyFilename string) error {
+
+ popts := OptsFromTgtBldr(t, ver, sigKeys, encKeyFilename)
+ pset, err := ProduceImages(popts)
+ if err != nil {
+ return err
+ }
+
+ mopts := manifest.ManifestCreateOpts{
+ TgtBldr: t,
+ AppHash: pset.App.Hash,
+ Version: ver,
+ BuildID: fmt.Sprintf("%x", pset.App.Hash),
+ FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+ }
+
+ if pset.Loader != nil {
+ mopts.LoaderHash = pset.Loader.Hash
+ }
+
+ if err := ProduceManifest(mopts); err != nil {
+ return err
+ }
+
+ if err := verifyImgSizes(pset, mopts.TgtBldr.MaxImgSizes()); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/newt/imgprod/v1.go b/newt/imgprod/v1.go
new file mode 100644
index 0000000..37067d4
--- /dev/null
+++ b/newt/imgprod/v1.go
@@ -0,0 +1,218 @@
+package imgprod
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "mynewt.apache.org/newt/artifact/image"
+ "mynewt.apache.org/newt/newt/builder"
+ "mynewt.apache.org/newt/newt/manifest"
+ "mynewt.apache.org/newt/newt/newtutil"
+ "mynewt.apache.org/newt/util"
+)
+
+type ProducedImageV1 struct {
+ Filename string
+ Image image.ImageV1
+ Hash []byte
+ FileSize int
+}
+
+type ProducedImageSetV1 struct {
+ Loader *ProducedImageV1
+ App ProducedImageV1
+}
+
+func produceLoaderV1(opts ImageProdOpts) (ProducedImageV1, error) {
+ pi := ProducedImageV1{}
+
+ igo := image.ImageCreateOpts{
+ SrcBinFilename: opts.LoaderSrcFilename,
+ SrcEncKeyFilename: opts.EncKeyFilename,
+ Version: opts.Version,
+ SigKeys: opts.SigKeys,
+ }
+
+ img, err := image.GenerateV1Image(igo)
+ if err != nil {
+ return pi, err
+ }
+
+ hash, err := img.Hash()
+ if err != nil {
+ return pi, err
+ }
+
+ fileSize, err := img.TotalSize()
+ if err != nil {
+ return pi, err
+ }
+
+ imgFile, err := os.OpenFile(opts.LoaderDstFilename,
+ os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ return pi, util.FmtNewtError(
+ "Can't open target image %s: %s",
+ opts.LoaderDstFilename, err.Error())
+ }
+ defer imgFile.Close()
+
+ if _, err := img.Write(imgFile); err != nil {
+ return pi, err
+ }
+
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "V1 loader image successfully generated: %s\n", opts.LoaderDstFilename)
+
+ pi.Filename = opts.LoaderDstFilename
+ pi.Image = img
+ pi.Hash = hash
+ pi.FileSize = fileSize
+
+ return pi, nil
+}
+
+func produceAppV1(opts ImageProdOpts,
+ loaderHash []byte) (ProducedImageV1, error) {
+
+ pi := ProducedImageV1{}
+
+ igo := image.ImageCreateOpts{
+ SrcBinFilename: opts.AppSrcFilename,
+ SrcEncKeyFilename: opts.EncKeyFilename,
+ Version: opts.Version,
+ SigKeys: opts.SigKeys,
+ LoaderHash: loaderHash,
+ }
+
+ img, err := image.GenerateV1Image(igo)
+ if err != nil {
+ return pi, err
+ }
+
+ hash, err := img.Hash()
+ if err != nil {
+ return pi, err
+ }
+
+ fileSize, err := img.TotalSize()
+ if err != nil {
+ return pi, err
+ }
+
+ imgFile, err := os.OpenFile(opts.AppDstFilename,
+ os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ return pi, util.FmtNewtError(
+ "Can't open target image %s: %s", opts.AppDstFilename, err.Error())
+ }
+ defer imgFile.Close()
+
+ if _, err := img.Write(imgFile); err != nil {
+ return pi, err
+ }
+
+ util.StatusMessage(util.VERBOSITY_DEFAULT,
+ "App image successfully generated: %s\n", opts.AppDstFilename)
+
+ pi.Filename = opts.AppDstFilename
+ pi.Image = img
+ pi.Hash = hash
+ pi.FileSize = fileSize
+
+ return pi, nil
+}
+
+// Verifies that each already-built image leaves enough room for a boot trailer
+// a the end of its slot.
+func verifyImgSizesV1(pset ProducedImageSetV1, maxSizes []int) error {
+ errLines := []string{}
+ slot := 0
+
+ if pset.Loader != nil {
+ if overflow := int(pset.Loader.FileSize) - maxSizes[0]; overflow > 0 {
+ errLines = append(errLines,
+ fmt.Sprintf("loader overflows slot-0 by %d bytes "+
+ "(image=%d max=%d)",
+ overflow, pset.Loader.FileSize, maxSizes[0]))
+ }
+ slot++
+ }
+
+ if overflow := int(pset.App.FileSize) - maxSizes[slot]; overflow > 0 {
+ errLines = append(errLines,
+ fmt.Sprintf("app overflows slot-%d by %d bytes "+
+ "(image=%d max=%d)",
+ slot, overflow, pset.App.FileSize, maxSizes[slot]))
+
+ }
+
+ 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
+}
+
+func ProduceImagesV1(opts ImageProdOpts) (ProducedImageSetV1, error) {
+ pset := ProducedImageSetV1{}
+
+ var loaderHash []byte
+ if opts.LoaderSrcFilename != "" {
+ pi, err := produceLoaderV1(opts)
+ if err != nil {
+ return pset, err
+ }
+ loaderHash = pi.Hash
+
+ pset.Loader = &pi
+ }
+
+ pi, err := produceAppV1(opts, loaderHash)
+ if err != nil {
+ return pset, err
+ }
+ pset.App = pi
+
+ return pset, nil
+}
+
+func ProduceAllV1(t *builder.TargetBuilder, ver image.ImageVersion,
+ sigKeys []image.ImageSigKey, encKeyFilename string) error {
+
+ popts := OptsFromTgtBldr(t, ver, sigKeys, encKeyFilename)
+ pset, err := ProduceImagesV1(popts)
+ if err != nil {
+ return err
+ }
+
+ mopts := manifest.ManifestCreateOpts{
+ TgtBldr: t,
+ AppHash: pset.App.Hash,
+ Version: ver,
+ BuildID: fmt.Sprintf("%x", pset.App.Hash),
+ FlashAreas: t.BspPkg().FlashMap.SortedAreas(),
+ }
+
+ if pset.Loader != nil {
+ mopts.LoaderHash = pset.Loader.Hash
+ }
+
+ if err := ProduceManifest(mopts); err != nil {
+ return err
+ }
+
+ if err := verifyImgSizesV1(pset, mopts.TgtBldr.MaxImgSizes()); err != nil {
+ return err
+ }
+
+ return nil
+}