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
+}