diff --git a/README.md b/README.md index 975f425..02189de 100644 --- a/README.md +++ b/README.md @@ -25,26 +25,27 @@ Available Commands: list List information Flags: - -c, --clean Remove intermediate and output directories - --context string Input context name e.g. [project][.buildType][+targetType] - -d, --debug Enable debug messages - -g, --generator string Select build system generator (default "Ninja") - -h, --help Print usage - -i, --intdir string Set directory for intermediate files - -j, --jobs int Number of job slots for parallel execution - --load string Set policy for packs loading [latest|all|required] - -l, --log string Save output messages in a log file - -o, --outdir string Set directory for output binary files - -O, --output string Set directory for all output files - -p, --packs Download missing software packs with cpackget - -q, --quiet Suppress output messages except build invocations - -r, --rebuild Remove intermediate and output directories and rebuild - -s, --schema Validate project input file(s) against schema - -t, --target string Optional CMake target name - -u, --update string Generate *.cprj file for reproducing current build - --update-rte Update the RTE directory and files - -v, --verbose Enable verbose messages from toolchain builds - -V, --version Print version + -c, --clean Remove intermediate and output directories + --configuration string Input configuration name e.g. [.buildType][+targetType] + --context string Input context name e.g. project.buildType+targetType + -d, --debug Enable debug messages + -g, --generator string Select build system generator (default "Ninja") + -h, --help Print usage + -i, --intdir string Set directory for intermediate files + -j, --jobs int Number of job slots for parallel execution + --load string Set policy for packs loading [latest|all|required] + -l, --log string Save output messages in a log file + -o, --outdir string Set directory for output binary files + -O, --output string Set directory for all output files + -p, --packs Download missing software packs with cpackget + -q, --quiet Suppress output messages except build invocations + -r, --rebuild Remove intermediate and output directories and rebuild + -s, --schema Validate project input file(s) against schema + -t, --target string Optional CMake target name + -u, --update string Generate *.cprj file for reproducing current build + --update-rte Update the RTE directory and files + -v, --verbose Enable verbose messages from toolchain builds + -V, --version Print version Use "cbuild [command] --help" for more information about a command. ``` diff --git a/pkg/builder/csolution/builder.go b/pkg/builder/csolution/builder.go index caa9fb9..9790446 100644 --- a/pkg/builder/csolution/builder.go +++ b/pkg/builder/csolution/builder.go @@ -211,6 +211,21 @@ func (b CSolutionBuilder) getCsolutionPath() (path string, err error) { return } +func (b CSolutionBuilder) validateContext(allContexts []string) (err error) { + _, err = utils.ParseContext(b.Options.Context) + if err != nil { + return + } + + if !utils.Contains(allContexts, b.Options.Context) { + sort.Strings(allContexts) + err = errors.New("specified context '" + b.Options.Context + + "' not found. One of the following contexts must be specified:\n" + + strings.Join(allContexts, "\n")) + } + return +} + func (b CSolutionBuilder) Build() (err error) { _ = utils.UpdateEnvVars(b.InstallConfigs.BinPath, b.InstallConfigs.EtcPath) csolutionBin, err := b.getCsolutionPath() @@ -232,12 +247,7 @@ func (b CSolutionBuilder) Build() (err error) { var selectedContexts []string if b.Options.Context != "" { - // validate context - if !utils.Contains(allContexts, b.Options.Context) { - sort.Strings(allContexts) - err = errors.New("specified context '" + b.Options.Context + - "' not found. One of the following contexts must be specified:\n" + - strings.Join(allContexts, "\n")) + if err = b.validateContext(allContexts); err != nil { return } selectedContexts = append(selectedContexts, b.Options.Context) @@ -291,8 +301,11 @@ func (b CSolutionBuilder) Build() (err error) { args = append(args, "--no-update-rte") } if b.Options.Configuration != "" { - configurationItem, _ := utils.ParseConfiguration(b.Options.Configuration) - contextQuery := "*" + b.Options.Configuration + configurationItem, err := utils.ParseConfiguration(b.Options.Configuration) + if err != nil { + return err + } + contextQuery := "*" + utils.CreateConfiguration(configurationItem) if configurationItem.TargetType == "" { contextQuery += "*" } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 54e3c8a..532992d 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -100,10 +100,12 @@ func GetDefaultCmsisPackRoot() (root string) { } func ParseContext(context string) (item ContextItem, err error) { + parseError := errors.New("invalid context. Follow project.buildType+targetType symantic") + periodCount := strings.Count(context, ".") plusCount := strings.Count(context, "+") if context == "" || periodCount > 1 || plusCount > 1 { - err = errors.New("invalid context. Follow [project][.buildType][+targetType] symantic") + err = parseError return } @@ -112,38 +114,35 @@ func ParseContext(context string) (item ContextItem, err error) { targetIdx := strings.Index(context, "+") buildIdx := strings.Index(context, ".") - if targetIdx == -1 && buildIdx == -1 { - // context with only projectName - projectName = context - } else if targetIdx != -1 && buildIdx == -1 { - // context with only projectName+targetType - projectName = context[:targetIdx] - targetType = context[targetIdx+1:] - } else if targetIdx == -1 && buildIdx != -1 { - // context with only projectName.buildType - projectName = context[:buildIdx] - buildType = context[buildIdx+1:] + if targetIdx == -1 || buildIdx == -1 { + err = parseError + return + } + + // fully specified contexts + part := context[:targetIdx] + buildIdx = strings.Index(part, ".") + + if buildIdx > -1 { + projectName = part[:buildIdx] + buildType = part[buildIdx+1:] } else { - // fully specified contexts - part := context[:targetIdx] - buildIdx := strings.Index(part, ".") + projectName = part + } - if buildIdx > -1 { - projectName = part[:buildIdx] - buildType = part[buildIdx+1:] - } else { - projectName = part - } + part = context[targetIdx+1:] + buildIdx = strings.Index(part, ".") - part = context[targetIdx+1:] - buildIdx = strings.Index(part, ".") + if buildIdx > -1 { + targetType = part[:buildIdx] + buildType = part[buildIdx+1:] + } else { + targetType = part + } - if buildIdx > -1 { - targetType = part[:buildIdx] - buildType = part[buildIdx+1:] - } else { - targetType = part - } + if projectName == "" || buildType == "" || targetType == "" { + err = parseError + return } item.ProjectName = projectName @@ -153,16 +152,62 @@ func ParseContext(context string) (item ContextItem, err error) { } func ParseConfiguration(configuration string) (item ConfigurationItem, err error) { - context, err := ParseContext(configuration) - if err != nil { + parseErr := errors.New("invalid context. Follow [.buildType][+targetType] symantic") + periodCount := strings.Count(configuration, ".") + plusCount := strings.Count(configuration, "+") + if configuration == "" || periodCount > 1 || plusCount > 1 { + err = parseErr return } - if context.ProjectName != "" { - return item, errors.New("invalid configuration") + var buildType, targetType string + targetIdx := strings.Index(configuration, "+") + buildIdx := strings.Index(configuration, ".") + + if targetIdx == -1 && buildIdx == -1 { + err = parseErr + return + } + + if !(targetIdx == 0 || buildIdx == 0) { + err = parseErr + return + } + + if targetIdx == -1 { + // configuration contains only buildType + buildType = configuration[buildIdx+1:] + } else if buildIdx == -1 { + // configuration contains only targetType + targetType = configuration[targetIdx+1:] + } else { + // fully specified configuration + if buildIdx == 0 { + buildType = configuration[buildIdx+1 : targetIdx] + targetType = configuration[targetIdx+1:] + } else { + targetType = configuration[targetIdx+1 : buildIdx] + buildType = configuration[buildIdx+1:] + } + } + + if buildType == "" && targetType == "" { + err = parseErr + return + } + + item.BuildType = buildType + item.TargetType = targetType + return +} + +func CreateConfiguration(configItem ConfigurationItem) (configuration string) { + if configItem.BuildType != "" { + configuration += "." + configItem.BuildType + } + if configItem.TargetType != "" { + configuration += "+" + configItem.TargetType } - item.BuildType = context.BuildType - item.TargetType = context.TargetType return } diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index c06bf44..361c8da 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -71,15 +71,18 @@ func TestParseContext(t *testing.T) { ExpectedContext ContextItem }{ {"", true, ContextItem{}}, + {".+", true, ContextItem{}}, + {".Build+", true, ContextItem{}}, + {".+Target", true, ContextItem{}}, {".Build.Build2+Target", true, ContextItem{}}, {".Build+Target+Test", true, ContextItem{}}, - {"+Target", false, ContextItem{ProjectName: "", BuildType: "", TargetType: "Target"}}, - {".Build", false, ContextItem{ProjectName: "", BuildType: "Build", TargetType: ""}}, - {".Build+Target", false, ContextItem{ProjectName: "", BuildType: "Build", TargetType: "Target"}}, - {"+Target.Build", false, ContextItem{ProjectName: "", BuildType: "Build", TargetType: "Target"}}, - {"Project", false, ContextItem{ProjectName: "Project", BuildType: "", TargetType: ""}}, - {"Project.Build", false, ContextItem{ProjectName: "Project", BuildType: "Build", TargetType: ""}}, - {"Project+Target", false, ContextItem{ProjectName: "Project", BuildType: "", TargetType: "Target"}}, + {"+Target", true, ContextItem{}}, + {".Build", true, ContextItem{}}, + {".Build+Target", true, ContextItem{}}, + {"+Target.Build", true, ContextItem{}}, + {"Project", true, ContextItem{}}, + {"Project.Build", true, ContextItem{}}, + {"Project+Target", true, ContextItem{}}, {"Project.Build+Target", false, ContextItem{ProjectName: "Project", BuildType: "Build", TargetType: "Target"}}, {"Project+Target.Build", false, ContextItem{ProjectName: "Project", BuildType: "Build", TargetType: "Target"}}, } @@ -96,6 +99,60 @@ func TestParseContext(t *testing.T) { } } +func TestParseConfiguration(t *testing.T) { + assert := assert.New(t) + + testCases := []struct { + Input string + ExpectError bool + ExpectedContext ConfigurationItem + }{ + {"", true, ConfigurationItem{}}, + {".+", true, ConfigurationItem{}}, + {"Project", true, ConfigurationItem{}}, + {".Build.Build2+Target", true, ConfigurationItem{}}, + {".Build+Target+Test", true, ConfigurationItem{}}, + {"Project.Build", true, ConfigurationItem{}}, + {"Project+Target", true, ConfigurationItem{}}, + {"Project.Build+Target", true, ConfigurationItem{}}, + {"Project+Target.Build", true, ConfigurationItem{}}, + {".+Target", false, ConfigurationItem{BuildType: "", TargetType: "Target"}}, + {".Build+", false, ConfigurationItem{BuildType: "Build", TargetType: ""}}, + {"+Target", false, ConfigurationItem{BuildType: "", TargetType: "Target"}}, + {".Build", false, ConfigurationItem{BuildType: "Build", TargetType: ""}}, + {".Build+Target", false, ConfigurationItem{BuildType: "Build", TargetType: "Target"}}, + {"+Target.Build", false, ConfigurationItem{BuildType: "Build", TargetType: "Target"}}, + } + for _, test := range testCases { + configItem, err := ParseConfiguration(test.Input) + if test.ExpectError { + assert.Error(err) + } else { + assert.Nil(err) + } + assert.Equal(configItem.BuildType, test.ExpectedContext.BuildType) + assert.Equal(configItem.TargetType, test.ExpectedContext.TargetType) + } +} + +func TestCreateConfiguration(t *testing.T) { + assert := assert.New(t) + + testCases := []struct { + Input ConfigurationItem + ExpectedOutput string + }{ + {ConfigurationItem{}, ""}, + {ConfigurationItem{BuildType: "Build", TargetType: "Target"}, ".Build+Target"}, + {ConfigurationItem{BuildType: "", TargetType: "Target"}, "+Target"}, + {ConfigurationItem{BuildType: "Build", TargetType: ""}, ".Build"}, + } + for _, test := range testCases { + config := CreateConfiguration(test.Input) + assert.Equal(config, test.ExpectedOutput) + } +} + func TestGetSelectedContexts(t *testing.T) { assert := assert.New(t) var empty []string