| /** |
| * 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 cli |
| |
| import ( |
| "fmt" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "github.com/spf13/cobra" |
| "mynewt.apache.org/newt/newt/builder" |
| "mynewt.apache.org/newt/newt/imgprod" |
| "mynewt.apache.org/newt/newt/manifest" |
| "mynewt.apache.org/newt/newt/pkg" |
| "mynewt.apache.org/newt/newt/project" |
| "mynewt.apache.org/newt/newt/target" |
| "mynewt.apache.org/newt/util" |
| ) |
| |
| const TARGET_TEST_NAME = "unittest" |
| |
| var testablePkgMap map[*pkg.LocalPackage]struct{} |
| |
| func testablePkgs() map[*pkg.LocalPackage]struct{} { |
| if testablePkgMap != nil { |
| return testablePkgMap |
| } |
| |
| testablePkgMap := map[*pkg.LocalPackage]struct{}{} |
| |
| // Create a map of path => lclPkg. |
| proj, err := project.TryGetProject() |
| if err != nil { |
| return nil |
| } |
| |
| allPkgs := proj.PackagesOfType(-1) |
| pathLpkgMap := make(map[string]*pkg.LocalPackage, len(allPkgs)) |
| for _, p := range allPkgs { |
| lpkg := p.(*pkg.LocalPackage) |
| pathLpkgMap[lpkg.BasePath()] = lpkg |
| } |
| |
| // Add all unit test packages to the testable package map. |
| testPkgs := proj.PackagesOfType(pkg.PACKAGE_TYPE_UNITTEST) |
| for _, p := range testPkgs { |
| lclPack := p.(*pkg.LocalPackage) |
| testablePkgMap[lclPack] = struct{}{} |
| } |
| |
| // Next add first ancestor of each test package. |
| for _, testPkgItf := range testPkgs { |
| testPkg := testPkgItf.(*pkg.LocalPackage) |
| for cur := filepath.ToSlash(filepath.Dir(testPkg.BasePath())); cur != proj.BasePath; cur = filepath.ToSlash(filepath.Dir(cur)) { |
| lpkg := pathLpkgMap[cur] |
| if lpkg != nil && lpkg.Type() != pkg.PACKAGE_TYPE_UNITTEST { |
| testablePkgMap[lpkg] = struct{}{} |
| break |
| } |
| } |
| } |
| |
| return testablePkgMap |
| } |
| |
| func pkgToUnitTests(pack *pkg.LocalPackage) []*pkg.LocalPackage { |
| // If the user specified a unittest package, just test that one. |
| if pack.Type() == pkg.PACKAGE_TYPE_UNITTEST { |
| return []*pkg.LocalPackage{pack} |
| } |
| |
| // Otherwise, return all the package's direct descendants that are unit |
| // test packages. |
| result := []*pkg.LocalPackage{} |
| for p, _ := range testablePkgs() { |
| if p.Type() == pkg.PACKAGE_TYPE_UNITTEST && |
| strings.HasPrefix(p.FullName(), pack.FullName()) { |
| |
| result = append(result, p) |
| } |
| } |
| |
| return result |
| } |
| |
| var extraJtagCmd string |
| var noGDB_flag bool |
| var diffFriendly_flag bool |
| var imgFileOverride string |
| var elfFileOverride string |
| |
| func buildRunCmd(cmd *cobra.Command, args []string, printShellCmds bool, executeShell bool) { |
| if len(args) < 1 { |
| NewtUsage(cmd, nil) |
| } |
| |
| util.PrintShellCmds = printShellCmds |
| util.ExecuteShell = executeShell |
| |
| TryGetProject() |
| |
| // Verify and resolve each specified package. |
| targets, all, err := ResolveTargetsOrAll(args...) |
| if err != nil { |
| NewtUsage(cmd, err) |
| } |
| |
| if all { |
| // Collect all targets that specify an app package. |
| targets = []*target.Target{} |
| for _, name := range targetList() { |
| t := ResolveTarget(name) |
| if t != nil && t.AppName != "" { |
| targets = append(targets, t) |
| } |
| } |
| } |
| |
| for i, _ := range targets { |
| // Reset the global state for the next build. |
| // XXX: It is not good that this is necessary. This is certainly going |
| // to bite us... |
| if i > 0 { |
| if err := ResetGlobalState(); err != nil { |
| NewtUsage(nil, err) |
| } |
| } |
| |
| // Look up the target by name. This has to be done a second time here |
| // now that the project has been reset. |
| t := ResolveTarget(targets[i].FullName()) |
| if t == nil { |
| NewtUsage(nil, util.NewNewtError("Failed to resolve target: "+ |
| targets[i].Name())) |
| } |
| |
| util.StatusMessage(util.VERBOSITY_DEFAULT, "Building target %s\n", |
| t.FullName()) |
| |
| b, err := builder.NewTargetBuilder(t) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| if err := b.Build(); err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| // Produce bare "imageless" manifest. |
| mopts, err := manifest.OptsForNonImage(b) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| if err := imgprod.ProduceManifest(mopts); err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| util.StatusMessage(util.VERBOSITY_DEFAULT, |
| "Target successfully built: %s\n", t.Name()) |
| } |
| } |
| |
| func cleanDir(path string) { |
| util.StatusMessage(util.VERBOSITY_VERBOSE, |
| "Cleaning directory %s\n", path) |
| |
| err := os.RemoveAll(path) |
| if err != nil { |
| NewtUsage(nil, util.NewNewtError(err.Error())) |
| } |
| } |
| |
| func cleanRunCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 1 { |
| NewtUsage(cmd, util.NewNewtError("Must specify target")) |
| } |
| |
| TryGetProject() |
| |
| cleanAll := false |
| targets := []*target.Target{} |
| for _, arg := range args { |
| if arg == TARGET_KEYWORD_ALL { |
| cleanAll = true |
| } else { |
| t, _, err := ResolveTargetOrUnittest(arg) |
| if err != nil { |
| NewtUsage(cmd, err) |
| } |
| targets = append(targets, t) |
| } |
| } |
| |
| if cleanAll { |
| cleanDir(builder.BinRoot()) |
| } else { |
| for _, t := range targets { |
| cleanDir(builder.TargetBinDir(t.FullName())) |
| } |
| } |
| } |
| |
| func pkgnames(pkgs []*pkg.LocalPackage) string { |
| s := "" |
| |
| for _, p := range pkgs { |
| s += p.Name() + " " |
| } |
| |
| return s |
| } |
| |
| func testRunCmd(cmd *cobra.Command, args []string, exclude string, executeShell bool) { |
| if len(args) < 1 { |
| NewtUsage(cmd, nil) |
| } |
| |
| util.ExecuteShell = executeShell |
| |
| proj := TryGetProject() |
| |
| // Verify and resolve each specified package. |
| testAll := false |
| packs := []*pkg.LocalPackage{} |
| for _, pkgName := range args { |
| if pkgName == "all" { |
| testAll = true |
| } else { |
| pack, err := proj.ResolvePackage(proj.LocalRepo(), pkgName) |
| if err != nil { |
| NewtUsage(cmd, err) |
| } |
| |
| testPkgs := pkgToUnitTests(pack) |
| if len(testPkgs) == 0 { |
| NewtUsage(nil, util.FmtNewtError("Package %s contains no "+ |
| "unit tests", pack.FullName())) |
| } |
| |
| packs = append(packs, testPkgs...) |
| } |
| } |
| |
| if testAll { |
| packItfs := proj.PackagesOfType(pkg.PACKAGE_TYPE_UNITTEST) |
| packs = make([]*pkg.LocalPackage, len(packItfs)) |
| for i, p := range packItfs { |
| packs[i] = p.(*pkg.LocalPackage) |
| } |
| |
| packs = pkg.SortLclPkgs(packs) |
| } |
| |
| if len(exclude) > 0 { |
| // filter out excluded tests |
| orig := packs |
| packs = packs[:0] |
| excls := strings.Split(exclude, ",") |
| packLoop: |
| for _, pack := range orig { |
| for _, excl := range excls { |
| if pack.Name() == excl || |
| strings.HasPrefix(pack.Name(), excl+"/") || |
| pack.NameWithRepo() == excl || |
| strings.HasPrefix(pack.NameWithRepo(), excl+"/") { |
| continue packLoop |
| } |
| } |
| packs = append(packs, pack) |
| } |
| } |
| |
| if len(packs) == 0 { |
| NewtUsage(nil, util.NewNewtError("No testable packages found")) |
| } |
| |
| passedPkgs := []*pkg.LocalPackage{} |
| failedPkgs := []*pkg.LocalPackage{} |
| for _, pack := range packs { |
| // Reset the global state for the next test. |
| if err := ResetGlobalState(); err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| t, err := ResolveUnittest(pack.Name()) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| b, err := builder.NewTargetTester(t, pack) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| util.StatusMessage(util.VERBOSITY_DEFAULT, "Testing package %s\n", |
| pack.FullName()) |
| |
| err = b.SelfTestExecute() |
| if err == nil { |
| passedPkgs = append(passedPkgs, pack) |
| } else { |
| newtError := err.(*util.NewtError) |
| util.StatusMessage(util.VERBOSITY_QUIET, newtError.Text) |
| failedPkgs = append(failedPkgs, pack) |
| } |
| } |
| |
| passStr := fmt.Sprintf("Passed tests: [%s]", PackageNameList(passedPkgs)) |
| failStr := fmt.Sprintf("Failed tests: [%s]", PackageNameList(failedPkgs)) |
| |
| if len(failedPkgs) > 0 { |
| NewtUsage(nil, util.FmtNewtError("Test failure(s):\n%s\n%s", passStr, |
| failStr)) |
| } else { |
| util.StatusMessage(util.VERBOSITY_DEFAULT, "%s\n", passStr) |
| util.StatusMessage(util.VERBOSITY_DEFAULT, "All tests passed\n") |
| } |
| } |
| |
| func loadRunCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 1 { |
| NewtUsage(cmd, util.NewNewtError("Must specify target")) |
| } |
| |
| TryGetProject() |
| |
| t := ResolveTarget(args[0]) |
| if t == nil { |
| NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0])) |
| } |
| |
| b, err := builder.NewTargetBuilder(t) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| if err := b.Load(extraJtagCmd, imgFileOverride); err != nil { |
| NewtUsage(cmd, err) |
| } |
| } |
| |
| func debugRunCmd(cmd *cobra.Command, args []string) { |
| if len(args) < 1 { |
| NewtUsage(cmd, util.NewNewtError("Must specify target")) |
| } |
| |
| TryGetProject() |
| |
| t := ResolveTarget(args[0]) |
| if t == nil { |
| NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0])) |
| } |
| |
| b, err := builder.NewTargetBuilder(t) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| if err := b.Debug(extraJtagCmd, false, noGDB_flag, elfFileOverride); err != nil { |
| NewtUsage(cmd, err) |
| } |
| } |
| |
| func sizeRunCmd(cmd *cobra.Command, args []string, ram bool, flash bool, section string) { |
| if len(args) < 1 { |
| NewtUsage(cmd, util.NewNewtError("Must specify target")) |
| } |
| |
| TryGetProject() |
| |
| t := ResolveTarget(args[0]) |
| if t == nil { |
| NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0])) |
| } |
| |
| b, err := builder.NewTargetBuilder(t) |
| if err != nil { |
| NewtUsage(nil, err) |
| } |
| |
| var sections []string |
| |
| if ram { |
| sections = append(sections, "RAM") |
| } |
| |
| if flash { |
| sections = append(sections, "FLASH") |
| } |
| |
| if section != "" { |
| sections = append(sections, section) |
| } |
| |
| if len(sections) > 0 { |
| for _, sectionName := range sections { |
| if err := b.SizeReport(sectionName, diffFriendly_flag); err != nil { |
| NewtUsage(cmd, err) |
| } |
| } |
| |
| return |
| } |
| |
| if err := b.Size(); err != nil { |
| NewtUsage(cmd, err) |
| } |
| } |
| |
| func AddBuildCommands(cmd *cobra.Command) { |
| var printShellCmds bool |
| var executeShell bool |
| |
| buildCmd := &cobra.Command{ |
| Use: "build <target-name> [target-names...]", |
| Short: "Build one or more targets", |
| Run: func(cmd *cobra.Command, args []string) { |
| buildRunCmd(cmd, args, printShellCmds, executeShell) |
| }, |
| } |
| |
| buildCmd.Flags().BoolVarP(&printShellCmds, "printCmds", "p", false, |
| "Print executed build commands") |
| |
| buildCmd.Flags().BoolVar(&executeShell, "executeShell", false, |
| "Execute build command using /bin/sh (Linux and MacOS only)") |
| |
| cmd.AddCommand(buildCmd) |
| AddTabCompleteFn(buildCmd, func() []string { |
| return append(targetList(), "all") |
| }) |
| |
| cleanCmd := &cobra.Command{ |
| Use: "clean <target-name> [target-names...] | all", |
| Short: "Delete build artifacts for one or more targets", |
| Run: cleanRunCmd, |
| } |
| |
| cmd.AddCommand(cleanCmd) |
| AddTabCompleteFn(cleanCmd, func() []string { |
| return append(append(targetList(), unittestList()...), "all") |
| }) |
| |
| var exclude string |
| testCmd := &cobra.Command{ |
| Use: "test <package-name> [package-names...] | all", |
| Short: "Executes unit tests for one or more packages", |
| Run: func(cmd *cobra.Command, args []string) { |
| testRunCmd(cmd, args, exclude, executeShell) |
| }, |
| } |
| testCmd.Flags().StringVarP(&exclude, "exclude", "e", "", "Comma separated list of packages to exclude") |
| testCmd.Flags().BoolVar(&executeShell, "executeShell", false, |
| "Execute build command using /bin/sh (Linux and MacOS only)") |
| cmd.AddCommand(testCmd) |
| AddTabCompleteFn(testCmd, func() []string { |
| return append(testablePkgList(), "all", "allexcept") |
| }) |
| |
| loadHelpText := "Load application image on to the board for <target-name>" |
| |
| loadCmd := &cobra.Command{ |
| Use: "load <target-name>", |
| Short: "Load built target to board", |
| Long: loadHelpText, |
| Run: loadRunCmd, |
| } |
| |
| cmd.AddCommand(loadCmd) |
| AddTabCompleteFn(loadCmd, targetList) |
| |
| loadCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "", "", |
| "Extra commands to send to JTAG software") |
| loadCmd.PersistentFlags().StringVarP(&imgFileOverride, "imgfile", "", "", |
| "Path of .img file to load instead of target artifact") |
| |
| debugHelpText := "Open a debugger session for <target-name>" |
| |
| debugCmd := &cobra.Command{ |
| Use: "debug <target-name>", |
| Short: "Open debugger session to target", |
| Long: debugHelpText, |
| Run: debugRunCmd, |
| } |
| |
| debugCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "", |
| "", "Extra commands to send to JTAG software") |
| debugCmd.PersistentFlags().BoolVarP(&noGDB_flag, "noGDB", "n", false, |
| "Do not start GDB from command line") |
| debugCmd.PersistentFlags().StringVarP(&elfFileOverride, "elffile", "", |
| "", "Path of .elf file to debug instead of target artifact") |
| |
| cmd.AddCommand(debugCmd) |
| AddTabCompleteFn(debugCmd, targetList) |
| |
| sizeHelpText := "Calculate the size of target components specified by " + |
| "<target-name>." |
| |
| var ram, flash bool |
| var section string |
| sizeCmd := &cobra.Command{ |
| Use: "size <target-name>", |
| Short: "Size of target components", |
| Long: sizeHelpText, |
| Run: func(cmd *cobra.Command, args []string) { |
| sizeRunCmd(cmd, args, ram, flash, section) |
| }, |
| } |
| |
| sizeCmd.PersistentFlags().BoolVarP(&diffFriendly_flag, "diffable", "d", false, |
| "Produce diff-friendly output of statistics") |
| sizeCmd.Flags().BoolVarP(&ram, "ram", "R", false, "Print RAM statistics") |
| sizeCmd.Flags().BoolVarP(&flash, "flash", "F", false, |
| "Print FLASH statistics") |
| sizeCmd.Flags().StringVarP(§ion, "section", "S", "", "Print section statistics") |
| |
| cmd.AddCommand(sizeCmd) |
| AddTabCompleteFn(sizeCmd, targetList) |
| } |