From 5d23fac09cb4feb9f100e4603b07aa65652ecb0f Mon Sep 17 00:00:00 2001 From: Sourabh Mehta <73165318+soumeh01@users.noreply.github.com> Date: Mon, 19 Feb 2024 08:37:17 +0100 Subject: [PATCH] Call optionally cbuild2cmake instead of cbuildgen (#167) Addressing https://github.com/Open-CMSIS-Pack/cbuild/issues/165 --- cmd/cbuild/commands/build/buildcprj_test.go | 12 +- .../commands/list/list_contexts_test.go | 5 +- cmd/cbuild/commands/list/list_test.go | 3 +- .../commands/list/list_toolchains_test.go | 5 +- cmd/cbuild/commands/root.go | 45 ++-- cmd/cbuild/commands/root_test.go | 14 +- pkg/builder/cbuildidx/builder.go | 216 ++++++++++++++++ pkg/builder/cbuildidx/builder_test.go | 223 ++++++++++++++++ pkg/builder/cbuildidx/reader.go | 32 +++ pkg/builder/cbuildidx/reader_test.go | 31 +++ pkg/builder/cproject/builder.go | 203 ++++++--------- pkg/builder/cproject/builder_test.go | 108 ++++---- pkg/builder/csolution/builder.go | 144 +++++++---- pkg/builder/csolution/builder_test.go | 79 +++--- pkg/builder/interface.go | 111 ++++++-- pkg/inittest/inittest.go | 42 +-- pkg/utils/configs_test.go | 6 +- pkg/utils/utils_test.go | 12 +- test/data/Hello.Debug+AVH.cbuild.yml | 243 ++++++++++++++++++ test/data/Hello.cbuild-idx.yml | 13 + 20 files changed, 1190 insertions(+), 357 deletions(-) create mode 100644 pkg/builder/cbuildidx/builder.go create mode 100644 pkg/builder/cbuildidx/builder_test.go create mode 100644 pkg/builder/cbuildidx/reader.go create mode 100644 pkg/builder/cbuildidx/reader_test.go create mode 100644 test/data/Hello.Debug+AVH.cbuild.yml create mode 100644 test/data/Hello.cbuild-idx.yml diff --git a/cmd/cbuild/commands/build/buildcprj_test.go b/cmd/cbuild/commands/build/buildcprj_test.go index d757daf..1cc0b40 100644 --- a/cmd/cbuild/commands/build/buildcprj_test.go +++ b/cmd/cbuild/commands/build/buildcprj_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Arm Limited. All rights reserved. + * Copyright (c) 2023-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ package build_test import ( "os" + "path/filepath" "testing" "github.com/Open-CMSIS-Pack/cbuild/v2/cmd/cbuild/commands" @@ -16,10 +17,11 @@ import ( ) const testRoot = "../../../../test" +const testDir = "command" func TestBuildCPRJCommand(t *testing.T) { assert := assert.New(t) - cprjFile := testRoot + "/run/minimal.cprj" + cprjFile := filepath.Join(testRoot, testDir, "minimal.cprj") t.Run("multiple arguments", func(t *testing.T) { cmd := commands.NewRootCmd() @@ -38,9 +40,9 @@ func TestBuildCPRJCommand(t *testing.T) { func TestPreLogConfiguration(t *testing.T) { assert := assert.New(t) - logDir := testRoot + "/run/log" - logFile := logDir + "/test.log" - cprjFile := testRoot + "/run/minimal.cprj" + logDir := filepath.Join(testRoot, testDir, "log") + logFile := filepath.Join(logDir, "test.log") + cprjFile := filepath.Join(testRoot, testDir, "minimal.cprj") t.Run("test normal verbosity level", func(t *testing.T) { // No quiet, No debug diff --git a/cmd/cbuild/commands/list/list_contexts_test.go b/cmd/cbuild/commands/list/list_contexts_test.go index 4fa9c5d..ef8a01e 100644 --- a/cmd/cbuild/commands/list/list_contexts_test.go +++ b/cmd/cbuild/commands/list/list_contexts_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Arm Limited. All rights reserved. + * Copyright (c) 2023-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +7,7 @@ package list_test import ( + "path/filepath" "testing" "github.com/Open-CMSIS-Pack/cbuild/v2/cmd/cbuild/commands" @@ -15,7 +16,7 @@ import ( func TestListContextsCommand(t *testing.T) { assert := assert.New(t) - csolutionFile := testRoot + "/run/TestSolution/test.csolution.yml" + csolutionFile := filepath.Join(testRoot, testDir, "TestSolution/test.csolution.yml") t.Run("No arguments", func(t *testing.T) { cmd := commands.NewRootCmd() diff --git a/cmd/cbuild/commands/list/list_test.go b/cmd/cbuild/commands/list/list_test.go index 780684b..e71f37b 100644 --- a/cmd/cbuild/commands/list/list_test.go +++ b/cmd/cbuild/commands/list/list_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Arm Limited. All rights reserved. + * Copyright (c) 2023-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ import ( ) const testRoot = "../../../../test" +const testDir = "command" func TestListCommand(t *testing.T) { assert := assert.New(t) diff --git a/cmd/cbuild/commands/list/list_toolchains_test.go b/cmd/cbuild/commands/list/list_toolchains_test.go index 5ff23cd..4953c2b 100644 --- a/cmd/cbuild/commands/list/list_toolchains_test.go +++ b/cmd/cbuild/commands/list/list_toolchains_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Arm Limited. All rights reserved. + * Copyright (c) 2023-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +7,7 @@ package list_test import ( + "path/filepath" "testing" "github.com/Open-CMSIS-Pack/cbuild/v2/cmd/cbuild/commands" @@ -15,7 +16,7 @@ import ( func TestListToolchainsCommand(t *testing.T) { assert := assert.New(t) - csolutionFile := testRoot + "/run/TestSolution/test.csolution.yml" + csolutionFile := filepath.Join(testRoot, testDir, "TestSolution/test.csolution.yml") t.Run("invalid flag", func(t *testing.T) { cmd := commands.NewRootCmd() diff --git a/cmd/cbuild/commands/root.go b/cmd/cbuild/commands/root.go index 72b78e9..cd1d94a 100644 --- a/cmd/cbuild/commands/root.go +++ b/cmd/cbuild/commands/root.go @@ -129,29 +129,31 @@ func NewRootCmd() *cobra.Command { toolchain, _ := cmd.Flags().GetString("toolchain") useContextSet, _ := cmd.Flags().GetBool("context-set") frozenPacks, _ := cmd.Flags().GetBool("frozen-packs") + useCbuild2CMake, _ := cmd.Flags().GetBool("cbuild2cmake") options := builder.Options{ - IntDir: intDir, - OutDir: outDir, - LockFile: lockFile, - LogFile: logFile, - Generator: generator, - Target: target, - Jobs: jobs, - Quiet: quiet, - Debug: debug, - Verbose: verbose, - Clean: clean, - Schema: schema, - Packs: packs, - Rebuild: rebuild, - UpdateRte: updateRte, - Contexts: contexts, - UseContextSet: useContextSet, - Load: load, - Output: output, - Toolchain: toolchain, - FrozenPacks: frozenPacks, + IntDir: intDir, + OutDir: outDir, + LockFile: lockFile, + LogFile: logFile, + Generator: generator, + Target: target, + Jobs: jobs, + Quiet: quiet, + Debug: debug, + Verbose: verbose, + Clean: clean, + Schema: schema, + Packs: packs, + Rebuild: rebuild, + UpdateRte: updateRte, + Contexts: contexts, + UseContextSet: useContextSet, + Load: load, + Output: output, + Toolchain: toolchain, + FrozenPacks: frozenPacks, + UseCbuild2CMake: useCbuild2CMake, } configs, err := utils.GetInstallConfigs() @@ -208,6 +210,7 @@ func NewRootCmd() *cobra.Command { rootCmd.PersistentFlags().BoolP("schema", "s", false, "Validate project input file(s) against schema") rootCmd.PersistentFlags().StringP("log", "", "", "Save output messages in a log file") rootCmd.PersistentFlags().StringP("toolchain", "", "", "Input toolchain to be used") + rootCmd.Flags().BoolP("cbuild2cmake", "", false, "Use cbuild2cmake") // CPRJ specific hidden flags rootCmd.Flags().StringP("intdir", "i", "", "Set directory for intermediate files") diff --git a/cmd/cbuild/commands/root_test.go b/cmd/cbuild/commands/root_test.go index 5498a7d..1cda417 100644 --- a/cmd/cbuild/commands/root_test.go +++ b/cmd/cbuild/commands/root_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ package commands_test import ( "os" + "path/filepath" "testing" "github.com/Open-CMSIS-Pack/cbuild/v2/cmd/cbuild/commands" @@ -17,15 +18,16 @@ import ( ) const testRoot = "../../../test" +const testDir = "command" func init() { - inittest.TestInitialization(testRoot) + inittest.TestInitialization(testRoot, testDir) } func TestCommands(t *testing.T) { assert := assert.New(t) - cprjFile := testRoot + "/run/minimal.cprj" - csolutionFile := testRoot + "/run/test.csolution.yml" + cprjFile := filepath.Join(testRoot, testDir, "minimal.cprj") + csolutionFile := filepath.Join(testRoot, testDir, "test.csolution.yml") t.Run("test version", func(t *testing.T) { cmd := commands.NewRootCmd() @@ -72,8 +74,8 @@ func TestCommands(t *testing.T) { func TestPreLogConfiguration(t *testing.T) { assert := assert.New(t) - logDir := testRoot + "/run/log" - logFile := logDir + "/test.log" + logDir := filepath.Join(testRoot, testDir, "log") + logFile := filepath.Join(logDir, "test.log") t.Run("test normal verbosity level", func(t *testing.T) { // No quiet, No debug diff --git a/pkg/builder/cbuildidx/builder.go b/pkg/builder/cbuildidx/builder.go new file mode 100644 index 0000000..d9a34dc --- /dev/null +++ b/pkg/builder/cbuildidx/builder.go @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2024 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package cbuildidx + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + builder "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder" + utils "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/utils" + log "github.com/sirupsen/logrus" +) + +type CbuildIdxBuilder struct { + builder.BuilderParams +} + +func (b CbuildIdxBuilder) checkCbuildIdx() error { + fileName := filepath.Base(b.InputFile) + if !strings.HasSuffix(fileName, ".cbuild-idx.yml") { + err := errors.New(".cbuild-idx.yml file not found") + return err + } else { + if _, err := os.Stat(b.InputFile); os.IsNotExist(err) { + log.Error("cbuild-idx file " + b.InputFile + " does not exist") + return err + } + } + return nil +} + +func (b CbuildIdxBuilder) clean(dirs builder.BuildDirs, vars builder.InternalVars) (err error) { + removeDirectory := func(dir string) error { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return nil + } + args := []string{"-E", "remove_directory", dir} + _, err = b.Runner.ExecuteCommand(vars.CmakeBin, false, args...) + if err != nil { + log.Error("error executing 'cmake' clean for " + dir) + } + return err + } + + if err := removeDirectory(dirs.IntDir); err != nil { + return err + } + + if err := removeDirectory(dirs.OutDir); err != nil { + return err + } + + log.Info("clean finished successfully!") + return nil +} + +func (b CbuildIdxBuilder) getDirs(context string) (dirs builder.BuildDirs, err error) { + if _, err := os.Stat(b.InputFile); os.IsNotExist(err) { + log.Error("file " + b.InputFile + " does not exist") + return dirs, err + } + + if b.Options.OutDir != "" { + dirs.OutDir = b.Options.OutDir + } + + if b.Options.Output != "" { + dirs.IntDir = "" + dirs.OutDir = "" + } + + // cbuild2cmake generates cmake files under fixed tmp directory + dirs.IntDir = "tmp" + dirs.IntDir = filepath.Join(filepath.Dir(b.InputFile), dirs.IntDir) + + if dirs.OutDir == "" { + // get output directory from cbuild.yml file + path := filepath.Dir(b.InputFile) + cbuildFile := filepath.Join(path, context+".cbuild.yml") + _, outDir, err := GetBuildDirs(cbuildFile) + if err != nil { + log.Error("error parsing file: " + cbuildFile) + return dirs, err + } + + dirs.OutDir = outDir + if dirs.OutDir == "" { + dirs.OutDir = "OutDir" + } + if !filepath.IsAbs(dirs.OutDir) { + dirs.OutDir = filepath.Join(filepath.Dir(b.InputFile), dirs.OutDir) + } + } + + dirs.IntDir, _ = filepath.Abs(dirs.IntDir) + dirs.OutDir, _ = filepath.Abs(dirs.OutDir) + + log.Debug("dirs.IntDir: " + dirs.IntDir) + log.Debug("dirs.OutDir: " + dirs.OutDir) + + return dirs, err +} + +func (b CbuildIdxBuilder) Build() error { + b.InputFile, _ = filepath.Abs(b.InputFile) + err := b.checkCbuildIdx() + if err != nil { + return err + } + + vars, err := b.GetInternalVars() + if err != nil { + return err + } + + _ = utils.UpdateEnvVars(vars.BinPath, vars.EtcPath) + + if len(b.Options.Contexts) == 0 { + err = errors.New("error no context(s) to process") + return err + } + + dirs := builder.BuildDirs{ + IntDir: filepath.Join(filepath.Dir(b.InputFile), "tmp"), + } + + if b.Options.Clean { + for _, context := range b.Options.Contexts { + dirs, err := b.getDirs(context) + if err != nil { + return err + } + + log.Info("Cleaning context: \"" + context + "\"") + if err := b.clean(dirs, vars); err != nil { + return err + } + } + return nil + } + + args := []string{b.InputFile} + if b.Options.Debug { + log.Debug("cbuild2cmake command: " + vars.Cbuild2cmakeBin + " " + strings.Join(args, " ")) + } + + _, err = b.Runner.ExecuteCommand(vars.Cbuild2cmakeBin, false, args...) + if err != nil { + log.Error("error executing 'cbuild2cmake " + b.InputFile + "'") + return err + } + if _, err := os.Stat(dirs.IntDir + "/CMakeLists.txt"); errors.Is(err, os.ErrNotExist) { + return err + } + + if vars.CmakeBin == "" { + log.Error("cmake was not found") + return err + } + if b.Options.Generator == "" { + b.Options.Generator = "Ninja" + if vars.NinjaBin == "" { + log.Error("ninja was not found") + return err + } + } + + // CMake configuration command + args = []string{"-G", b.Options.Generator, "-S", dirs.IntDir, "-B", dirs.IntDir} + if b.Options.Debug { + args = append(args, "-Wdev") + } else { + args = append(args, "-Wno-dev") + } + + if b.Options.Debug { + log.Debug("cmake configuration command: " + vars.CmakeBin + " " + strings.Join(args, " ")) + } + + _, err = b.Runner.ExecuteCommand(vars.CmakeBin, b.Options.Quiet, args...) + if err != nil { + log.Error("error executing 'cmake' configuration") + return err + } + + // CMake build target(s) command + args = []string{"--build", dirs.IntDir, "-j", fmt.Sprintf("%d", b.GetJobs())} + for _, context := range b.Options.Contexts { + args = append(args, "--target", context) + args = append(args, "--target", context+"-database") + } + + if b.Options.Debug || b.Options.Verbose { + args = append(args, "--verbose") + } + + if b.Options.Debug { + log.Debug("cmake build command: " + vars.CmakeBin + " " + strings.Join(args, " ")) + } + + _, err = b.Runner.ExecuteCommand(vars.CmakeBin, false, args...) + if err != nil { + log.Error("error executing 'cmake' build") + return err + } + + log.Info("build finished successfully!") + return nil +} diff --git a/pkg/builder/cbuildidx/builder_test.go b/pkg/builder/cbuildidx/builder_test.go new file mode 100644 index 0000000..81fe5aa --- /dev/null +++ b/pkg/builder/cbuildidx/builder_test.go @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2024 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package cbuildidx + +import ( + "errors" + "os" + "path/filepath" + "strings" + "testing" + + builder "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder" + "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/inittest" + utils "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/utils" + "github.com/stretchr/testify/assert" +) + +const testRoot = "../../../test" +const testDir = "cbuildidx" + +func init() { + inittest.TestInitialization(testRoot, testDir) +} + +type RunnerMock struct{} + +func (r RunnerMock) ExecuteCommand(program string, quiet bool, args ...string) (string, error) { + if strings.Contains(program, "cbuild2cmake") { + _ = os.MkdirAll(filepath.Join(testRoot, testDir, "tmp"), 0755) + cmakelistFile := filepath.Join(testRoot, testDir, "tmp/CMakeLists.txt") + file, _ := os.Create(cmakelistFile) + defer file.Close() + } else if strings.Contains(program, "cpackget") { + } else if strings.Contains(program, "cmake") { + } else if strings.Contains(program, "ninja") { + } else if strings.Contains(program, "xmllint") { + } else { + return "", errors.New("invalid command") + } + return "", nil +} + +func TestCheckCbuildIdx(t *testing.T) { + assert := assert.New(t) + + b := CbuildIdxBuilder{ + builder.BuilderParams{ + Runner: RunnerMock{}, + }, + } + + t.Run("test valid cbuild-idx", func(t *testing.T) { + b.InputFile = filepath.Join(testRoot, testDir, "Hello.cbuild-idx.yml") + err := b.checkCbuildIdx() + assert.Nil(err) + }) + + t.Run("test existent file, invalid extension", func(t *testing.T) { + b.InputFile = filepath.Join(testRoot, testDir, "main.c") + err := b.checkCbuildIdx() + assert.Error(err) + }) + + t.Run("test invalid file", func(t *testing.T) { + b.InputFile = filepath.Join(testRoot, testDir, "invalid-file.cbuild-idx.yml") + err := b.checkCbuildIdx() + assert.Error(err) + }) +} + +func TestGetDirs(t *testing.T) { + assert := assert.New(t) + + b := CbuildIdxBuilder{ + builder.BuilderParams{ + Runner: RunnerMock{}, + InputFile: filepath.Join(testRoot, testDir, "Hello.cbuild-idx.yml"), + }, + } + + t.Run("test valid directories", func(t *testing.T) { + dirs, err := b.getDirs("Hello.Debug+AVH") + assert.Nil(err) + intDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "tmp")) + outDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "out/AVH")) + assert.Equal(intDir, dirs.IntDir) + assert.Equal(outDir, dirs.OutDir) + }) + + t.Run("test valid directories as arguments", func(t *testing.T) { + b.Options.IntDir = "cmdOptionsIntDir" + b.Options.OutDir = "cmdOptionsOutDir" + dirs, err := b.getDirs("Hello.Debug+AVH") + assert.Nil(err) + intDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "tmp")) + outDir, _ := filepath.Abs(b.Options.OutDir) + assert.Equal(intDir, dirs.IntDir) + assert.Equal(outDir, dirs.OutDir) + }) + + t.Run("test invalid cbuild-idx", func(t *testing.T) { + b.InputFile = filepath.Join(testRoot, testDir, "invalid-file.cbuild-idx.yml") + _, err := b.getDirs("Hello.Debug+AVH") + assert.Error(err) + }) +} + +func TestClean(t *testing.T) { + assert := assert.New(t) + + b := CbuildIdxBuilder{ + builder.BuilderParams{ + Runner: RunnerMock{}, + }, + } + var dirs builder.BuildDirs + var vars builder.InternalVars + + t.Run("test clean directories, invalid tool", func(t *testing.T) { + vars.CmakeBin = testRoot + "/bin/invalid-tool" + + dirs.OutDir = filepath.Join(testRoot, testDir, "OutDir") + _ = os.MkdirAll(dirs.OutDir, 0755) + err := b.clean(dirs, vars) + assert.Error(err) + + dirs.IntDir = filepath.Join(testRoot, testDir, "IntDir") + _ = os.MkdirAll(dirs.IntDir, 0755) + err = b.clean(dirs, vars) + assert.Error(err) + }) + + t.Run("test clean directories", func(t *testing.T) { + vars.CmakeBin = testRoot + "/bin/cmake" + dirs.IntDir = filepath.Join(testRoot, testDir, "tmp") + dirs.OutDir = filepath.Join(testRoot, testDir, "OutDir") + _ = os.MkdirAll(dirs.IntDir, 0755) + _ = os.MkdirAll(dirs.OutDir, 0755) + err := b.clean(dirs, vars) + assert.Nil(err) + }) +} + +func TestBuild(t *testing.T) { + assert := assert.New(t) + configs := inittest.GetTestConfigs(testRoot, testDir) + + b := CbuildIdxBuilder{ + builder.BuilderParams{ + Runner: RunnerMock{}, + InputFile: filepath.Join(testRoot, testDir, "Hello.cbuild-idx.yml"), + Options: builder.Options{ + Contexts: []string{"Hello.Debug+AVH"}, + OutDir: filepath.Join(testRoot, testDir, "OutDir"), + Packs: true, + }, + InstallConfigs: utils.Configurations{ + BinPath: configs.BinPath, + BinExtn: configs.BinExtn, + EtcPath: configs.EtcPath, + }, + }, + } + t.Run("test build cbuild-idx", func(t *testing.T) { + err := b.Build() + assert.Nil(err) + }) + + t.Run("test build cbuild-idx quiet", func(t *testing.T) { + b.Options.Quiet = true + err := b.Build() + assert.Nil(err) + }) + + t.Run("test build cbuild-idx debug", func(t *testing.T) { + b.Options.Debug = true + err := b.Build() + assert.Nil(err) + }) + + t.Run("test rebuild cbuild-idx", func(t *testing.T) { + b.Options.Rebuild = true + err := b.Build() + assert.Nil(err) + }) + + t.Run("test build log", func(t *testing.T) { + b.Options.LogFile = filepath.Join(testRoot, testDir, "log/test.log") + err := b.Build() + assert.Nil(err) + }) + + t.Run("test build jobs", func(t *testing.T) { + b.Options.Jobs = 1 + err := b.Build() + assert.Nil(err) + }) + + t.Run("test build verbose", func(t *testing.T) { + b.Options.Debug = false + b.Options.Verbose = true + err := b.Build() + assert.Nil(err) + }) + + t.Run("test build makefile generator", func(t *testing.T) { + b.Options.OutDir = filepath.Join(testRoot, testDir, "OutDir") + b.Options.Debug = true + b.Options.Generator = "Unix Makefiles" + err := b.Build() + assert.Nil(err) + }) + + t.Run("test clean cbuild-idx", func(t *testing.T) { + b.Options.Clean = true + err := b.Build() + assert.Nil(err) + }) +} diff --git a/pkg/builder/cbuildidx/reader.go b/pkg/builder/cbuildidx/reader.go new file mode 100644 index 0000000..0ba18ff --- /dev/null +++ b/pkg/builder/cbuildidx/reader.go @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package cbuildidx + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +type Cbuild struct { + Build struct { + OutputDirs struct { + Intdir string `yaml:"intdir"` + Outdir string `yaml:"outdir"` + } `yaml:"output-dirs"` + } `yaml:"build"` +} + +func GetBuildDirs(file string) (string, string, error) { + yfile, err := os.ReadFile(file) + if err != nil { + return "", "", err + } + data := Cbuild{} + err = yaml.Unmarshal(yfile, &data) + return data.Build.OutputDirs.Intdir, data.Build.OutputDirs.Outdir, err +} diff --git a/pkg/builder/cbuildidx/reader_test.go b/pkg/builder/cbuildidx/reader_test.go new file mode 100644 index 0000000..9c7e22c --- /dev/null +++ b/pkg/builder/cbuildidx/reader_test.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package cbuildidx + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetBuildDirs(t *testing.T) { + assert := assert.New(t) + t.Run("test get build directories when file is missing", func(t *testing.T) { + cbuildFile := filepath.Join(testRoot, testDir, "missing.cbuild.yml") + _, _, err := GetBuildDirs(cbuildFile) + assert.Error(err) + }) + + t.Run("test get build directories from .cbuild.yml", func(t *testing.T) { + cbuildFile := filepath.Join(testRoot, testDir, "Hello.Debug+AVH.cbuild.yml") + intDir, outDir, err := GetBuildDirs(cbuildFile) + assert.Nil(err) + assert.Equal("tmp/Hello/AVH/Debug", intDir) + assert.Equal("out/AVH", outDir) + }) +} diff --git a/pkg/builder/cproject/builder.go b/pkg/builder/cproject/builder.go index 3cfb23a..dc3e066 100644 --- a/pkg/builder/cproject/builder.go +++ b/pkg/builder/cproject/builder.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,9 +10,7 @@ import ( "errors" "fmt" "os" - "os/exec" "path/filepath" - "runtime" "strings" builder "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder" @@ -24,24 +22,6 @@ type CprjBuilder struct { builder.BuilderParams } -type BuildDirs struct { - intDir string - outDir string -} - -type InternalVars struct { - cprjPath string - cprjFilename string - packlistFile string - binPath string - etcPath string - cbuildgenBin string - xmllintBin string - cpackgetBin string - cmakeBin string - ninjaBin string -} - func (b CprjBuilder) checkCprj() error { if filepath.Ext(b.InputFile) != ".cprj" { err := errors.New("missing required argument .cprj") @@ -55,17 +35,40 @@ func (b CprjBuilder) checkCprj() error { return nil } -func (b CprjBuilder) getDirs() (dirs BuildDirs, err error) { +func (b CprjBuilder) clean(dirs builder.BuildDirs, vars builder.InternalVars) (err error) { + fileName := filepath.Base(b.InputFile) + fileName = fileName[:len(fileName)-len(filepath.Ext(fileName))] + log.Info("Cleaning context: \"" + fileName + "\"") + + if _, err := os.Stat(dirs.IntDir); !os.IsNotExist(err) { + _, err = b.Runner.ExecuteCommand(vars.CbuildgenBin, false, "rmdir", dirs.IntDir) + if err != nil { + log.Error("error executing 'cbuildgen rmdir'") + return err + } + } + if _, err := os.Stat(dirs.OutDir); !os.IsNotExist(err) { + _, err = b.Runner.ExecuteCommand(vars.CbuildgenBin, false, "rmdir", dirs.OutDir) + if err != nil { + log.Error("error executing 'cbuildgen rmdir'") + return err + } + } + log.Info("clean finished successfully!") + return nil +} + +func (b CprjBuilder) getDirs() (dirs builder.BuildDirs, err error) { if b.Options.IntDir != "" { - dirs.intDir = b.Options.IntDir + dirs.IntDir = b.Options.IntDir } if b.Options.OutDir != "" { - dirs.outDir = b.Options.OutDir + dirs.OutDir = b.Options.OutDir } if b.Options.Output != "" { - dirs.intDir = "" - dirs.outDir = "" + dirs.IntDir = "" + dirs.OutDir = "" } intDir, outDir, err := GetCprjDirs(b.InputFile) @@ -73,96 +76,34 @@ func (b CprjBuilder) getDirs() (dirs BuildDirs, err error) { log.Error("error parsing file: " + b.InputFile) return dirs, err } - if dirs.intDir == "" { - dirs.intDir = intDir - if dirs.intDir == "" { - dirs.intDir = "IntDir" + if dirs.IntDir == "" { + dirs.IntDir = intDir + if dirs.IntDir == "" { + dirs.IntDir = "IntDir" } - if !filepath.IsAbs(dirs.intDir) { - dirs.intDir = filepath.Join(filepath.Dir(b.InputFile), dirs.intDir) + if !filepath.IsAbs(dirs.IntDir) { + dirs.IntDir = filepath.Join(filepath.Dir(b.InputFile), dirs.IntDir) } } - if dirs.outDir == "" { - dirs.outDir = outDir - if dirs.outDir == "" { - dirs.outDir = "OutDir" + if dirs.OutDir == "" { + dirs.OutDir = outDir + if dirs.OutDir == "" { + dirs.OutDir = "OutDir" } - if !filepath.IsAbs(dirs.outDir) { - dirs.outDir = filepath.Join(filepath.Dir(b.InputFile), dirs.outDir) + if !filepath.IsAbs(dirs.OutDir) { + dirs.OutDir = filepath.Join(filepath.Dir(b.InputFile), dirs.OutDir) } } - dirs.intDir, _ = filepath.Abs(dirs.intDir) - dirs.outDir, _ = filepath.Abs(dirs.outDir) + dirs.IntDir, _ = filepath.Abs(dirs.IntDir) + dirs.OutDir, _ = filepath.Abs(dirs.OutDir) - log.Debug("dirs.intDir: " + dirs.intDir) - log.Debug("dirs.outDir: " + dirs.outDir) + log.Debug("dirs.IntDir: " + dirs.IntDir) + log.Debug("dirs.OutDir: " + dirs.OutDir) return dirs, err } -func (b CprjBuilder) clean(dirs BuildDirs, vars InternalVars) (err error) { - if _, err := os.Stat(dirs.intDir); !os.IsNotExist(err) { - _, err = b.Runner.ExecuteCommand(vars.cbuildgenBin, false, "rmdir", dirs.intDir) - if err != nil { - log.Error("error executing 'cbuildgen rmdir'") - return err - } - } - if _, err := os.Stat(dirs.outDir); !os.IsNotExist(err) { - _, err = b.Runner.ExecuteCommand(vars.cbuildgenBin, false, "rmdir", dirs.outDir) - if err != nil { - log.Error("error executing 'cbuildgen rmdir'") - return err - } - } - log.Info("clean finished successfully!") - return nil -} - -func (b CprjBuilder) getInternalVars() (vars InternalVars, err error) { - - vars.cprjPath = filepath.Dir(b.InputFile) - vars.cprjFilename = filepath.Base(b.InputFile) - vars.cprjFilename = strings.TrimSuffix(vars.cprjFilename, filepath.Ext(vars.cprjFilename)) - - vars.binPath = b.InstallConfigs.BinPath - vars.etcPath = b.InstallConfigs.EtcPath - - vars.cbuildgenBin = filepath.Join(vars.binPath, "cbuildgen"+b.InstallConfigs.BinExtn) - if _, err := os.Stat(vars.cbuildgenBin); os.IsNotExist(err) { - log.Error("cbuildgen was not found") - return vars, err - } - - cpackgetBin := filepath.Join(vars.binPath, "cpackget"+b.InstallConfigs.BinExtn) - if _, err := os.Stat(cpackgetBin); !os.IsNotExist(err) { - vars.cpackgetBin = cpackgetBin - } - - vars.xmllintBin, _ = exec.LookPath("xmllint") - vars.cmakeBin, _ = exec.LookPath("cmake") - vars.ninjaBin, _ = exec.LookPath("ninja") - - log.Debug("vars.binPath: " + vars.binPath) - log.Debug("vars.etcPath: " + vars.etcPath) - log.Debug("vars.cbuildgenBin: " + vars.cbuildgenBin) - log.Debug("vars.cpackgetBin: " + vars.cpackgetBin) - log.Debug("vars.xmllintBin: " + vars.xmllintBin) - log.Debug("vars.cmakeBin: " + vars.cmakeBin) - log.Debug("vars.ninjaBin: " + vars.ninjaBin) - - return vars, err -} - -func (b CprjBuilder) getJobs() (jobs int) { - jobs = runtime.NumCPU() - if b.Options.Jobs > 0 { - jobs = b.Options.Jobs - } - return jobs -} - func (b CprjBuilder) Build() error { b.InputFile, _ = filepath.Abs(b.InputFile) @@ -176,12 +117,12 @@ func (b CprjBuilder) Build() error { return err } - vars, err := b.getInternalVars() + vars, err := b.GetInternalVars() if err != nil { return err } - _ = utils.UpdateEnvVars(vars.binPath, vars.etcPath) + _ = utils.UpdateEnvVars(vars.BinPath, vars.EtcPath) if b.Options.Rebuild { err = b.clean(dirs, vars) @@ -193,10 +134,10 @@ func (b CprjBuilder) Build() error { } if b.Options.Schema { - if vars.xmllintBin == "" { + if vars.XmllintBin == "" { log.Warn("xmllint was not found, proceed without xml validation") } else { - _, err = b.Runner.ExecuteCommand(vars.xmllintBin, b.Options.Quiet, "--schema", filepath.Join(vars.etcPath, "CPRJ.xsd"), b.InputFile, "--noout") + _, err = b.Runner.ExecuteCommand(vars.XmllintBin, b.Options.Quiet, "--schema", filepath.Join(vars.EtcPath, "CPRJ.xsd"), b.InputFile, "--noout") if err != nil { log.Error("error executing 'xmllint'") return err @@ -204,38 +145,40 @@ func (b CprjBuilder) Build() error { } } - vars.packlistFile = filepath.Join(dirs.intDir, vars.cprjFilename+".cpinstall") - log.Debug("vars.packlistFile: " + vars.packlistFile) - _ = os.Remove(vars.packlistFile) - _ = os.MkdirAll(dirs.intDir, 0755) + cprjFilename := filepath.Base(b.InputFile) + cprjFilename = strings.TrimSuffix(cprjFilename, filepath.Ext(cprjFilename)) + packlistFile := filepath.Join(dirs.IntDir, cprjFilename+".cpinstall") + log.Debug("vars.packlistFile: " + packlistFile) + _ = os.Remove(packlistFile) + _ = os.MkdirAll(dirs.IntDir, 0755) var args []string - args = []string{"packlist", b.InputFile, "--outdir=" + dirs.outDir, "--intdir=" + dirs.intDir} + args = []string{"packlist", b.InputFile, "--outdir=" + dirs.OutDir, "--intdir=" + dirs.IntDir} if b.Options.Quiet { args = append(args, "--quiet") } if b.Options.UpdateRte { args = append(args, "--update-rte") } - _, err = b.Runner.ExecuteCommand(vars.cbuildgenBin, false, args...) + _, err = b.Runner.ExecuteCommand(vars.CbuildgenBin, false, args...) if err != nil { log.Error("error executing 'cbuildgen packlist'") return err } - if _, err := os.Stat(vars.packlistFile); !os.IsNotExist(err) { + if _, err := os.Stat(packlistFile); !os.IsNotExist(err) { if b.Options.Packs { - if vars.cpackgetBin == "" { + if vars.CpackgetBin == "" { err := errors.New("cpackget was not found, missing packs cannot be downloaded") return err } - args = []string{"add", "--agree-embedded-license", "--no-dependencies", "--packs-list-filename", vars.packlistFile} + args = []string{"add", "--agree-embedded-license", "--no-dependencies", "--packs-list-filename", packlistFile} if b.Options.Debug { args = append(args, "--verbose") } else if b.Options.Quiet { args = append(args, "--quiet") } - _, err = b.Runner.ExecuteCommand(vars.cpackgetBin, b.Options.Quiet, args...) + _, err = b.Runner.ExecuteCommand(vars.CpackgetBin, b.Options.Quiet, args...) if err != nil { log.Error("error executing 'cpackget add'") return err @@ -247,7 +190,7 @@ func (b CprjBuilder) Build() error { } } - args = []string{"cmake", b.InputFile, "--outdir=" + dirs.outDir, "--intdir=" + dirs.intDir} + args = []string{"cmake", b.InputFile, "--outdir=" + dirs.OutDir, "--intdir=" + dirs.IntDir} if b.Options.Quiet { args = append(args, "--quiet") } @@ -257,33 +200,33 @@ func (b CprjBuilder) Build() error { } if b.Options.Debug { - log.Debug("cbuildgen command: " + vars.cbuildgenBin + " " + strings.Join(args, " ")) + log.Debug("cbuildgen command: " + vars.CbuildgenBin + " " + strings.Join(args, " ")) } - _, err = b.Runner.ExecuteCommand(vars.cbuildgenBin, false, args...) + _, err = b.Runner.ExecuteCommand(vars.CbuildgenBin, false, args...) if err != nil { log.Error("error executing 'cbuildgen cmake'") return err } - if _, err := os.Stat(dirs.intDir + "/CMakeLists.txt"); errors.Is(err, os.ErrNotExist) { + if _, err := os.Stat(dirs.IntDir + "/CMakeLists.txt"); errors.Is(err, os.ErrNotExist) { return err } - if vars.cmakeBin == "" { + if vars.CmakeBin == "" { log.Error("cmake was not found") return err } if b.Options.Generator == "" { b.Options.Generator = "Ninja" - if vars.ninjaBin == "" { + if vars.NinjaBin == "" { log.Error("ninja was not found") return err } } - args = []string{"-G", b.Options.Generator, "-S", dirs.intDir, "-B", dirs.intDir} + args = []string{"-G", b.Options.Generator, "-S", dirs.IntDir, "-B", dirs.IntDir} if b.Options.Debug { args = append(args, "-Wdev") } else { @@ -291,16 +234,16 @@ func (b CprjBuilder) Build() error { } if b.Options.Debug { - log.Debug("cmake configuration command: " + vars.cmakeBin + " " + strings.Join(args, " ")) + log.Debug("cmake configuration command: " + vars.CmakeBin + " " + strings.Join(args, " ")) } - _, err = b.Runner.ExecuteCommand(vars.cmakeBin, b.Options.Quiet, args...) + _, err = b.Runner.ExecuteCommand(vars.CmakeBin, b.Options.Quiet, args...) if err != nil { log.Error("error executing 'cmake' configuration") return err } - args = []string{"--build", dirs.intDir, "-j", fmt.Sprintf("%d", b.getJobs())} + args = []string{"--build", dirs.IntDir, "-j", fmt.Sprintf("%d", b.GetJobs())} if b.Options.Target != "" { args = append(args, "--target", b.Options.Target) } @@ -309,10 +252,10 @@ func (b CprjBuilder) Build() error { } if b.Options.Debug { - log.Debug("cmake build command: " + vars.cmakeBin + " " + strings.Join(args, " ")) + log.Debug("cmake build command: " + vars.CmakeBin + " " + strings.Join(args, " ")) } - _, err = b.Runner.ExecuteCommand(vars.cmakeBin, false, args...) + _, err = b.Runner.ExecuteCommand(vars.CmakeBin, false, args...) if err != nil { log.Error("error executing 'cmake' build") return err diff --git a/pkg/builder/cproject/builder_test.go b/pkg/builder/cproject/builder_test.go index 374e9e2..1bb820a 100644 --- a/pkg/builder/cproject/builder_test.go +++ b/pkg/builder/cproject/builder_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,16 +14,17 @@ import ( "strings" "testing" - "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder" + builder "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder" "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/inittest" - "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/utils" + utils "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/utils" "github.com/stretchr/testify/assert" ) const testRoot = "../../../test" +const testDir = "cproject" func init() { - inittest.TestInitialization(testRoot) + inittest.TestInitialization(testRoot, testDir) } type RunnerMock struct{} @@ -31,11 +32,11 @@ type RunnerMock struct{} func (r RunnerMock) ExecuteCommand(program string, quiet bool, args ...string) (string, error) { if strings.Contains(program, "cbuildgen") { if args[0] == "packlist" { - packlistFile := testRoot + "/run/IntDir/minimal.cpinstall" + packlistFile := filepath.Join(testRoot, testDir, "IntDir/minimal.cpinstall") file, _ := os.Create(packlistFile) defer file.Close() } else if args[0] == "cmake" { - cmakelistFile := testRoot + "/run/IntDir/CMakeLists.txt" + cmakelistFile := filepath.Join(testRoot, testDir, "IntDir/CMakeLists.txt") file, _ := os.Create(cmakelistFile) defer file.Close() } @@ -59,19 +60,19 @@ func TestCheckCprj(t *testing.T) { } t.Run("test valid cprj", func(t *testing.T) { - b.InputFile = testRoot + "/run/minimal.cprj" + b.InputFile = filepath.Join(testRoot, testDir, "minimal.cprj") err := b.checkCprj() assert.Nil(err) }) t.Run("test existent file, invalid extension", func(t *testing.T) { - b.InputFile = testRoot + "/run/main.c" + b.InputFile = filepath.Join(testRoot, testDir, "main.c") err := b.checkCprj() assert.Error(err) }) t.Run("test invalid file", func(t *testing.T) { - b.InputFile = testRoot + "/run/invalid-file.cprj" + b.InputFile = filepath.Join(testRoot, testDir, "invalid-file.cprj") err := b.checkCprj() assert.Error(err) }) @@ -87,39 +88,39 @@ func TestGetDirs(t *testing.T) { } t.Run("test default directories", func(t *testing.T) { - b.InputFile = testRoot + "/run/minimal.cprj" + b.InputFile = filepath.Join(testRoot, testDir, "minimal.cprj") dirs, err := b.getDirs() assert.Nil(err) - intDir, _ := filepath.Abs(testRoot + "/run/IntDir") - outDir, _ := filepath.Abs(testRoot + "/run/OutDir") - assert.Equal(intDir, dirs.intDir) - assert.Equal(outDir, dirs.outDir) + intDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "IntDir")) + outDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "OutDir")) + assert.Equal(intDir, dirs.IntDir) + assert.Equal(outDir, dirs.OutDir) }) t.Run("test valid directories in cprj", func(t *testing.T) { - b.InputFile = testRoot + "/run/minimal-dirs.cprj" + b.InputFile = filepath.Join(testRoot, testDir, "minimal-dirs.cprj") dirs, err := b.getDirs() assert.Nil(err) - intDir, _ := filepath.Abs(testRoot + "/run/Intermediate") - outDir, _ := filepath.Abs(testRoot + "/run/Output") - assert.Equal(intDir, dirs.intDir) - assert.Equal(outDir, dirs.outDir) + intDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "Intermediate")) + outDir, _ := filepath.Abs(filepath.Join(testRoot, testDir, "Output")) + assert.Equal(intDir, dirs.IntDir) + assert.Equal(outDir, dirs.OutDir) }) t.Run("test valid directories as arguments", func(t *testing.T) { - b.InputFile = testRoot + "/run/minimal.cprj" + b.InputFile = filepath.Join(testRoot, testDir, "minimal.cprj") b.Options.IntDir = "cmdOptionsIntDir" b.Options.OutDir = "cmdOptionsOutDir" dirs, err := b.getDirs() assert.Nil(err) intDir, _ := filepath.Abs(b.Options.IntDir) outDir, _ := filepath.Abs(b.Options.OutDir) - assert.Equal(intDir, dirs.intDir) - assert.Equal(outDir, dirs.outDir) + assert.Equal(intDir, dirs.IntDir) + assert.Equal(outDir, dirs.OutDir) }) t.Run("test invalid cprj", func(t *testing.T) { - b.InputFile = testRoot + "/run/invalid-file.cprj" + b.InputFile = filepath.Join(testRoot, testDir, "invalid-file.cprj") _, err := b.getDirs() assert.Error(err) }) @@ -133,36 +134,36 @@ func TestClean(t *testing.T) { Runner: RunnerMock{}, }, } - var dirs BuildDirs - var vars InternalVars + var dirs builder.BuildDirs + var vars builder.InternalVars t.Run("test clean directories, invalid tool", func(t *testing.T) { - vars.cbuildgenBin = testRoot + "/bin/invalid-tool" + vars.CbuildgenBin = testRoot + "/bin/invalid-tool" - dirs.outDir = testRoot + "/run/OutDir" - _ = os.MkdirAll(dirs.outDir, 0755) + dirs.OutDir = filepath.Join(testRoot, testDir, "OutDir") + _ = os.MkdirAll(dirs.OutDir, 0755) err := b.clean(dirs, vars) assert.Error(err) - dirs.intDir = testRoot + "/run/IntDir" - _ = os.MkdirAll(dirs.intDir, 0755) + dirs.IntDir = filepath.Join(testRoot, testDir, "IntDir") + _ = os.MkdirAll(dirs.IntDir, 0755) err = b.clean(dirs, vars) assert.Error(err) }) t.Run("test clean directories", func(t *testing.T) { - vars.cbuildgenBin = testRoot + "/bin/cbuildgen" - dirs.intDir = testRoot + "/run/IntDir" - dirs.outDir = testRoot + "/run/OutDir" - _ = os.MkdirAll(dirs.intDir, 0755) - _ = os.MkdirAll(dirs.outDir, 0755) + vars.CbuildgenBin = testRoot + "/bin/cbuildgen" + dirs.IntDir = filepath.Join(testRoot, testDir, "IntDir") + dirs.OutDir = filepath.Join(testRoot, testDir, "OutDir") + _ = os.MkdirAll(dirs.IntDir, 0755) + _ = os.MkdirAll(dirs.OutDir, 0755) err := b.clean(dirs, vars) assert.Nil(err) }) t.Run("test clean non-existent directories", func(t *testing.T) { - dirs.intDir = testRoot + "/run/non-existent-intdir" - dirs.outDir = testRoot + "/run/non-existent-outdir" + dirs.IntDir = filepath.Join(testRoot, testDir, "non-existent-intdir") + dirs.OutDir = filepath.Join(testRoot, testDir, "non-existent-outdir") err := b.clean(dirs, vars) assert.Nil(err) }) @@ -173,12 +174,12 @@ func TestGetInternalVars(t *testing.T) { b := CprjBuilder{ builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/minimal.cprj", + InputFile: filepath.Join(testRoot, testDir, "minimal.cprj"), }, } t.Run("test get internal vars", func(t *testing.T) { - _, err := b.getInternalVars() + _, err := b.GetInternalVars() assert.Error(err) }) } @@ -193,34 +194,34 @@ func TestGetJobs(t *testing.T) { t.Run("test get jobs = 0", func(t *testing.T) { b.Options.Jobs = 0 - jobs := b.getJobs() + jobs := b.GetJobs() assert.Equal(jobs, runtime.NumCPU()) }) t.Run("test get jobs > 0", func(t *testing.T) { b.Options.Jobs = 2 - jobs := b.getJobs() + jobs := b.GetJobs() assert.Equal(jobs, 2) }) t.Run("test get jobs < 0", func(t *testing.T) { b.Options.Jobs = -1 - jobs := b.getJobs() + jobs := b.GetJobs() assert.Equal(jobs, runtime.NumCPU()) }) } func TestBuild(t *testing.T) { assert := assert.New(t) - configs := inittest.GetTestConfigs(testRoot) + configs := inittest.GetTestConfigs(testRoot, testDir) b := CprjBuilder{ builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/minimal.cprj", + InputFile: filepath.Join(testRoot, testDir, "minimal.cprj"), Options: builder.Options{ - IntDir: testRoot + "/run/IntDir", - OutDir: testRoot + "/run/OutDir", + IntDir: filepath.Join(testRoot, testDir, "IntDir"), + OutDir: filepath.Join(testRoot, testDir, "OutDir"), Packs: true, }, InstallConfigs: utils.Configurations{ @@ -232,7 +233,6 @@ func TestBuild(t *testing.T) { } t.Run("test build cprj", func(t *testing.T) { - err := b.Build() assert.Nil(err) }) @@ -262,13 +262,13 @@ func TestBuild(t *testing.T) { }) t.Run("test build lock file", func(t *testing.T) { - b.Options.LockFile = testRoot + "/run/lockfile.cprj" + b.Options.LockFile = filepath.Join(testRoot, testDir, "lockfile.cprj") err := b.Build() assert.Nil(err) }) t.Run("test build log", func(t *testing.T) { - b.Options.LogFile = testRoot + "/run/log/test.log" + b.Options.LogFile = filepath.Join(testRoot, testDir, "log/test.log") err := b.Build() assert.Nil(err) }) @@ -299,8 +299,8 @@ func TestBuild(t *testing.T) { }) t.Run("test build makefile generator", func(t *testing.T) { - b.Options.IntDir = testRoot + "/run/IntDir" - b.Options.OutDir = testRoot + "/run/OutDir" + b.Options.IntDir = filepath.Join(testRoot, testDir, "IntDir") + b.Options.OutDir = filepath.Join(testRoot, testDir, "OutDir") b.Options.Debug = true b.Options.Generator = "Unix Makefiles" err := b.Build() @@ -319,10 +319,10 @@ func TestBuildFail(t *testing.T) { b := CprjBuilder{ builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/minimal.cprj", + InputFile: filepath.Join(testRoot, testDir, "minimal.cprj"), Options: builder.Options{ - IntDir: testRoot + "/run/IntDir", - OutDir: testRoot + "/run/OutDir", + IntDir: filepath.Join(testRoot, testDir, "IntDir"), + OutDir: filepath.Join(testRoot, testDir, "OutDir"), }, }, } diff --git a/pkg/builder/csolution/builder.go b/pkg/builder/csolution/builder.go index 4e28957..1886fe4 100644 --- a/pkg/builder/csolution/builder.go +++ b/pkg/builder/csolution/builder.go @@ -16,6 +16,7 @@ import ( "strings" builder "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder" + "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder/cbuildidx" "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/builder/cproject" utils "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/utils" log "github.com/sirupsen/logrus" @@ -193,74 +194,121 @@ func (b CSolutionBuilder) getSetFilePath() (string, error) { return setFilePath, nil } -func (b CSolutionBuilder) getCprjsBuilders(selectedContexts []string) (cprjBuilders []cproject.CprjBuilder, err error) { - for _, context := range selectedContexts { - infoMsg := "Retrieve build information for context: \"" + context + "\"" - log.Info(infoMsg) - - // if --output is used, ignore provided --outdir and --intdir - if b.Options.Output != "" && (b.Options.OutDir != "" || b.Options.IntDir != "") { - log.Warn("output files are generated under: \"" + - b.Options.Output + "\". Options --outdir and --intdir shall be ignored.") - } +func (b CSolutionBuilder) getProjsBuilders(selectedContexts []string) (projBuilders []builder.IBuilderInterface, err error) { + buildOptions := b.Options - idxFile, err := b.getIdxFilePath() - if err != nil { - return cprjBuilders, err - } + // Set XML schema check to false, when input is yml + if b.Options.Schema { + buildOptions.Schema = false + } - cprjFile, err := b.getCprjFilePath(idxFile, context) - if err != nil { - log.Error("error getting cprj file: " + err.Error()) - return cprjBuilders, err - } + idxFile, err := b.getIdxFilePath() + if err != nil { + return projBuilders, err + } - // get cprj builder - cprjBuilder := cproject.CprjBuilder{ + var projBuilder builder.IBuilderInterface + if b.Options.UseCbuild2CMake { + buildOptions.Contexts = selectedContexts + // get idx builder + projBuilder = cbuildidx.CbuildIdxBuilder{ BuilderParams: builder.BuilderParams{ Runner: b.Runner, - Options: b.Options, - InputFile: cprjFile, + Options: buildOptions, + InputFile: idxFile, InstallConfigs: b.InstallConfigs, }, } + projBuilders = append(projBuilders, projBuilder) + } else { + for _, context := range selectedContexts { + infoMsg := "Retrieve build information for context: \"" + context + "\"" + log.Info(infoMsg) + + // if --output is used, ignore provided --outdir and --intdir + if b.Options.Output != "" && (b.Options.OutDir != "" || b.Options.IntDir != "") { + log.Warn("output files are generated under: \"" + + b.Options.Output + "\". Options --outdir and --intdir shall be ignored.") + } - // Set XML schema check to false, when input is yml - if b.Options.Schema { - cprjBuilder.Options.Schema = false - } + cprjFile, err := b.getCprjFilePath(idxFile, context) + if err != nil { + log.Error("error getting cprj file: " + err.Error()) + return projBuilders, err + } - cprjBuilders = append(cprjBuilders, cprjBuilder) + // get cprj builder + projBuilder = cproject.CprjBuilder{ + BuilderParams: builder.BuilderParams{ + Runner: b.Runner, + Options: buildOptions, + InputFile: cprjFile, + InstallConfigs: b.InstallConfigs, + }, + } + projBuilders = append(projBuilders, projBuilder) + } } - return cprjBuilders, err + return projBuilders, err } -func (b CSolutionBuilder) cleanContexts(selectedContexts []string, cprjBuilders []cproject.CprjBuilder) (err error) { - for index, cprjBuilder := range cprjBuilders { - infoMsg := "Cleaning context: \"" + selectedContexts[index] + "\"" - log.Info(infoMsg) +func (b CSolutionBuilder) setBuilderOptions(builder *builder.IBuilderInterface, clean bool) { + if b.Options.UseCbuild2CMake { + idxBuilder := (*builder).(cbuildidx.CbuildIdxBuilder) + idxBuilder.Options.Rebuild = false + idxBuilder.Options.Clean = clean + (*builder) = idxBuilder + } else { + cprjBuilder := (*builder).(cproject.CprjBuilder) cprjBuilder.Options.Rebuild = false - cprjBuilder.Options.Clean = true - err = cprjBuilder.Build() + cprjBuilder.Options.Clean = clean + (*builder) = cprjBuilder + } +} + +func (b CSolutionBuilder) getBuilderInputFile(builder builder.IBuilderInterface) string { + var inputFile string + if b.Options.UseCbuild2CMake { + idxBuilder := builder.(cbuildidx.CbuildIdxBuilder) + inputFile = idxBuilder.InputFile + } else { + cprjBuilder := builder.(cproject.CprjBuilder) + inputFile = cprjBuilder.InputFile + } + return inputFile +} + +// func (b CSolutionBuilder) cleanContexts(selectedContexts []string, projBuilders []builder.IBuilderInterface) (err error) { +func (b CSolutionBuilder) cleanContexts(projBuilders []builder.IBuilderInterface) (err error) { + for index := range projBuilders { + // infoMsg := "Cleaning context: \"" + selectedContexts[index] + "\"" + // log.Info(infoMsg) + + b.setBuilderOptions(&projBuilders[index], true) + err = projBuilders[index].Build() if err != nil { - log.Error("error cleaning '" + cprjBuilder.InputFile + "'") + log.Error("error cleaning '" + b.getBuilderInputFile(projBuilders[index]) + "'") } } return } -func (b CSolutionBuilder) buildContexts(selectedContexts []string, cprjBuilders []cproject.CprjBuilder) (err error) { - for index, cprjBuilder := range cprjBuilders { - progress := fmt.Sprintf("(%s/%d)", strconv.Itoa(index+1), len(selectedContexts)) - infoMsg := progress + " Building context: \"" + selectedContexts[index] + "\"" +func (b CSolutionBuilder) buildContexts(selectedContexts []string, projBuilders []builder.IBuilderInterface) (err error) { + for index := range projBuilders { + var infoMsg string + if b.Options.UseContextSet { + infoMsg = " Building " + b.InputFile + } else { + progress := fmt.Sprintf("(%s/%d)", strconv.Itoa(index+1), len(selectedContexts)) + infoMsg = progress + " Building context: \"" + selectedContexts[index] + "\"" + } sep := strings.Repeat("=", len(infoMsg)+13) + "\n" _, _ = log.StandardLogger().Out.Write([]byte(sep)) log.Info(infoMsg) - cprjBuilder.Options.Rebuild = false - cprjBuilder.Options.Clean = false - err = cprjBuilder.Build() + b.setBuilderOptions(&projBuilders[index], false) + err = projBuilders[index].Build() if err != nil { - log.Error("error building '" + cprjBuilder.InputFile + "'") + log.Error("error building '" + b.getBuilderInputFile(projBuilders[index]) + "'") } } return @@ -431,21 +479,21 @@ func (b CSolutionBuilder) Build() (err error) { totalContexts := strconv.Itoa(len(selectedContexts)) log.Info("Processing " + totalContexts + " context(s)") - // get cprj builder for each selected context - cprjsBuilders, err := b.getCprjsBuilders(selectedContexts) + // get builder for each selected context + projBuilders, err := b.getProjsBuilders(selectedContexts) if err != nil { return err } // clean all selected contexts when rebuild or clean are requested if b.Options.Rebuild || b.Options.Clean { - err = b.cleanContexts(selectedContexts, cprjsBuilders) + err = b.cleanContexts(projBuilders) if b.Options.Clean || err != nil { return err } } // build all selected contexts - err = b.buildContexts(selectedContexts, cprjsBuilders) + err = b.buildContexts(selectedContexts, projBuilders) return err } diff --git a/pkg/builder/csolution/builder_test.go b/pkg/builder/csolution/builder_test.go index 7f14118..409b8a8 100644 --- a/pkg/builder/csolution/builder_test.go +++ b/pkg/builder/csolution/builder_test.go @@ -20,9 +20,13 @@ import ( ) const testRoot = "../../../test" +const testDir = "csolution" + +var configs inittest.TestConfigs func init() { - inittest.TestInitialization(testRoot) + inittest.TestInitialization(testRoot, testDir) + configs = inittest.GetTestConfigs(testRoot, testDir) } type RunnerMock struct{} @@ -55,12 +59,10 @@ func (r RunnerMock) ExecuteCommand(program string, quiet bool, args ...string) ( func TestListContexts(t *testing.T) { assert := assert.New(t) - configs := inittest.GetTestConfigs(testRoot) - b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/TestSolution/test.csolution.yml", + InputFile: filepath.Join(testRoot, testDir, "TestSolution/test.csolution.yml"), InstallConfigs: utils.Configurations{ BinPath: configs.BinPath, BinExtn: configs.BinExtn, @@ -119,11 +121,10 @@ func TestListContexts(t *testing.T) { func TestListToolchians(t *testing.T) { assert := assert.New(t) - configs := inittest.GetTestConfigs(testRoot) b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/test.csolution.yml", + InputFile: filepath.Join(testRoot, testDir, "test.csolution.yml"), InstallConfigs: utils.Configurations{ BinPath: configs.BinPath, BinExtn: configs.BinExtn, @@ -188,7 +189,6 @@ func TestListToolchians(t *testing.T) { func TestListEnvironment(t *testing.T) { assert := assert.New(t) - configs := inittest.GetTestConfigs(testRoot) b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, @@ -233,15 +233,13 @@ func TestListEnvironment(t *testing.T) { func TestBuild(t *testing.T) { assert := assert.New(t) - os.Setenv("CMSIS_PACK_ROOT", testRoot+"/run/packs") - configs := inittest.GetTestConfigs(testRoot) + os.Setenv("CMSIS_PACK_ROOT", filepath.Join(testRoot, testDir, "packs")) b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/Test.csolution.yml", + InputFile: filepath.Join(testRoot, testDir, "Test.csolution.yml"), Options: builder.Options{ - //IntDir: testRoot + "/run/IntDir", - OutDir: testRoot + "/run/OutDir", + OutDir: filepath.Join(testRoot, testDir, "OutDir"), Packs: true, }, InstallConfigs: utils.Configurations{ @@ -262,18 +260,24 @@ func TestBuild(t *testing.T) { err := b.Build() assert.Error(err) }) + + t.Run("test build csolution using cbuild2cmake", func(t *testing.T) { + b.Options.Contexts = []string{"test.Debug+CM0"} + b.Options.UseCbuild2CMake = true + err := b.Build() + assert.Error(err) + }) } func TestRebuild(t *testing.T) { assert := assert.New(t) - os.Setenv("CMSIS_PACK_ROOT", testRoot+"/run/packs") - configs := inittest.GetTestConfigs(testRoot) + os.Setenv("CMSIS_PACK_ROOT", filepath.Join(testRoot, testDir, "packs")) b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/Test.csolution.yml", + InputFile: filepath.Join(testRoot, testDir, "Test.csolution.yml"), Options: builder.Options{ - OutDir: testRoot + "/run/OutDir", + OutDir: filepath.Join(testRoot, testDir, "OutDir"), Packs: true, Rebuild: true, }, @@ -293,8 +297,6 @@ func TestRebuild(t *testing.T) { func TestInstallMissingPacks(t *testing.T) { assert := assert.New(t) - configs := inittest.GetTestConfigs(testRoot) - b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, @@ -322,8 +324,7 @@ func TestInstallMissingPacks(t *testing.T) { func TestGetCprjFilePath(t *testing.T) { assert := assert.New(t) - - testIdxFile := testRoot + "/run/Test.cbuild-idx.yml" + testIdxFile := filepath.Join(testRoot, testDir, "Test.cbuild-idx.yml") b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, @@ -351,14 +352,13 @@ func TestGetCprjFilePath(t *testing.T) { testIdxFile, "HelloWorld_cm0plus.Debug+FRDM-K32L3A6") assert.Nil(err) - assert.Equal(path, filepath.Join(testRoot, "run", "cm0plus", "HelloWorld_cm0plus.Debug+FRDM-K32L3A6.cprj")) + assert.Equal(path, filepath.Join(testRoot, testDir, "cm0plus", "HelloWorld_cm0plus.Debug+FRDM-K32L3A6.cprj")) }) } func TestGetSelectedContexts(t *testing.T) { assert := assert.New(t) - - testSetFile := testRoot + "/run/Test.cbuild-set.yml" + testSetFile := filepath.Join(testRoot, testDir, "Test.cbuild-set.yml") b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, @@ -393,7 +393,7 @@ func TestGetSelectedContexts(t *testing.T) { "HelloWorld_cm4.Release+FRDM-K32L3A6", } b.Options.UseContextSet = false - contexts, err := b.getSelectedContexts(testRoot + "/run/Test.cbuild-idx.yml") + contexts, err := b.getSelectedContexts(filepath.Join(testRoot, testDir, "Test.cbuild-idx.yml")) assert.Nil(err) assert.Equal(contexts, expectedContexts) }) @@ -401,7 +401,6 @@ func TestGetSelectedContexts(t *testing.T) { func TestGetIdxFilePath(t *testing.T) { assert := assert.New(t) - b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, @@ -409,7 +408,7 @@ func TestGetIdxFilePath(t *testing.T) { } t.Run("test invalid input file", func(t *testing.T) { - b.InputFile = "run/TestSolution/invalid_file.yml" + b.InputFile = filepath.Join(testRoot, testDir, "TestSolution/invalid_file.yml") path, err := b.getIdxFilePath() assert.Error(err) @@ -417,20 +416,20 @@ func TestGetIdxFilePath(t *testing.T) { }) t.Run("test get idx file path", func(t *testing.T) { - b.InputFile = "run/TestSolution/test.csolution.yml" + b.InputFile = filepath.Join(testRoot, testDir, "TestSolution/test.csolution.yml") path, err := b.getIdxFilePath() assert.Nil(err) - assert.Equal(path, "run/TestSolution/test.cbuild-idx.yml") + assert.Equal(path, utils.NormalizePath(filepath.Join(testRoot, testDir, "TestSolution/test.cbuild-idx.yml"))) }) t.Run("test get idx file path with output path", func(t *testing.T) { - b.InputFile = "run/TestSolution/test.csolution.yml" - b.Options.Output = "run/outdir" + b.InputFile = filepath.Join(testRoot, testDir, "TestSolution/test.csolution.yml") + b.Options.Output = filepath.Join(testRoot, testDir, "outdir") path, err := b.getIdxFilePath() assert.Nil(err) - assert.Equal(path, b.Options.Output+"/test.cbuild-idx.yml") + assert.Equal(path, utils.NormalizePath(b.Options.Output+"/test.cbuild-idx.yml")) }) } @@ -439,27 +438,27 @@ func TestFormulateArg(t *testing.T) { b := CSolutionBuilder{ BuilderParams: builder.BuilderParams{ Runner: RunnerMock{}, - InputFile: testRoot + "/run/Test.csolution.yml", + InputFile: filepath.Join(testRoot, testDir, "Test.csolution.yml"), }, } t.Run("test default arg", func(t *testing.T) { args, err := b.formulateArgs([]string{"convert"}) - strArg := strings.Join(args, " ") + strArg := utils.NormalizePath(strings.Join(args, " ")) assert.Nil(err) - assert.Equal("convert --solution=../../../test/run/Test.csolution.yml --no-check-schema --no-update-rte", strArg) + assert.Equal("convert --solution=../../../test/"+testDir+"/Test.csolution.yml --no-check-schema --no-update-rte", strArg) }) t.Run("test context-set arg", func(t *testing.T) { b.Options = builder.Options{ - OutDir: testRoot + "/run/OutDir", + OutDir: filepath.Join(testRoot, testDir, "OutDir"), Contexts: []string{"test.Debug+Target", "test.Release+Target"}, UseContextSet: true, } args, err := b.formulateArgs([]string{"convert"}) - strArg := strings.Join(args, " ") + strArg := utils.NormalizePath(strings.Join(args, " ")) assert.Nil(err) - assert.Equal("convert --solution=../../../test/run/Test.csolution.yml --no-check-schema --no-update-rte --context=test.Debug+Target --context=test.Release+Target --context-set", strArg) + assert.Equal("convert --solution=../../../test/"+testDir+"/Test.csolution.yml --no-check-schema --no-update-rte --context=test.Debug+Target --context=test.Release+Target --context-set", strArg) }) } @@ -473,7 +472,7 @@ func TestGetCbuildSetFilePath(t *testing.T) { } t.Run("test invalid input file", func(t *testing.T) { - b.InputFile = "run/TestSolution/invalid_file.yml" + b.InputFile = filepath.Join(testRoot, testDir, "TestSolution/invalid_file.yml") path, err := b.getSetFilePath() assert.Error(err) @@ -481,10 +480,10 @@ func TestGetCbuildSetFilePath(t *testing.T) { }) t.Run("test get cbuild-set file path", func(t *testing.T) { - b.InputFile = "run/TestSolution/test.csolution.yml" + b.InputFile = filepath.Join(testRoot, testDir, "TestSolution/test.csolution.yml") path, err := b.getSetFilePath() assert.Nil(err) - assert.Equal(path, "run/TestSolution/test.cbuild-set.yml") + assert.Equal(path, utils.NormalizePath(filepath.Join(testRoot, testDir, "TestSolution/test.cbuild-set.yml"))) }) } diff --git a/pkg/builder/interface.go b/pkg/builder/interface.go index d545df7..4d8d85c 100644 --- a/pkg/builder/interface.go +++ b/pkg/builder/interface.go @@ -7,7 +7,13 @@ package builder import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/utils" + log "github.com/sirupsen/logrus" ) type BuilderParams struct { @@ -18,28 +24,89 @@ type BuilderParams struct { } type Options struct { - IntDir string - OutDir string - LockFile string - LogFile string - Generator string - Target string - Contexts []string - Filter string - Load string - Output string - Toolchain string - Jobs int - Quiet bool - Debug bool - Verbose bool - Clean bool - Schema bool - Packs bool - Rebuild bool - UpdateRte bool - UseContextSet bool - FrozenPacks bool + IntDir string + OutDir string + LockFile string + LogFile string + Generator string + Target string + Contexts []string + Filter string + Load string + Output string + Toolchain string + Jobs int + Quiet bool + Debug bool + Verbose bool + Clean bool + Schema bool + Packs bool + Rebuild bool + UpdateRte bool + UseContextSet bool + FrozenPacks bool + UseCbuild2CMake bool +} + +type InternalVars struct { + BinPath string + EtcPath string + CbuildgenBin string + Cbuild2cmakeBin string + XmllintBin string + CpackgetBin string + CmakeBin string + NinjaBin string +} + +type BuildDirs struct { + IntDir string + OutDir string +} + +func (b BuilderParams) GetInternalVars() (vars InternalVars, err error) { + vars.BinPath = b.InstallConfigs.BinPath + vars.EtcPath = b.InstallConfigs.EtcPath + + vars.CbuildgenBin = filepath.Join(vars.BinPath, "cbuildgen"+b.InstallConfigs.BinExtn) + if _, err := os.Stat(vars.CbuildgenBin); os.IsNotExist(err) { + log.Error("cbuildgen was not found") + return vars, err + } + + vars.Cbuild2cmakeBin = filepath.Join(vars.BinPath, "cbuild2cmake"+b.InstallConfigs.BinExtn) + if _, err := os.Stat(vars.Cbuild2cmakeBin); os.IsNotExist(err) { + log.Error("cbuild2cmake was not found") + return vars, err + } + + cpackgetBin := filepath.Join(vars.BinPath, "cpackget"+b.InstallConfigs.BinExtn) + if _, err := os.Stat(cpackgetBin); !os.IsNotExist(err) { + vars.CpackgetBin = cpackgetBin + } + + vars.XmllintBin, _ = exec.LookPath("xmllint") + vars.CmakeBin, _ = exec.LookPath("cmake") + vars.NinjaBin, _ = exec.LookPath("ninja") + + log.Debug("vars.binPath: " + vars.BinPath) + log.Debug("vars.etcPath: " + vars.EtcPath) + log.Debug("vars.cbuildgenBin: " + vars.CbuildgenBin) + log.Debug("vars.cpackgetBin: " + vars.CpackgetBin) + log.Debug("vars.xmllintBin: " + vars.XmllintBin) + log.Debug("vars.cmakeBin: " + vars.CmakeBin) + log.Debug("vars.ninjaBin: " + vars.NinjaBin) + + return vars, err +} + +func (b BuilderParams) GetJobs() (jobs int) { + jobs = runtime.NumCPU() + if b.Options.Jobs > 0 { + jobs = b.Options.Jobs + } + return jobs } type IBuilderInterface interface { diff --git a/pkg/inittest/inittest.go b/pkg/inittest/inittest.go index 874f550..93b7097 100644 --- a/pkg/inittest/inittest.go +++ b/pkg/inittest/inittest.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Arm Limited. All rights reserved. + * Copyright (c) 2023-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,36 +14,42 @@ import ( "os" "path/filepath" "runtime" - "time" cp "github.com/otiai10/copy" ) -func TestInitialization(testRoot string) { +func TestInitialization(testRoot string, testDir string) { + CleanUp(testRoot, testDir) + testDirPath := testRoot + "/" + testDir // Prepare test data - _ = os.RemoveAll(testRoot + "/run") - time.Sleep(2 * time.Second) - _ = cp.Copy(testRoot+"/data", testRoot+"/run") - - _ = os.MkdirAll(testRoot+"/run/bin", 0755) - _ = os.MkdirAll(testRoot+"/run/etc", 0755) - _ = os.MkdirAll(testRoot+"/run/packs", 0755) - _ = os.MkdirAll(testRoot+"/run/IntDir", 0755) - _ = os.MkdirAll(testRoot+"/run/OutDir", 0755) + _ = cp.Copy(testRoot+"/data", testDirPath) + _ = os.MkdirAll(testDirPath+"/bin", 0755) + _ = os.MkdirAll(testDirPath+"/etc", 0755) + _ = os.MkdirAll(testDirPath+"/packs", 0755) + _ = os.MkdirAll(testDirPath+"/IntDir", 0755) + _ = os.MkdirAll(testDirPath+"/OutDir", 0755) var binExtension string if runtime.GOOS == "windows" { binExtension = ".exe" } - cbuildgenBin := testRoot + "/run/bin/cbuildgen" + binExtension + cbuildgenBin := testDirPath + "/bin/cbuildgen" + binExtension file, _ := os.Create(cbuildgenBin) defer file.Close() - csolutionBin := testRoot + "/run/bin/csolution" + binExtension + csolutionBin := testDirPath + "/bin/csolution" + binExtension file, _ = os.Create(csolutionBin) defer file.Close() - cpackgetBin := testRoot + "/run/bin/cpackget" + binExtension + cpackgetBin := testDirPath + "/bin/cpackget" + binExtension file, _ = os.Create(cpackgetBin) defer file.Close() + cbuild2cmakeBin := testDirPath + "/bin/cbuild2cmake" + binExtension + file, _ = os.Create(cbuild2cmakeBin) + defer file.Close() +} + +func CleanUp(testRoot string, testDir string) { + testDirPath := testRoot + "/" + testDir + _ = os.RemoveAll(testDirPath) } type TestConfigs struct { @@ -52,11 +58,11 @@ type TestConfigs struct { BinExtn string } -func GetTestConfigs(testRoot string) (configs TestConfigs) { +func GetTestConfigs(testRoot string, testDir string) (configs TestConfigs) { if runtime.GOOS == "windows" { configs.BinExtn = ".exe" } - configs.BinPath, _ = filepath.Abs(testRoot + "/run/bin") - configs.EtcPath, _ = filepath.Abs(testRoot + "/run/etc") + configs.BinPath, _ = filepath.Abs(testRoot + "/" + testDir + "/bin") + configs.EtcPath, _ = filepath.Abs(testRoot + "/" + testDir + "/etc") return configs } diff --git a/pkg/utils/configs_test.go b/pkg/utils/configs_test.go index ef6fb34..ed7c6c4 100644 --- a/pkg/utils/configs_test.go +++ b/pkg/utils/configs_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Arm Limited. All rights reserved. + * Copyright (c) 2023-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,13 +9,9 @@ package utils import ( "testing" - "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/inittest" "github.com/stretchr/testify/assert" ) -func init() { - inittest.TestInitialization(testRoot) -} func TestGetInstallConfigs(t *testing.T) { assert := assert.New(t) t.Run("test get install configurations", func(t *testing.T) { diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 96928ec..5a531e1 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Arm Limited. All rights reserved. + * Copyright (c) 2022-2024 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,10 +12,16 @@ import ( "regexp" "testing" + "github.com/Open-CMSIS-Pack/cbuild/v2/pkg/inittest" "github.com/stretchr/testify/assert" ) const testRoot = "../../test" +const testDir = "utils" + +func init() { + inittest.TestInitialization(testRoot, testDir) +} func TestGetExecutablePath(t *testing.T) { assert := assert.New(t) @@ -139,7 +145,7 @@ func TestParseCbuildIndexFile(t *testing.T) { }) t.Run("test cbuild-idx file parsing", func(t *testing.T) { - data, err := ParseCbuildIndexFile(testRoot + "/run/Test.cbuild-idx.yml") + data, err := ParseCbuildIndexFile(filepath.Join(testRoot, testDir, "Test.cbuild-idx.yml")) assert.Nil(err) var re = regexp.MustCompile(`^csolution\s[\d]+.[\d+]+.[\d+].*`) assert.True(re.MatchString(data.BuildIdx.GeneratedBy)) @@ -166,7 +172,7 @@ func TestParseCbuildSetFile(t *testing.T) { }) t.Run("test cbuild-set file parsing", func(t *testing.T) { - data, err := ParseCbuildSetFile(testRoot + "/run/Test.cbuild-set.yml") + data, err := ParseCbuildSetFile(filepath.Join(testRoot, testDir, "Test.cbuild-set.yml")) assert.Nil(err) var re = regexp.MustCompile(`^csolution\sversion\s[\d]+.[\d+]+.[\d+].*`) assert.True(re.MatchString(data.ContextSet.GeneratedBy)) diff --git a/test/data/Hello.Debug+AVH.cbuild.yml b/test/data/Hello.Debug+AVH.cbuild.yml new file mode 100644 index 0000000..2c1225f --- /dev/null +++ b/test/data/Hello.Debug+AVH.cbuild.yml @@ -0,0 +1,243 @@ +build: + generated-by: csolution version 2.2.1 + solution: Hello.csolution.yml + project: Hello.cproject.yml + context: Hello.Debug+AVH + compiler: AC6 + device: ARM::SSE-300-MPS3 + device-pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + processor: + dsp: on + fpu: dp + core: Cortex-M55 + packs: + - pack: ARM::CMSIS@5.9.0 + path: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0 + - pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + path: ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0 + - pack: Keil::ARM_Compiler@1.7.2 + path: ${CMSIS_PACK_ROOT}/Keil/ARM_Compiler/1.7.2 + optimize: none + debug: on + misc: + ASM: + - -masm=auto + C: + - -std=gnu11 + - -Wno-macro-redefined + - -Wno-pragma-pack + - -Wno-parentheses-equality + - -Wno-license-management + CPP: + - -Wno-macro-redefined + - -Wno-pragma-pack + - -Wno-parentheses-equality + - -Wno-license-management + Link: + - --entry=Reset_Handler + - --map + - --info summarysizes + - --summary_stderr + - --diag_suppress=L6314W + define: + - _RTE_ + add-path: + - RTE/CMSIS + - RTE/Device/SSE-300-MPS3 + - RTE/_Debug_AVH + - ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/Core/Include + - ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/Driver/Include + - ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/Include + - ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Include + - ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Board/Device_Definition + - ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Board/Platform + - ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/CMSIS_Driver/Config + - ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/CMSIS_Driver/Include + - ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Device/Include + - ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Native_Driver + output-dirs: + intdir: tmp/Hello/AVH/Debug + outdir: out/AVH + rtedir: RTE + output: + - type: elf + file: Hello.axf + components: + - component: ARM::CMSIS Driver:USART@1.0.0 + condition: SSE-300-MPS3 USART + from-pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + selected-by: CMSIS Driver:USART + files: + - file: ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/CMSIS_Driver/Driver_USART.c + category: source + - component: ARM::CMSIS:CORE@5.6.0 + condition: ARMv6_7_8-M Device + from-pack: ARM::CMSIS@5.9.0 + selected-by: CMSIS:CORE + - component: ARM::CMSIS:RTOS2:Keil RTX5&Source@5.5.4 + condition: RTOS2 RTX5 + from-pack: ARM::CMSIS@5.9.0 + selected-by: ARM::CMSIS:RTOS2:Keil RTX5&Source + files: + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/GCC/irq_armv8mml.S + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_delay.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_evflags.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_evr.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_kernel.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_lib.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_memory.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_mempool.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_msgqueue.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_mutex.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_semaphore.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_system.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_thread.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/RTX/Source/rtx_timer.c + category: source + - file: ${CMSIS_PACK_ROOT}/ARM/CMSIS/5.9.0/CMSIS/RTOS2/Source/os_systick.c + category: source + - file: RTE/CMSIS/RTX_Config.c + category: source + attr: config + version: 5.1.1 + - file: RTE/CMSIS/RTX_Config.h + category: header + attr: config + version: 5.5.2 + - component: ARM::Device:Definition@1.1.0 + condition: SSE-300-MPS3 Device + from-pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + selected-by: Device:Definition + files: + - file: ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Board/Device_Definition/device_definition.c + category: source + - file: RTE/Device/SSE-300-MPS3/platform_base_address.h + category: header + attr: config + version: 1.1.2 + - file: RTE/Device/SSE-300-MPS3/system_SSE300MPS3.c + category: source + attr: config + version: 1.1.1 + - component: ARM::Device:Startup&Baremetal@1.1.0 + condition: SSE-300-MPS3 Device + from-pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + selected-by: Device:Startup&Baremetal + files: + - file: RTE/Device/SSE-300-MPS3/cmsis_driver_config.h + category: header + attr: config + version: 1.1.1 + - file: RTE/Device/SSE-300-MPS3/RTE_Device.h + category: header + attr: config + version: 1.1.0 + - file: RTE/Device/SSE-300-MPS3/device_cfg.h + category: header + attr: config + version: 1.1.2 + - file: RTE/Device/SSE-300-MPS3/region_defs.h + category: header + attr: config + version: 1.0.0 + - file: RTE/Device/SSE-300-MPS3/region_limits.h + category: header + attr: config + version: 1.0.0 + - file: RTE/Device/SSE-300-MPS3/fvp_sse300_mps3_s.sct + category: linkerScript + attr: config + version: 1.1.0 + - file: RTE/Device/SSE-300-MPS3/startup_fvp_sse300_mps3.c + category: source + attr: config + version: 1.1.1 + - component: ARM::Native Driver:IO@1.0.0 + condition: SSE-300-MPS3 Device + from-pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + selected-by: ARM::Native Driver:IO + files: + - file: ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Native_Driver/arm_mps3_io_drv.c + category: source + - component: ARM::Native Driver:UART@1.0.0 + condition: SSE-300-MPS3 Device + from-pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + selected-by: ARM::Native Driver:UART + files: + - file: ${CMSIS_PACK_ROOT}/ARM/V2M_MPS3_SSE_300_BSP/1.2.0/Native_Driver/uart_cmsdk_drv.c + category: source + - component: Keil::Compiler&ARM Compiler:I/O:STDERR&User@1.2.0 + condition: ARMCC Cortex-M + from-pack: Keil::ARM_Compiler@1.7.2 + selected-by: Compiler:I/O:STDERR&User + - component: Keil::Compiler&ARM Compiler:I/O:STDIN&User@1.2.0 + condition: ARMCC Cortex-M + from-pack: Keil::ARM_Compiler@1.7.2 + selected-by: Compiler:I/O:STDIN&User + - component: Keil::Compiler&ARM Compiler:I/O:STDOUT&User@1.2.0 + condition: ARMCC Cortex-M + from-pack: Keil::ARM_Compiler@1.7.2 + selected-by: Compiler:I/O:STDOUT&User + files: + - file: ${CMSIS_PACK_ROOT}/Keil/ARM_Compiler/1.7.2/Source/retarget_io.c + category: source + linker: + script: RTE/Device/SSE-300-MPS3/fvp_sse300_mps3_s.sct + groups: + - group: Documentation + files: + - file: ./README.md + category: doc + - group: Main + files: + - file: ./main.c + category: sourceC + - group: App + files: + - file: ./hello.c + category: sourceC + - group: Board IO + files: + - file: ./Board_IO/retarget_stdio.c + category: sourceC + constructed-files: + - file: RTE/_Debug_AVH/RTE_Components.h + category: header + licenses: + - license: + packs: + - pack: ARM::CMSIS@5.9.0 + components: + - component: ::CMSIS Driver:USART(API) + - component: ::CMSIS:RTOS2(API) + - component: ARM::CMSIS:CORE@5.6.0 + - component: ARM::CMSIS:RTOS2:Keil RTX5&Source@5.5.4 + - license: + packs: + - pack: ARM::V2M_MPS3_SSE_300_BSP@1.2.0 + components: + - component: ARM::CMSIS Driver:USART@1.0.0 + - component: ARM::Device:Definition@1.1.0 + - component: ARM::Device:Startup&Baremetal@1.1.0 + - component: ARM::Native Driver:IO@1.0.0 + - component: ARM::Native Driver:UART@1.0.0 + - license: + packs: + - pack: Keil::ARM_Compiler@1.7.2 + components: + - component: Keil::Compiler&ARM Compiler:I/O:STDERR&User@1.2.0 + - component: Keil::Compiler&ARM Compiler:I/O:STDIN&User@1.2.0 + - component: Keil::Compiler&ARM Compiler:I/O:STDOUT&User@1.2.0 diff --git a/test/data/Hello.cbuild-idx.yml b/test/data/Hello.cbuild-idx.yml new file mode 100644 index 0000000..7e57684 --- /dev/null +++ b/test/data/Hello.cbuild-idx.yml @@ -0,0 +1,13 @@ +build-idx: + generated-by: csolution version 2.2.1 + cdefault: ${CMSIS_COMPILER_ROOT}/cdefault.yml + csolution: Hello.csolution.yml + cprojects: + - cproject: Hello.cproject.yml + cbuilds: + - cbuild: Hello.Debug+AVH.cbuild.yml + project: Hello + configuration: .Debug+AVH + - cbuild: Hello.Release+AVH.cbuild.yml + project: Hello + configuration: .Release+AVH