| /** |
| * 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 - Manifest generation. |
| |
| package manifest |
| |
| import ( |
| "fmt" |
| "os" |
| "sort" |
| "strings" |
| "time" |
| |
| log "github.com/sirupsen/logrus" |
| |
| "github.com/apache/mynewt-artifact/image" |
| "github.com/apache/mynewt-artifact/manifest" |
| "mynewt.apache.org/newt/newt/builder" |
| "mynewt.apache.org/newt/newt/pkg" |
| "mynewt.apache.org/newt/newt/syscfg" |
| "mynewt.apache.org/newt/util" |
| ) |
| |
| type ManifestSizeCollector struct { |
| Pkgs []*manifest.ManifestSizePkg |
| } |
| |
| type ManifestCreateOpts struct { |
| TgtBldr *builder.TargetBuilder |
| LoaderHash []byte |
| AppHash []byte |
| Version image.ImageVersion |
| BuildID string |
| Syscfg map[string]string |
| } |
| |
| type RepoManager struct { |
| repos map[string]manifest.ManifestRepo |
| } |
| |
| func NewRepoManager() *RepoManager { |
| return &RepoManager{ |
| repos: make(map[string]manifest.ManifestRepo), |
| } |
| } |
| |
| func (r *RepoManager) AllRepos() []*manifest.ManifestRepo { |
| keys := make([]string, 0, len(r.repos)) |
| for k := range r.repos { |
| keys = append(keys, k) |
| } |
| |
| sort.Strings(keys) |
| |
| repos := make([]*manifest.ManifestRepo, 0, len(keys)) |
| for _, key := range keys { |
| r := r.repos[key] |
| repos = append(repos, &r) |
| } |
| |
| return repos |
| } |
| |
| func (c *ManifestSizeCollector) AddPkg(pkg string) *manifest.ManifestSizePkg { |
| p := &manifest.ManifestSizePkg{ |
| Name: pkg, |
| } |
| c.Pkgs = append(c.Pkgs, p) |
| |
| return p |
| } |
| |
| func AddSymbol(p *manifest.ManifestSizePkg, file string, sym string, area string, |
| symSz uint32) { |
| |
| f := addFile(p, file) |
| s := addSym(f, sym) |
| addArea(s, area, symSz) |
| } |
| |
| func addFile(p *manifest.ManifestSizePkg, file string) *manifest.ManifestSizeFile { |
| for _, f := range p.Files { |
| if f.Name == file { |
| return f |
| } |
| } |
| f := &manifest.ManifestSizeFile{ |
| Name: file, |
| } |
| p.Files = append(p.Files, f) |
| |
| return f |
| } |
| |
| func addSym(f *manifest.ManifestSizeFile, sym string) *manifest.ManifestSizeSym { |
| s := &manifest.ManifestSizeSym{ |
| Name: sym, |
| } |
| f.Syms = append(f.Syms, s) |
| |
| return s |
| } |
| |
| func addArea(s *manifest.ManifestSizeSym, area string, areaSz uint32) { |
| a := &manifest.ManifestSizeArea{ |
| Name: area, |
| Size: areaSz, |
| } |
| s.Areas = append(s.Areas, a) |
| } |
| |
| func (r *RepoManager) GetManifestPkg( |
| lpkg *pkg.LocalPackage) *manifest.ManifestPkg { |
| |
| ip := &manifest.ManifestPkg{ |
| Name: lpkg.FullName(), |
| } |
| |
| ip.Repo = lpkg.Repo().Name() |
| path := lpkg.BasePath() |
| |
| if _, present := r.repos[ip.Repo]; present { |
| return ip |
| } |
| |
| repo := manifest.ManifestRepo{ |
| Name: ip.Repo, |
| } |
| |
| // Make sure we restore the current working dir to whatever it was when |
| // this function was called |
| cwd, err := os.Getwd() |
| if err != nil { |
| log.Debugf("Unable to determine current working directory: %v", err) |
| return ip |
| } |
| defer os.Chdir(cwd) |
| |
| if err := os.Chdir(path); err != nil { |
| return ip |
| } |
| |
| var res []byte |
| |
| res, err = util.ShellCommand([]string{ |
| "git", |
| "rev-parse", |
| "HEAD", |
| }, nil) |
| if err != nil { |
| log.Debugf("Unable to determine commit hash for %s: %v", path, err) |
| repo.Commit = "UNKNOWN" |
| } else { |
| repo.Commit = strings.TrimSpace(string(res)) |
| res, err = util.ShellCommand([]string{ |
| "git", |
| "status", |
| "--porcelain", |
| }, nil) |
| if err != nil { |
| log.Debugf("Unable to determine dirty state for %s: %v", path, err) |
| } else { |
| if len(res) > 0 { |
| repo.Dirty = true |
| } |
| } |
| res, err = util.ShellCommand([]string{ |
| "git", |
| "config", |
| "--get", |
| "remote.origin.url", |
| }, nil) |
| if err != nil { |
| log.Debugf("Unable to determine URL for %s: %v", path, err) |
| } else { |
| repo.URL = strings.TrimSpace(string(res)) |
| } |
| } |
| r.repos[ip.Repo] = repo |
| |
| return ip |
| } |
| |
| func ManifestPkgSizes(b *builder.Builder) (ManifestSizeCollector, error) { |
| msc := ManifestSizeCollector{} |
| |
| libs, err := builder.ParseMapFileSizes(b.AppMapPath()) |
| if err != nil { |
| return msc, err |
| } |
| |
| // Order libraries by name. |
| pkgSizes := make(builder.PkgSizeArray, len(libs)) |
| i := 0 |
| for _, es := range libs { |
| pkgSizes[i] = es |
| i++ |
| } |
| sort.Sort(pkgSizes) |
| |
| for _, es := range pkgSizes { |
| p := msc.AddPkg(b.FindPkgNameByArName(es.Name)) |
| |
| // Order symbols by name. |
| symbols := make(builder.SymbolDataArray, len(es.Syms)) |
| i := 0 |
| for _, sym := range es.Syms { |
| symbols[i] = sym |
| i++ |
| } |
| sort.Sort(symbols) |
| for _, sym := range symbols { |
| for area, areaSz := range sym.Sizes { |
| if areaSz != 0 { |
| AddSymbol(p, sym.ObjName, sym.Name, area, areaSz) |
| } |
| } |
| } |
| } |
| |
| return msc, nil |
| } |
| |
| func OptsForNonImage(t *builder.TargetBuilder) (ManifestCreateOpts, error) { |
| res, err := t.Resolve() |
| if err != nil { |
| return ManifestCreateOpts{}, err |
| } |
| |
| return ManifestCreateOpts{ |
| TgtBldr: t, |
| Syscfg: res.Cfg.SettingValues().ToMap(), |
| }, nil |
| } |
| |
| func OptsForImage(t *builder.TargetBuilder, ver image.ImageVersion, |
| appHash []byte, loaderHash []byte) (ManifestCreateOpts, error) { |
| |
| res, err := t.Resolve() |
| if err != nil { |
| return ManifestCreateOpts{}, err |
| } |
| |
| return ManifestCreateOpts{ |
| TgtBldr: t, |
| AppHash: appHash, |
| LoaderHash: loaderHash, |
| Version: ver, |
| BuildID: fmt.Sprintf("%x", appHash), |
| Syscfg: res.Cfg.SettingValues().ToMap(), |
| }, nil |
| } |
| |
| func CreateManifest(opts ManifestCreateOpts) (manifest.Manifest, error) { |
| t := opts.TgtBldr |
| |
| m := manifest.Manifest{ |
| Name: t.GetTarget().FullName(), |
| Date: time.Now().Format(time.RFC3339), |
| Version: opts.Version.String(), |
| BuildID: opts.BuildID, |
| Image: t.AppBuilder.AppImgPath(), |
| ImageHash: fmt.Sprintf("%x", opts.AppHash), |
| Syscfg: opts.Syscfg, |
| } |
| |
| rm := NewRepoManager() |
| for _, rpkg := range t.AppBuilder.SortedRpkgs() { |
| m.Pkgs = append(m.Pkgs, rm.GetManifestPkg(rpkg.Lpkg)) |
| } |
| |
| m.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 { |
| m.TgtVars = append(m.TgtVars, k+"="+vars[k]) |
| } |
| syscfgKV, err := t.GetTarget().Package().SyscfgY.GetValStringMapString( |
| "syscfg.vals", nil) |
| util.OneTimeWarningError(err) |
| |
| if len(syscfgKV) > 0 { |
| tgtSyscfg := fmt.Sprintf("target.syscfg=%s", |
| syscfg.KeyValueToStr(syscfgKV)) |
| m.TgtVars = append(m.TgtVars, tgtSyscfg) |
| } |
| |
| c, err := ManifestPkgSizes(t.AppBuilder) |
| if err == nil { |
| m.PkgSizes = c.Pkgs |
| } |
| |
| if t.LoaderBuilder != nil { |
| m.Loader = t.LoaderBuilder.AppImgPath() |
| m.LoaderHash = fmt.Sprintf("%x", opts.LoaderHash) |
| |
| for _, rpkg := range t.LoaderBuilder.SortedRpkgs() { |
| m.LoaderPkgs = append(m.LoaderPkgs, rm.GetManifestPkg(rpkg.Lpkg)) |
| } |
| |
| c, err = ManifestPkgSizes(t.LoaderBuilder) |
| if err == nil { |
| m.LoaderPkgSizes = c.Pkgs |
| } |
| } |
| |
| return m, nil |
| } |