From ccef5a119c79e9f4f4a748815b4ecd73d0ca500f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 27 Nov 2024 18:06:20 +0100 Subject: [PATCH 1/6] Made FQBN parsing package public Because it may turn out useful for other projects. --- commands/service_board_details.go | 6 +- commands/service_board_list.go | 12 +-- commands/service_compile.go | 6 +- commands/service_debug_config.go | 6 +- commands/service_library_list.go | 10 +- commands/service_monitor.go | 7 +- commands/service_upload.go | 9 +- commands/service_upload_list_programmers.go | 3 +- commands/service_upload_test.go | 5 +- .../arduino/builder/build_options_manager.go | 4 +- internal/arduino/builder/builder.go | 3 +- internal/arduino/cores/board.go | 5 +- .../cores/packagemanager/package_manager.go | 23 ++--- .../packagemanager/package_manager_test.go | 53 +++++------ internal/cli/board/list.go | 10 +- {internal/arduino/cores => pkg/fqbn}/fqbn.go | 63 +++++++------ .../arduino/cores => pkg/fqbn}/fqbn_test.go | 91 ++++++++++--------- 17 files changed, 162 insertions(+), 154 deletions(-) rename {internal/arduino/cores => pkg/fqbn}/fqbn.go (80%) rename {internal/arduino/cores => pkg/fqbn}/fqbn_test.go (71%) diff --git a/commands/service_board_details.go b/commands/service_board_details.go index 5f042452582..4104c9bc21b 100644 --- a/commands/service_board_details.go +++ b/commands/service_board_details.go @@ -20,8 +20,8 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/utils" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) @@ -34,7 +34,7 @@ func (s *arduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.Board } defer release() - fqbn, err := cores.ParseFQBN(req.GetFqbn()) + fqbn, err := fqbn.Parse(req.GetFqbn()) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } @@ -48,7 +48,7 @@ func (s *arduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.Board details.Name = board.Name() details.Fqbn = board.FQBN() details.PropertiesId = board.BoardID - details.Official = fqbn.Package == "arduino" + details.Official = fqbn.Packager == "arduino" details.Version = board.PlatformRelease.Version.String() details.IdentificationProperties = []*rpc.BoardIdentificationProperties{} for _, p := range board.GetIdentificationProperties() { diff --git a/commands/service_board_list.go b/commands/service_board_list.go index 2b124c29f37..9a84e3319f5 100644 --- a/commands/service_board_list.go +++ b/commands/service_board_list.go @@ -30,11 +30,11 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" f "github.com/arduino/arduino-cli/internal/algorithms" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" "github.com/arduino/arduino-cli/internal/inventory" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-properties-orderedmap" discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2" @@ -148,7 +148,7 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port, settings *conf // first query installed cores through the Package Manager logrus.Debug("Querying installed cores for board identification...") for _, board := range pme.IdentifyBoard(port.Properties) { - fqbn, err := cores.ParseFQBN(board.FQBN()) + fqbn, err := fqbn.Parse(board.FQBN()) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } @@ -210,10 +210,10 @@ func (s *arduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardLis } defer release() - var fqbnFilter *cores.FQBN + var fqbnFilter *fqbn.FQBN if f := req.GetFqbn(); f != "" { var err error - fqbnFilter, err = cores.ParseFQBN(f) + fqbnFilter, err = fqbn.Parse(f) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } @@ -247,9 +247,9 @@ func (s *arduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardLis }, nil } -func hasMatchingBoard(b *rpc.DetectedPort, fqbnFilter *cores.FQBN) bool { +func hasMatchingBoard(b *rpc.DetectedPort, fqbnFilter *fqbn.FQBN) bool { for _, detectedBoard := range b.GetMatchingBoards() { - detectedFqbn, err := cores.ParseFQBN(detectedBoard.GetFqbn()) + detectedFqbn, err := fqbn.Parse(detectedBoard.GetFqbn()) if err != nil { continue } diff --git a/commands/service_compile.go b/commands/service_compile.go index a7ce1ea2bbf..61f8e1ef1d7 100644 --- a/commands/service_compile.go +++ b/commands/service_compile.go @@ -28,13 +28,13 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/builder" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/arduino/utils" "github.com/arduino/arduino-cli/internal/buildcache" "github.com/arduino/arduino-cli/internal/i18n" "github.com/arduino/arduino-cli/internal/inventory" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" @@ -116,7 +116,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu return &cmderrors.MissingFQBNError{} } - fqbn, err := cores.ParseFQBN(fqbnIn) + fqbn, err := fqbn.Parse(fqbnIn) if err != nil { return &cmderrors.InvalidFQBNError{Cause: err} } @@ -124,7 +124,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu if err != nil { if targetPlatform == nil { return &cmderrors.PlatformNotFoundError{ - Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch), + Platform: fmt.Sprintf("%s:%s", fqbn.Packager, fqbn.Architecture), Cause: errors.New(i18n.Tr("platform not installed")), } } diff --git a/commands/service_debug_config.go b/commands/service_debug_config.go index c2cf04e5aa3..f755b68adb5 100644 --- a/commands/service_debug_config.go +++ b/commands/service_debug_config.go @@ -27,10 +27,10 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" @@ -76,7 +76,7 @@ func (s *arduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.I // Compute the minimum FQBN required to get the same debug configuration. // (i.e. the FQBN cleaned up of the options that do not affect the debugger configuration) - minimumFQBN := cores.MustParseFQBN(req.GetFqbn()) + minimumFQBN := fqbn.MustParse(req.GetFqbn()) for _, config := range minimumFQBN.Configs.Keys() { checkFQBN := minimumFQBN.Clone() checkFQBN.Configs.Remove(config) @@ -127,7 +127,7 @@ func (s *arduinoCoreServerImpl) getDebugProperties(req *rpc.GetDebugConfigReques if fqbnIn == "" { return nil, &cmderrors.MissingFQBNError{} } - fqbn, err := cores.ParseFQBN(fqbnIn) + fqbn, err := fqbn.Parse(fqbnIn) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } diff --git a/commands/service_library_list.go b/commands/service_library_list.go index 35104caf08b..2d30e11dbd3 100644 --- a/commands/service_library_list.go +++ b/commands/service_library_list.go @@ -21,12 +21,12 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/libraries" "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex" "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager" "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesresolver" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) @@ -59,7 +59,7 @@ func (s *arduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.Librar var allLibs []*installedLib if fqbnString := req.GetFqbn(); fqbnString != "" { allLibs = listLibraries(lme, li, req.GetUpdatable(), true) - fqbn, err := cores.ParseFQBN(req.GetFqbn()) + fqbn, err := fqbn.Parse(req.GetFqbn()) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } @@ -77,8 +77,8 @@ func (s *arduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.Librar } } if latest, has := filteredRes[lib.Library.Name]; has { - latestPriority := librariesresolver.ComputePriority(latest.Library, "", fqbn.PlatformArch) - libPriority := librariesresolver.ComputePriority(lib.Library, "", fqbn.PlatformArch) + latestPriority := librariesresolver.ComputePriority(latest.Library, "", fqbn.Architecture) + libPriority := librariesresolver.ComputePriority(lib.Library, "", fqbn.Architecture) if latestPriority >= libPriority { // Pick library with the best priority continue @@ -87,7 +87,7 @@ func (s *arduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.Librar // Check if library is compatible with board specified by FBQN lib.Library.CompatibleWith = map[string]bool{ - fqbnString: lib.Library.IsCompatibleWith(fqbn.PlatformArch), + fqbnString: lib.Library.IsCompatibleWith(fqbn.Architecture), } filteredRes[lib.Library.Name] = lib diff --git a/commands/service_monitor.go b/commands/service_monitor.go index 8c3402681b7..012d4ddf8bc 100644 --- a/commands/service_monitor.go +++ b/commands/service_monitor.go @@ -28,6 +28,7 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" pluggableMonitor "github.com/arduino/arduino-cli/internal/arduino/monitor" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-properties-orderedmap" "github.com/djherbis/buffer" @@ -237,7 +238,7 @@ func (s *arduinoCoreServerImpl) Monitor(stream rpc.ArduinoCoreService_MonitorSer return nil } -func findMonitorAndSettingsForProtocolAndBoard(pme *packagemanager.Explorer, protocol, fqbn string) (*pluggableMonitor.PluggableMonitor, *properties.Map, error) { +func findMonitorAndSettingsForProtocolAndBoard(pme *packagemanager.Explorer, protocol, fqbnIn string) (*pluggableMonitor.PluggableMonitor, *properties.Map, error) { if protocol == "" { return nil, nil, &cmderrors.MissingPortProtocolError{} } @@ -246,8 +247,8 @@ func findMonitorAndSettingsForProtocolAndBoard(pme *packagemanager.Explorer, pro boardSettings := properties.NewMap() // If a board is specified search the monitor in the board package first - if fqbn != "" { - fqbn, err := cores.ParseFQBN(fqbn) + if fqbnIn != "" { + fqbn, err := fqbn.Parse(fqbnIn) if err != nil { return nil, nil, &cmderrors.InvalidFQBNError{Cause: err} } diff --git a/commands/service_upload.go b/commands/service_upload.go index 56d621813fe..2e5e9272d51 100644 --- a/commands/service_upload.go +++ b/commands/service_upload.go @@ -32,6 +32,7 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/globals" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" @@ -53,7 +54,7 @@ func (s *arduinoCoreServerImpl) SupportedUserFields(ctx context.Context, req *rp } defer release() - fqbn, err := cores.ParseFQBN(req.GetFqbn()) + fqbn, err := fqbn.Parse(req.GetFqbn()) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } @@ -61,7 +62,7 @@ func (s *arduinoCoreServerImpl) SupportedUserFields(ctx context.Context, req *rp _, platformRelease, _, boardProperties, _, err := pme.ResolveFQBN(fqbn) if platformRelease == nil { return nil, &cmderrors.PlatformNotFoundError{ - Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch), + Platform: fmt.Sprintf("%s:%s", fqbn.Packager, fqbn.Architecture), Cause: err, } } else if err != nil { @@ -282,7 +283,7 @@ func (s *arduinoCoreServerImpl) runProgramAction(ctx context.Context, pme *packa return nil, &cmderrors.MissingProgrammerError{} } - fqbn, err := cores.ParseFQBN(fqbnIn) + fqbn, err := fqbn.Parse(fqbnIn) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } @@ -292,7 +293,7 @@ func (s *arduinoCoreServerImpl) runProgramAction(ctx context.Context, pme *packa _, boardPlatform, board, boardProperties, buildPlatform, err := pme.ResolveFQBN(fqbn) if boardPlatform == nil { return nil, &cmderrors.PlatformNotFoundError{ - Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch), + Platform: fmt.Sprintf("%s:%s", fqbn.Packager, fqbn.Architecture), Cause: err, } } else if err != nil { diff --git a/commands/service_upload_list_programmers.go b/commands/service_upload_list_programmers.go index f05142cf147..761d9babf5c 100644 --- a/commands/service_upload_list_programmers.go +++ b/commands/service_upload_list_programmers.go @@ -21,6 +21,7 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) @@ -36,7 +37,7 @@ func (s *arduinoCoreServerImpl) ListProgrammersAvailableForUpload(ctx context.Co if fqbnIn == "" { return nil, &cmderrors.MissingFQBNError{} } - fqbn, err := cores.ParseFQBN(fqbnIn) + fqbn, err := fqbn.Parse(fqbnIn) if err != nil { return nil, &cmderrors.InvalidFQBNError{Cause: err} } diff --git a/commands/service_upload_test.go b/commands/service_upload_test.go index 737eec92e83..4a86a0f274b 100644 --- a/commands/service_upload_test.go +++ b/commands/service_upload_test.go @@ -25,6 +25,7 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" @@ -60,7 +61,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { importFile string importDir string sketch *sketch.Sketch - fqbn *cores.FQBN + fqbn *fqbn.FQBN resBuildPath string resSketchName string } @@ -68,7 +69,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { blonk, err := sketch.New(paths.New("testdata/upload/Blonk")) require.NoError(t, err) - fqbn, err := cores.ParseFQBN("arduino:samd:mkr1000") + fqbn, err := fqbn.Parse("arduino:samd:mkr1000") require.NoError(t, err) srv := NewArduinoCoreServer().(*arduinoCoreServerImpl) diff --git a/internal/arduino/builder/build_options_manager.go b/internal/arduino/builder/build_options_manager.go index 09f0afad815..ebaf97bda28 100644 --- a/internal/arduino/builder/build_options_manager.go +++ b/internal/arduino/builder/build_options_manager.go @@ -22,9 +22,9 @@ import ( "strings" "github.com/arduino/arduino-cli/internal/arduino/builder/internal/utils" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" ) @@ -51,7 +51,7 @@ func newBuildOptions( builtInLibrariesDirs, buildPath *paths.Path, sketch *sketch.Sketch, customBuildProperties []string, - fqbn *cores.FQBN, + fqbn *fqbn.FQBN, clean bool, compilerOptimizationFlags string, runtimePlatformPath, buildCorePath *paths.Path, diff --git a/internal/arduino/builder/builder.go b/internal/arduino/builder/builder.go index 91c74965782..58d607c827a 100644 --- a/internal/arduino/builder/builder.go +++ b/internal/arduino/builder/builder.go @@ -35,6 +35,7 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" @@ -127,7 +128,7 @@ func NewBuilder( requestBuildProperties []string, hardwareDirs, otherLibrariesDirs paths.PathList, builtInLibrariesDirs *paths.Path, - fqbn *cores.FQBN, + fqbn *fqbn.FQBN, clean bool, sourceOverrides map[string]string, onlyUpdateCompilationDatabase bool, diff --git a/internal/arduino/cores/board.go b/internal/arduino/cores/board.go index ed1aa9c68b5..6e966a68aee 100644 --- a/internal/arduino/cores/board.go +++ b/internal/arduino/cores/board.go @@ -21,6 +21,7 @@ import ( "sync" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" "github.com/arduino/go-properties-orderedmap" ) @@ -124,7 +125,7 @@ func (b *Board) GetConfigOptionValues(option string) *properties.Map { // GetBuildProperties returns the build properties and the build // platform for the Board with the configuration passed as parameter. -func (b *Board) GetBuildProperties(fqbn *FQBN) (*properties.Map, error) { +func (b *Board) GetBuildProperties(fqbn *fqbn.FQBN) (*properties.Map, error) { b.buildConfigOptionsStructures() // Override default configs with user configs @@ -161,7 +162,7 @@ func (b *Board) GetBuildProperties(fqbn *FQBN) (*properties.Map, error) { // "cpu=atmega2560". // FIXME: deprecated, use GetBuildProperties instead func (b *Board) GeneratePropertiesForConfiguration(config string) (*properties.Map, error) { - fqbn, err := ParseFQBN(b.String() + ":" + config) + fqbn, err := fqbn.Parse(b.String() + ":" + config) if err != nil { return nil, errors.New(i18n.Tr("parsing fqbn: %s", err)) } diff --git a/internal/arduino/cores/packagemanager/package_manager.go b/internal/arduino/cores/packagemanager/package_manager.go index aaaf675a410..4c22c19c6e4 100644 --- a/internal/arduino/cores/packagemanager/package_manager.go +++ b/internal/arduino/cores/packagemanager/package_manager.go @@ -33,6 +33,7 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/discovery/discoverymanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" "github.com/arduino/go-timeutils" @@ -290,7 +291,7 @@ func (pme *Explorer) FindBoardsWithID(id string) []*cores.Board { // FindBoardWithFQBN returns the board identified by the fqbn, or an error func (pme *Explorer) FindBoardWithFQBN(fqbnIn string) (*cores.Board, error) { - fqbn, err := cores.ParseFQBN(fqbnIn) + fqbn, err := fqbn.Parse(fqbnIn) if err != nil { return nil, errors.New(i18n.Tr("parsing fqbn: %s", err)) } @@ -318,22 +319,22 @@ func (pme *Explorer) FindBoardWithFQBN(fqbnIn string) (*cores.Board, error) { // // In case of error the partial results found in the meantime are // returned together with the error. -func (pme *Explorer) ResolveFQBN(fqbn *cores.FQBN) ( +func (pme *Explorer) ResolveFQBN(fqbn *fqbn.FQBN) ( *cores.Package, *cores.PlatformRelease, *cores.Board, *properties.Map, *cores.PlatformRelease, error) { // Find package - targetPackage := pme.packages[fqbn.Package] + targetPackage := pme.packages[fqbn.Packager] if targetPackage == nil { return nil, nil, nil, nil, nil, - errors.New(i18n.Tr("unknown package %s", fqbn.Package)) + errors.New(i18n.Tr("unknown package %s", fqbn.Packager)) } // Find platform - platform := targetPackage.Platforms[fqbn.PlatformArch] + platform := targetPackage.Platforms[fqbn.Architecture] if platform == nil { return targetPackage, nil, nil, nil, nil, - errors.New(i18n.Tr("unknown platform %s:%s", targetPackage, fqbn.PlatformArch)) + errors.New(i18n.Tr("unknown platform %s:%s", targetPackage, fqbn.Architecture)) } boardPlatformRelease := pme.GetInstalledPlatformRelease(platform) if boardPlatformRelease == nil { @@ -429,7 +430,7 @@ func (pme *Explorer) ResolveFQBN(fqbn *cores.FQBN) ( return targetPackage, boardPlatformRelease, board, buildProperties, corePlatformRelease, nil } -func (pme *Explorer) determineReferencedPlatformRelease(boardBuildProperties *properties.Map, boardPlatformRelease *cores.PlatformRelease, fqbn *cores.FQBN) (string, *cores.PlatformRelease, string, *cores.PlatformRelease, error) { +func (pme *Explorer) determineReferencedPlatformRelease(boardBuildProperties *properties.Map, boardPlatformRelease *cores.PlatformRelease, fqbn *fqbn.FQBN) (string, *cores.PlatformRelease, string, *cores.PlatformRelease, error) { core := boardBuildProperties.ExpandPropsInString(boardBuildProperties.Get("build.core")) referredCore := "" if split := strings.Split(core, ":"); len(split) > 1 { @@ -461,15 +462,15 @@ func (pme *Explorer) determineReferencedPlatformRelease(boardBuildProperties *pr return "", nil, "", nil, errors.New(i18n.Tr("missing package %[1]s referenced by board %[2]s", referredPackageName, fqbn)) } - referredPlatform := referredPackage.Platforms[fqbn.PlatformArch] + referredPlatform := referredPackage.Platforms[fqbn.Architecture] if referredPlatform == nil { return "", nil, "", nil, - errors.New(i18n.Tr("missing platform %[1]s:%[2]s referenced by board %[3]s", referredPackageName, fqbn.PlatformArch, fqbn)) + errors.New(i18n.Tr("missing platform %[1]s:%[2]s referenced by board %[3]s", referredPackageName, fqbn.Architecture, fqbn)) } referredPlatformRelease = pme.GetInstalledPlatformRelease(referredPlatform) if referredPlatformRelease == nil { return "", nil, "", nil, - errors.New(i18n.Tr("missing platform release %[1]s:%[2]s referenced by board %[3]s", referredPackageName, fqbn.PlatformArch, fqbn)) + errors.New(i18n.Tr("missing platform release %[1]s:%[2]s referenced by board %[3]s", referredPackageName, fqbn.Architecture, fqbn)) } } @@ -890,7 +891,7 @@ func (pme *Explorer) FindMonitorDependency(discovery *cores.MonitorDependency) * // NormalizeFQBN return a normalized copy of the given FQBN, that is the same // FQBN but with the unneeded or invalid options removed. -func (pme *Explorer) NormalizeFQBN(fqbn *cores.FQBN) (*cores.FQBN, error) { +func (pme *Explorer) NormalizeFQBN(fqbn *fqbn.FQBN) (*fqbn.FQBN, error) { _, _, board, _, _, err := pme.ResolveFQBN(fqbn) if err != nil { return nil, err diff --git a/internal/arduino/cores/packagemanager/package_manager_test.go b/internal/arduino/cores/packagemanager/package_manager_test.go index 4f8598a515a..92530af6dc3 100644 --- a/internal/arduino/cores/packagemanager/package_manager_test.go +++ b/internal/arduino/cores/packagemanager/package_manager_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/arduino/arduino-cli/internal/arduino/cores" + "github.com/arduino/arduino-cli/pkg/fqbn" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/require" @@ -69,7 +70,7 @@ func TestResolveFQBN(t *testing.T) { t.Run("NormalizeFQBN", func(t *testing.T) { testNormalization := func(in, expected string) { - fqbn, err := cores.ParseFQBN(in) + fqbn, err := fqbn.Parse(in) require.Nil(t, err) require.NotNil(t, fqbn) normalized, err := pme.NormalizeFQBN(fqbn) @@ -92,7 +93,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesArduinoUno", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("arduino:avr:uno") + fqbn, err := fqbn.Parse("arduino:avr:uno") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -113,7 +114,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesArduinoMega", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("arduino:avr:mega") + fqbn, err := fqbn.Parse("arduino:avr:mega") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -129,7 +130,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesArduinoMegaWithNonDefaultCpuOption", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("arduino:avr:mega:cpu=atmega1280") + fqbn, err := fqbn.Parse("arduino:avr:mega:cpu=atmega1280") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -147,7 +148,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesArduinoMegaWithDefaultCpuOption", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("arduino:avr:mega:cpu=atmega2560") + fqbn, err := fqbn.Parse("arduino:avr:mega:cpu=atmega2560") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -167,7 +168,7 @@ func TestResolveFQBN(t *testing.T) { t.Run("BoardAndBuildPropertiesForReferencedArduinoUno", func(t *testing.T) { // Test a board referenced from the main AVR arduino platform - fqbn, err := cores.ParseFQBN("referenced:avr:uno") + fqbn, err := fqbn.Parse("referenced:avr:uno") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -185,7 +186,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesForArduinoDue", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("arduino:sam:arduino_due_x") + fqbn, err := fqbn.Parse("arduino:sam:arduino_due_x") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -200,7 +201,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesForCustomArduinoYun", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("my_avr_platform:avr:custom_yun") + fqbn, err := fqbn.Parse("my_avr_platform:avr:custom_yun") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -216,7 +217,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("BoardAndBuildPropertiesForWatterotCore", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("watterott:avr:attiny841:core=spencekonde,info=info") + fqbn, err := fqbn.Parse("watterott:avr:attiny841:core=spencekonde,info=info") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -234,7 +235,7 @@ func TestResolveFQBN(t *testing.T) { t.Run("BoardAndBuildPropertiesForReferencedFeatherM0", func(t *testing.T) { // Test a board referenced from the Adafruit SAMD core (this tests // deriving where the package and core name are different) - fqbn, err := cores.ParseFQBN("referenced:samd:feather_m0") + fqbn, err := fqbn.Parse("referenced:samd:feather_m0") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -253,7 +254,7 @@ func TestResolveFQBN(t *testing.T) { t.Run("BoardAndBuildPropertiesForNonExistentPackage", func(t *testing.T) { // Test a board referenced from a non-existent package - fqbn, err := cores.ParseFQBN("referenced:avr:dummy_invalid_package") + fqbn, err := fqbn.Parse("referenced:avr:dummy_invalid_package") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -270,7 +271,7 @@ func TestResolveFQBN(t *testing.T) { t.Run("BoardAndBuildPropertiesForNonExistentArchitecture", func(t *testing.T) { // Test a board referenced from a non-existent platform/architecture - fqbn, err := cores.ParseFQBN("referenced:avr:dummy_invalid_platform") + fqbn, err := fqbn.Parse("referenced:avr:dummy_invalid_platform") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -288,7 +289,7 @@ func TestResolveFQBN(t *testing.T) { t.Run("BoardAndBuildPropertiesForNonExistentCore", func(t *testing.T) { // Test a board referenced from a non-existent core // Note that ResolveFQBN does not actually check this currently - fqbn, err := cores.ParseFQBN("referenced:avr:dummy_invalid_core") + fqbn, err := fqbn.Parse("referenced:avr:dummy_invalid_core") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -306,7 +307,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("AddBuildBoardPropertyIfMissing", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("my_avr_platform:avr:mymega") + fqbn, err := fqbn.Parse("my_avr_platform:avr:mymega") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -324,7 +325,7 @@ func TestResolveFQBN(t *testing.T) { }) t.Run("AddBuildBoardPropertyIfNotMissing", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("my_avr_platform:avr:mymega:cpu=atmega1280") + fqbn, err := fqbn.Parse("my_avr_platform:avr:mymega:cpu=atmega1280") require.Nil(t, err) require.NotNil(t, fqbn) pkg, platformRelease, board, props, buildPlatformRelease, err := pme.ResolveFQBN(fqbn) @@ -813,7 +814,7 @@ func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { defer release() { - fqbn, err := cores.ParseFQBN("esp32:esp32:esp32") + fqbn, err := fqbn.Parse("esp32:esp32:esp32") require.NoError(t, err) require.NotNil(t, fqbn) _, platformRelease, board, _, _, err := pme.ResolveFQBN(fqbn) @@ -836,7 +837,7 @@ func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { require.Equal(t, "{network_cmd} -i \"{upload.port.address}\" -p \"{upload.port.properties.port}\" \"--auth={upload.field.password}\" -f \"{build.path}/{build.project_name}.bin\"", platformProps.Get("tools.esptool__pluggable_network.upload.pattern")) } { - fqbn, err := cores.ParseFQBN("esp8266:esp8266:generic") + fqbn, err := fqbn.Parse("esp8266:esp8266:generic") require.NoError(t, err) require.NotNil(t, fqbn) _, platformRelease, board, _, _, err := pme.ResolveFQBN(fqbn) @@ -858,7 +859,7 @@ func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { require.Equal(t, "\"{network_cmd}\" -I \"{runtime.platform.path}/tools/espota.py\" -i \"{upload.port.address}\" -p \"{upload.port.properties.port}\" \"--auth={upload.field.password}\" -f \"{build.path}/{build.project_name}.bin\"", platformProps.Get("tools.esptool__pluggable_network.upload.pattern")) } { - fqbn, err := cores.ParseFQBN("arduino:avr:uno") + fqbn, err := fqbn.Parse("arduino:avr:uno") require.NoError(t, err) require.NotNil(t, fqbn) _, platformRelease, board, _, _, err := pme.ResolveFQBN(fqbn) @@ -888,7 +889,7 @@ func TestVariantAndCoreSelection(t *testing.T) { // build.core test suite t.Run("CoreWithoutSubstitutions", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test") + fqbn, err := fqbn.Parse("test2:avr:test") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -897,7 +898,7 @@ func TestVariantAndCoreSelection(t *testing.T) { requireSameFile(buildProps.GetPath("build.core.path"), dataDir1.Join("packages", "test2", "hardware", "avr", "1.0.0", "cores", "arduino")) }) t.Run("CoreWithSubstitutions", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test2") + fqbn, err := fqbn.Parse("test2:avr:test2") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -907,7 +908,7 @@ func TestVariantAndCoreSelection(t *testing.T) { requireSameFile(buildProps.GetPath("build.core.path"), dataDir1.Join("packages", "test2", "hardware", "avr", "1.0.0", "cores", "arduino")) }) t.Run("CoreWithSubstitutionsAndDefaultOption", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test3") + fqbn, err := fqbn.Parse("test2:avr:test3") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -917,7 +918,7 @@ func TestVariantAndCoreSelection(t *testing.T) { requireSameFile(buildProps.GetPath("build.core.path"), dataDir1.Join("packages", "test2", "hardware", "avr", "1.0.0", "cores", "arduino")) }) t.Run("CoreWithSubstitutionsAndNonDefaultOption", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test3:core=referenced") + fqbn, err := fqbn.Parse("test2:avr:test3:core=referenced") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -929,7 +930,7 @@ func TestVariantAndCoreSelection(t *testing.T) { // build.variant test suite t.Run("VariantWithoutSubstitutions", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test4") + fqbn, err := fqbn.Parse("test2:avr:test4") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -938,7 +939,7 @@ func TestVariantAndCoreSelection(t *testing.T) { requireSameFile(buildProps.GetPath("build.variant.path"), dataDir1.Join("packages", "test2", "hardware", "avr", "1.0.0", "variants", "standard")) }) t.Run("VariantWithSubstitutions", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test5") + fqbn, err := fqbn.Parse("test2:avr:test5") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -948,7 +949,7 @@ func TestVariantAndCoreSelection(t *testing.T) { requireSameFile(buildProps.GetPath("build.variant.path"), dataDir1.Join("packages", "test2", "hardware", "avr", "1.0.0", "variants", "standard")) }) t.Run("VariantWithSubstitutionsAndDefaultOption", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test6") + fqbn, err := fqbn.Parse("test2:avr:test6") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) @@ -958,7 +959,7 @@ func TestVariantAndCoreSelection(t *testing.T) { requireSameFile(buildProps.GetPath("build.variant.path"), dataDir1.Join("packages", "test2", "hardware", "avr", "1.0.0", "variants", "standard")) }) t.Run("VariantWithSubstitutionsAndNonDefaultOption", func(t *testing.T) { - fqbn, err := cores.ParseFQBN("test2:avr:test6:variant=referenced") + fqbn, err := fqbn.Parse("test2:avr:test6:variant=referenced") require.NoError(t, err) require.NotNil(t, fqbn) _, _, _, buildProps, _, err := pme.ResolveFQBN(fqbn) diff --git a/internal/cli/board/list.go b/internal/cli/board/list.go index 7d0c0c69082..9bd6fc37f22 100644 --- a/internal/cli/board/list.go +++ b/internal/cli/board/list.go @@ -24,13 +24,13 @@ import ( "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" "github.com/arduino/arduino-cli/internal/cli/instance" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/pkg/fqbn" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -159,9 +159,9 @@ func (dr listResult) String() string { // to improve the user experience, show on a dedicated column // the name of the core supporting the board detected var coreName = "" - fqbn, err := cores.ParseFQBN(b.Fqbn) + fqbn, err := fqbn.Parse(b.Fqbn) if err == nil { - coreName = fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch) + coreName = fmt.Sprintf("%s:%s", fqbn.Packager, fqbn.Architecture) } t.AddRow(address, protocol, protocolLabel, board, fqbn, coreName) @@ -215,9 +215,9 @@ func (dr watchEventResult) String() string { // to improve the user experience, show on a dedicated column // the name of the core supporting the board detected var coreName = "" - fqbn, err := cores.ParseFQBN(b.Fqbn) + fqbn, err := fqbn.Parse(b.Fqbn) if err == nil { - coreName = fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch) + coreName = fmt.Sprintf("%s:%s", fqbn.Packager, fqbn.Architecture) } t.AddRow(address, protocol, event, board, fqbn, coreName) diff --git a/internal/arduino/cores/fqbn.go b/pkg/fqbn/fqbn.go similarity index 80% rename from internal/arduino/cores/fqbn.go rename to pkg/fqbn/fqbn.go index 0db32f45cb0..14fa1b4a58b 100644 --- a/internal/arduino/cores/fqbn.go +++ b/pkg/fqbn/fqbn.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package cores +package fqbn import ( "errors" @@ -24,35 +24,35 @@ import ( properties "github.com/arduino/go-properties-orderedmap" ) -// FQBN represents a Board with a specific configuration +// FQBN represents an Fully Qualified Board Name string type FQBN struct { - Package string - PlatformArch string + Packager string + Architecture string BoardID string Configs *properties.Map } -// MustParseFQBN extract an FQBN object from the input string +// MustParse parse an FQBN string from the input string // or panics if the input is not a valid FQBN. -func MustParseFQBN(fqbnIn string) *FQBN { - res, err := ParseFQBN(fqbnIn) +func MustParse(fqbnIn string) *FQBN { + res, err := Parse(fqbnIn) if err != nil { panic(err) } return res } -// ParseFQBN extract an FQBN object from the input string -func ParseFQBN(fqbnIn string) (*FQBN, error) { - // Split fqbn +// Parse parses an FQBN string from the input string +func Parse(fqbnIn string) (*FQBN, error) { + // Split fqbn parts fqbnParts := strings.Split(fqbnIn, ":") if len(fqbnParts) < 3 || len(fqbnParts) > 4 { return nil, errors.New(i18n.Tr("not an FQBN: %s", fqbnIn)) } fqbn := &FQBN{ - Package: fqbnParts[0], - PlatformArch: fqbnParts[1], + Packager: fqbnParts[0], + Architecture: fqbnParts[1], BoardID: fqbnParts[2], Configs: properties.NewMap(), } @@ -91,29 +91,17 @@ func ParseFQBN(fqbnIn string) (*FQBN, error) { return fqbn, nil } -func (fqbn *FQBN) String() string { - res := fqbn.StringWithoutConfig() - if fqbn.Configs.Size() > 0 { - sep := ":" - for _, k := range fqbn.Configs.Keys() { - res += sep + k + "=" + fqbn.Configs.Get(k) - sep = "," - } - } - return res -} - // Clone returns a copy of this FQBN. func (fqbn *FQBN) Clone() *FQBN { return &FQBN{ - Package: fqbn.Package, - PlatformArch: fqbn.PlatformArch, + Packager: fqbn.Packager, + Architecture: fqbn.Architecture, BoardID: fqbn.BoardID, Configs: fqbn.Configs.Clone(), } } -// Match check if the target FQBN corresponds to the receiver one. +// Match checks if the target FQBN equals to this one. // The core parts are checked for exact equality while board options are loosely // matched: the set of boards options of the target must be fully contained within // the one of the receiver and their values must be equal. @@ -122,10 +110,8 @@ func (fqbn *FQBN) Match(target *FQBN) bool { return false } - searchedProperties := target.Configs.Clone() - actualConfigs := fqbn.Configs.AsMap() - for neededKey, neededValue := range searchedProperties.AsMap() { - targetValue, hasKey := actualConfigs[neededKey] + for neededKey, neededValue := range target.Configs.AsMap() { + targetValue, hasKey := fqbn.Configs.GetOk(neededKey) if !hasKey || targetValue != neededValue { return false } @@ -135,5 +121,18 @@ func (fqbn *FQBN) Match(target *FQBN) bool { // StringWithoutConfig returns the FQBN without the Config part func (fqbn *FQBN) StringWithoutConfig() string { - return fqbn.Package + ":" + fqbn.PlatformArch + ":" + fqbn.BoardID + return fqbn.Packager + ":" + fqbn.Architecture + ":" + fqbn.BoardID +} + +// String returns the FQBN as a string +func (fqbn *FQBN) String() string { + res := fqbn.StringWithoutConfig() + if fqbn.Configs.Size() > 0 { + sep := ":" + for _, k := range fqbn.Configs.Keys() { + res += sep + k + "=" + fqbn.Configs.Get(k) + sep = "," + } + } + return res } diff --git a/internal/arduino/cores/fqbn_test.go b/pkg/fqbn/fqbn_test.go similarity index 71% rename from internal/arduino/cores/fqbn_test.go rename to pkg/fqbn/fqbn_test.go index c7165af064a..7c5bc21fbf6 100644 --- a/internal/arduino/cores/fqbn_test.go +++ b/pkg/fqbn/fqbn_test.go @@ -13,67 +13,68 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package cores +package fqbn_test import ( "testing" + "github.com/arduino/arduino-cli/pkg/fqbn" "github.com/stretchr/testify/require" ) func TestFQBN(t *testing.T) { - a, err := ParseFQBN("arduino:avr:uno") + a, err := fqbn.Parse("arduino:avr:uno") require.Equal(t, "arduino:avr:uno", a.String()) require.NoError(t, err) - require.Equal(t, a.Package, "arduino") - require.Equal(t, a.PlatformArch, "avr") + require.Equal(t, a.Packager, "arduino") + require.Equal(t, a.Architecture, "avr") require.Equal(t, a.BoardID, "uno") require.Zero(t, a.Configs.Size()) // Allow empty platforms or packages (aka. vendors + architectures) - b1, err := ParseFQBN("arduino::uno") + b1, err := fqbn.Parse("arduino::uno") require.Equal(t, "arduino::uno", b1.String()) require.NoError(t, err) - require.Equal(t, b1.Package, "arduino") - require.Equal(t, b1.PlatformArch, "") + require.Equal(t, b1.Packager, "arduino") + require.Equal(t, b1.Architecture, "") require.Equal(t, b1.BoardID, "uno") require.Zero(t, b1.Configs.Size()) - b2, err := ParseFQBN(":avr:uno") + b2, err := fqbn.Parse(":avr:uno") require.Equal(t, ":avr:uno", b2.String()) require.NoError(t, err) - require.Equal(t, b2.Package, "") - require.Equal(t, b2.PlatformArch, "avr") + require.Equal(t, b2.Packager, "") + require.Equal(t, b2.Architecture, "avr") require.Equal(t, b2.BoardID, "uno") require.Zero(t, b2.Configs.Size()) - b3, err := ParseFQBN("::uno") + b3, err := fqbn.Parse("::uno") require.Equal(t, "::uno", b3.String()) require.NoError(t, err) - require.Equal(t, b3.Package, "") - require.Equal(t, b3.PlatformArch, "") + require.Equal(t, b3.Packager, "") + require.Equal(t, b3.Architecture, "") require.Equal(t, b3.BoardID, "uno") require.Zero(t, b3.Configs.Size()) // Do not allow missing board identifier - _, err = ParseFQBN("arduino:avr:") + _, err = fqbn.Parse("arduino:avr:") require.Error(t, err) // Do not allow partial fqbn - _, err = ParseFQBN("arduino") + _, err = fqbn.Parse("arduino") require.Error(t, err) - _, err = ParseFQBN("arduino:avr") + _, err = fqbn.Parse("arduino:avr") require.Error(t, err) // Keeps the config keys order - s1, err := ParseFQBN("arduino:avr:uno:d=x,b=x,a=x,e=x,c=x") + s1, err := fqbn.Parse("arduino:avr:uno:d=x,b=x,a=x,e=x,c=x") require.NoError(t, err) require.Equal(t, "arduino:avr:uno:d=x,b=x,a=x,e=x,c=x", s1.String()) require.Equal(t, "properties.Map{\n \"d\": \"x\",\n \"b\": \"x\",\n \"a\": \"x\",\n \"e\": \"x\",\n \"c\": \"x\",\n}", s1.Configs.Dump()) - s2, err := ParseFQBN("arduino:avr:uno:a=x,b=x,c=x,d=x,e=x") + s2, err := fqbn.Parse("arduino:avr:uno:a=x,b=x,c=x,d=x,e=x") require.NoError(t, err) require.Equal(t, "arduino:avr:uno:a=x,b=x,c=x,d=x,e=x", s2.String()) require.Equal(t, @@ -85,53 +86,53 @@ func TestFQBN(t *testing.T) { require.NotEqual(t, s1.String(), s2.String()) // Test configs - c, err := ParseFQBN("arduino:avr:uno:cpu=atmega") + c, err := fqbn.Parse("arduino:avr:uno:cpu=atmega") require.Equal(t, "arduino:avr:uno:cpu=atmega", c.String()) require.NoError(t, err) - require.Equal(t, c.Package, "arduino") - require.Equal(t, c.PlatformArch, "avr") + require.Equal(t, c.Packager, "arduino") + require.Equal(t, c.Architecture, "avr") require.Equal(t, c.BoardID, "uno") require.Equal(t, "properties.Map{\n \"cpu\": \"atmega\",\n}", c.Configs.Dump()) - d, err := ParseFQBN("arduino:avr:uno:cpu=atmega,speed=1000") + d, err := fqbn.Parse("arduino:avr:uno:cpu=atmega,speed=1000") require.Equal(t, "arduino:avr:uno:cpu=atmega,speed=1000", d.String()) require.NoError(t, err) - require.Equal(t, d.Package, "arduino") - require.Equal(t, d.PlatformArch, "avr") + require.Equal(t, d.Packager, "arduino") + require.Equal(t, d.Architecture, "avr") require.Equal(t, d.BoardID, "uno") require.Equal(t, "properties.Map{\n \"cpu\": \"atmega\",\n \"speed\": \"1000\",\n}", d.Configs.Dump()) // Do not allow empty keys or missing values in config - _, err = ParseFQBN("arduino:avr:uno:") + _, err = fqbn.Parse("arduino:avr:uno:") require.Error(t, err) - _, err = ParseFQBN("arduino:avr:uno,") + _, err = fqbn.Parse("arduino:avr:uno,") require.Error(t, err) - _, err = ParseFQBN("arduino:avr:uno:cpu") + _, err = fqbn.Parse("arduino:avr:uno:cpu") require.Error(t, err) - _, err = ParseFQBN("arduino:avr:uno:=atmega") + _, err = fqbn.Parse("arduino:avr:uno:=atmega") require.Error(t, err) - _, err = ParseFQBN("arduino:avr:uno:cpu=atmega,") + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,") require.Error(t, err) - _, err = ParseFQBN("arduino:avr:uno:cpu=atmega,speed") + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,speed") require.Error(t, err) - _, err = ParseFQBN("arduino:avr:uno:cpu=atmega,=1000") + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,=1000") require.Error(t, err) // Allow keys with empty values - e, err := ParseFQBN("arduino:avr:uno:cpu=") + e, err := fqbn.Parse("arduino:avr:uno:cpu=") require.Equal(t, "arduino:avr:uno:cpu=", e.String()) require.NoError(t, err) - require.Equal(t, e.Package, "arduino") - require.Equal(t, e.PlatformArch, "avr") + require.Equal(t, e.Packager, "arduino") + require.Equal(t, e.Architecture, "avr") require.Equal(t, e.BoardID, "uno") require.Equal(t, "properties.Map{\n \"cpu\": \"\",\n}", e.Configs.Dump()) // Allow "=" in config values - f, err := ParseFQBN("arduino:avr:uno:cpu=atmega,speed=1000,extra=core=arduino") + f, err := fqbn.Parse("arduino:avr:uno:cpu=atmega,speed=1000,extra=core=arduino") require.Equal(t, "arduino:avr:uno:cpu=atmega,speed=1000,extra=core=arduino", f.String()) require.NoError(t, err) - require.Equal(t, f.Package, "arduino") - require.Equal(t, f.PlatformArch, "avr") + require.Equal(t, f.Packager, "arduino") + require.Equal(t, f.Architecture, "avr") require.Equal(t, f.BoardID, "uno") require.Equal(t, "properties.Map{\n \"cpu\": \"atmega\",\n \"speed\": \"1000\",\n \"extra\": \"core=arduino\",\n}", @@ -148,9 +149,9 @@ func TestMatch(t *testing.T) { } for _, pair := range expectedMatches { - a, err := ParseFQBN(pair[0]) + a, err := fqbn.Parse(pair[0]) require.NoError(t, err) - b, err := ParseFQBN(pair[1]) + b, err := fqbn.Parse(pair[1]) require.NoError(t, err) require.True(t, b.Match(a)) } @@ -164,9 +165,9 @@ func TestMatch(t *testing.T) { } for _, pair := range expectedMismatches { - a, err := ParseFQBN(pair[0]) + a, err := fqbn.Parse(pair[0]) require.NoError(t, err) - b, err := ParseFQBN(pair[1]) + b, err := fqbn.Parse(pair[1]) require.NoError(t, err) require.False(t, b.Match(a)) } @@ -175,14 +176,14 @@ func TestMatch(t *testing.T) { func TestValidCharacters(t *testing.T) { // These FQBNs contain valid characters validFqbns := []string{"ardui_no:av_r:un_o", "arduin.o:av.r:un.o", "arduin-o:av-r:un-o", "arduin-o:av-r:un-o:a=b=c=d"} - for _, fqbn := range validFqbns { - _, err := ParseFQBN(fqbn) + for _, validFqbn := range validFqbns { + _, err := fqbn.Parse(validFqbn) require.NoError(t, err) } // These FQBNs contain invalid characters invalidFqbns := []string{"arduin-o:av-r:un=o", "arduin?o:av-r:uno", "arduino:av*r:uno"} - for _, fqbn := range invalidFqbns { - _, err := ParseFQBN(fqbn) + for _, validFqbn := range invalidFqbns { + _, err := fqbn.Parse(validFqbn) require.Error(t, err) } } From 3e13de952621bf94433449e1e24a87dea173b2ad Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 27 Nov 2024 18:17:07 +0100 Subject: [PATCH 2/6] 100% test coverage --- pkg/fqbn/fqbn_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pkg/fqbn/fqbn_test.go b/pkg/fqbn/fqbn_test.go index 7c5bc21fbf6..be76dad2f27 100644 --- a/pkg/fqbn/fqbn_test.go +++ b/pkg/fqbn/fqbn_test.go @@ -137,6 +137,27 @@ func TestFQBN(t *testing.T) { require.Equal(t, "properties.Map{\n \"cpu\": \"atmega\",\n \"speed\": \"1000\",\n \"extra\": \"core=arduino\",\n}", f.Configs.Dump()) + + // Check invalid characters in config keys + _, err = fqbn.Parse("arduino:avr:uno:cpu@=atmega") + require.Error(t, err) + _, err = fqbn.Parse("arduino:avr:uno:cpu@atmega") + require.Error(t, err) + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,speed@=1000") + require.Error(t, err) + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,speed@1000") + require.Error(t, err) + + // Check invalid characters in config values + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega@") + require.Error(t, err) + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega@extra") + require.Error(t, err) + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,speed=1000@") + require.Error(t, err) + _, err = fqbn.Parse("arduino:avr:uno:cpu=atmega,speed=1000@extra") + require.Error(t, err) + } func TestMatch(t *testing.T) { @@ -187,3 +208,21 @@ func TestValidCharacters(t *testing.T) { require.Error(t, err) } } + +func TestMustParse(t *testing.T) { + require.NotPanics(t, func() { fqbn.MustParse("arduino:avr:uno") }) + require.Panics(t, func() { fqbn.MustParse("ard=uino:avr=:u=no") }) +} + +func TestClone(t *testing.T) { + a, err := fqbn.Parse("arduino:avr:uno:opt1=1,opt2=2") + require.NoError(t, err) + b := a.Clone() + require.True(t, b.Match(a)) + require.True(t, a.Match(b)) + + c, err := fqbn.Parse("arduino:avr:uno:opt1=1,opt2=2,opt3=3") + require.NoError(t, err) + require.True(t, c.Match(a)) + require.False(t, a.Match(c)) +} From dd8c35ac1cb2da77bd668ca04f80c2034781f8a0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Nov 2024 10:53:38 +0100 Subject: [PATCH 3/6] Precompile validation regexp --- pkg/fqbn/fqbn.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/fqbn/fqbn.go b/pkg/fqbn/fqbn.go index 14fa1b4a58b..8773272dd04 100644 --- a/pkg/fqbn/fqbn.go +++ b/pkg/fqbn/fqbn.go @@ -42,6 +42,9 @@ func MustParse(fqbnIn string) *FQBN { return res } +var fqbnValidationRegex = regexp.MustCompile(`^[a-zA-Z0-9_.-]*$`) +var valueValidationRegex = regexp.MustCompile(`^[a-zA-Z0-9=_.-]*$`) + // Parse parses an FQBN string from the input string func Parse(fqbnIn string) (*FQBN, error) { // Split fqbn parts @@ -60,7 +63,6 @@ func Parse(fqbnIn string) (*FQBN, error) { return nil, errors.New(i18n.Tr("empty board identifier")) } // Check if the fqbn contains invalid characters - fqbnValidationRegex := regexp.MustCompile(`^[a-zA-Z0-9_.-]*$`) for i := 0; i < 3; i++ { if !fqbnValidationRegex.MatchString(fqbnParts[i]) { return nil, errors.New(i18n.Tr("fqbn's field %s contains an invalid character", fqbnParts[i])) @@ -81,7 +83,6 @@ func Parse(fqbnIn string) (*FQBN, error) { return nil, errors.New(i18n.Tr("config key %s contains an invalid character", k)) } // The config value can also contain the = symbol - valueValidationRegex := regexp.MustCompile(`^[a-zA-Z0-9=_.-]*$`) if !valueValidationRegex.MatchString(v) { return nil, errors.New(i18n.Tr("config value %s contains an invalid character", v)) } From a3c9f72668fbb4134da6e6c0b3ac9aaba46e7916 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Nov 2024 11:15:49 +0100 Subject: [PATCH 4/6] Remove logrus dependency from i18n --- internal/i18n/detect_windows.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/i18n/detect_windows.go b/internal/i18n/detect_windows.go index 84bc3af7997..f0adbce7718 100644 --- a/internal/i18n/detect_windows.go +++ b/internal/i18n/detect_windows.go @@ -19,14 +19,12 @@ import ( "strings" "syscall" "unsafe" - - "github.com/sirupsen/logrus" ) func getLocaleIdentifier() string { defer func() { if r := recover(); r != nil { - logrus.WithField("error", r).Errorf("Failed to get windows user locale") + // ignore error and do not panic } }() From b87a21542de2b3c29eefcd04ae9bcefedf0fbc19 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Nov 2024 11:31:52 +0100 Subject: [PATCH 5/6] Isolate locale-handling functions from i18n interface This changes allows to make a clean i18n package (without dependency on a specific implementation of the translation package) that, in turn, allows to export packages that internally use i18n with the minimal dependency load. --- Taskfile.yml | 10 ++--- commands/instances.go | 3 +- internal/i18n/i18n.go | 41 +++++++++---------- internal/i18n/i18n_test.go | 7 ++-- internal/{i18n => locales}/README.md | 0 internal/{i18n => locales}/cmd/ast/parser.go | 2 +- .../cmd/commands/catalog/catalog.go | 0 .../cmd/commands/catalog/generate_catalog.go | 2 +- .../{i18n => locales}/cmd/commands/root.go | 4 +- .../cmd/commands/transifex/pull_transifex.go | 0 .../cmd/commands/transifex/push_transifex.go | 0 .../cmd/commands/transifex/transifex.go | 0 internal/{i18n => locales}/cmd/main.go | 2 +- internal/{i18n => locales}/cmd/po/catalog.go | 0 .../{i18n => locales}/cmd/po/catalog_test.go | 0 internal/{i18n => locales}/cmd/po/merge.go | 0 .../{i18n => locales}/cmd/po/merge_test.go | 0 internal/{i18n => locales}/cmd/po/parser.go | 0 .../{i18n => locales}/cmd/po/parser_test.go | 0 internal/{i18n => locales}/convert.go | 2 +- internal/{i18n => locales}/convert_test.go | 2 +- internal/{i18n => locales}/data/.gitkeep | 0 internal/{i18n => locales}/data/README.md | 0 internal/{i18n => locales}/data/ar.po | 0 internal/{i18n => locales}/data/be.po | 0 internal/{i18n => locales}/data/de.po | 0 internal/{i18n => locales}/data/en.po | 0 internal/{i18n => locales}/data/es.po | 0 internal/{i18n => locales}/data/fr.po | 0 internal/{i18n => locales}/data/he.po | 0 internal/{i18n => locales}/data/it_IT.po | 0 internal/{i18n => locales}/data/ja.po | 0 internal/{i18n => locales}/data/kk.po | 0 internal/{i18n => locales}/data/ko.po | 0 internal/{i18n => locales}/data/lb.po | 0 internal/{i18n => locales}/data/mn.po | 0 internal/{i18n => locales}/data/my_MM.po | 0 internal/{i18n => locales}/data/ne.po | 0 internal/{i18n => locales}/data/pl.po | 0 internal/{i18n => locales}/data/pt.po | 0 internal/{i18n => locales}/data/pt_BR.po | 0 internal/{i18n => locales}/data/ru.po | 0 internal/{i18n => locales}/data/zh.po | 0 internal/{i18n => locales}/data/zh_TW.po | 0 internal/{i18n => locales}/detect.go | 2 +- .../{i18n => locales}/detect_cgo_darwin.go | 2 +- internal/{i18n => locales}/detect_freebsd.go | 2 +- internal/{i18n => locales}/detect_linux.go | 2 +- .../{i18n => locales}/detect_nocgo_darwin.go | 2 +- internal/{i18n => locales}/detect_windows.go | 2 +- internal/locales/i18n.go | 39 ++++++++++++++++++ internal/{i18n => locales}/locale.go | 14 +++---- internal/{i18n => locales}/locale_test.go | 2 +- main.go | 3 +- 54 files changed, 90 insertions(+), 55 deletions(-) rename internal/{i18n => locales}/README.md (100%) rename internal/{i18n => locales}/cmd/ast/parser.go (97%) rename internal/{i18n => locales}/cmd/commands/catalog/catalog.go (100%) rename internal/{i18n => locales}/cmd/commands/catalog/generate_catalog.go (95%) rename internal/{i18n => locales}/cmd/commands/root.go (87%) rename internal/{i18n => locales}/cmd/commands/transifex/pull_transifex.go (100%) rename internal/{i18n => locales}/cmd/commands/transifex/push_transifex.go (100%) rename internal/{i18n => locales}/cmd/commands/transifex/transifex.go (100%) rename internal/{i18n => locales}/cmd/main.go (93%) rename internal/{i18n => locales}/cmd/po/catalog.go (100%) rename internal/{i18n => locales}/cmd/po/catalog_test.go (100%) rename internal/{i18n => locales}/cmd/po/merge.go (100%) rename internal/{i18n => locales}/cmd/po/merge_test.go (100%) rename internal/{i18n => locales}/cmd/po/parser.go (100%) rename internal/{i18n => locales}/cmd/po/parser_test.go (100%) rename internal/{i18n => locales}/convert.go (98%) rename internal/{i18n => locales}/convert_test.go (99%) rename internal/{i18n => locales}/data/.gitkeep (100%) rename internal/{i18n => locales}/data/README.md (100%) rename internal/{i18n => locales}/data/ar.po (100%) rename internal/{i18n => locales}/data/be.po (100%) rename internal/{i18n => locales}/data/de.po (100%) rename internal/{i18n => locales}/data/en.po (100%) rename internal/{i18n => locales}/data/es.po (100%) rename internal/{i18n => locales}/data/fr.po (100%) rename internal/{i18n => locales}/data/he.po (100%) rename internal/{i18n => locales}/data/it_IT.po (100%) rename internal/{i18n => locales}/data/ja.po (100%) rename internal/{i18n => locales}/data/kk.po (100%) rename internal/{i18n => locales}/data/ko.po (100%) rename internal/{i18n => locales}/data/lb.po (100%) rename internal/{i18n => locales}/data/mn.po (100%) rename internal/{i18n => locales}/data/my_MM.po (100%) rename internal/{i18n => locales}/data/ne.po (100%) rename internal/{i18n => locales}/data/pl.po (100%) rename internal/{i18n => locales}/data/pt.po (100%) rename internal/{i18n => locales}/data/pt_BR.po (100%) rename internal/{i18n => locales}/data/ru.po (100%) rename internal/{i18n => locales}/data/zh.po (100%) rename internal/{i18n => locales}/data/zh_TW.po (100%) rename internal/{i18n => locales}/detect.go (98%) rename internal/{i18n => locales}/detect_cgo_darwin.go (98%) rename internal/{i18n => locales}/detect_freebsd.go (98%) rename internal/{i18n => locales}/detect_linux.go (98%) rename internal/{i18n => locales}/detect_nocgo_darwin.go (98%) rename internal/{i18n => locales}/detect_windows.go (98%) create mode 100644 internal/locales/i18n.go rename internal/{i18n => locales}/locale.go (93%) rename internal/{i18n => locales}/locale_test.go (98%) diff --git a/Taskfile.yml b/Taskfile.yml index 095cae2dd87..6dac2c66d54 100755 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -309,24 +309,24 @@ tasks: i18n:update: desc: Updates i18n files cmds: - - go run ./internal/i18n/cmd/main.go catalog generate . > ./internal/i18n/data/en.po + - go run ./internal/locales/cmd/main.go catalog generate . > ./internal/locales/data/en.po i18n:pull: desc: Pull i18n files from transifex cmds: - - go run ./internal/i18n/cmd/main.go transifex pull ./internal/i18n/data + - go run ./internal/locales/cmd/main.go transifex pull ./internal/locales/data i18n:push: desc: Push i18n files to transifex cmds: - - go run ./internal/i18n/cmd/main.go transifex push ./internal/i18n/data + - go run ./internal/locales/cmd/main.go transifex push ./internal/locales/data i18n:check: desc: Check if the i18n message catalog was updated cmds: - task: i18n:pull - - git add -N ./internal/i18n/data - - git diff --exit-code ./internal/i18n/data + - git add -N ./internal/locales/data + - git diff --exit-code ./internal/locales/data # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-mkdocs-task/Taskfile.yml website:check: diff --git a/commands/instances.go b/commands/instances.go index 23f42be9638..3c023301cd4 100644 --- a/commands/instances.go +++ b/commands/instances.go @@ -38,6 +38,7 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/arduino/utils" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/internal/locales" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" @@ -420,7 +421,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor // language of the CLI if the locale is different // after started. if locale, ok, _ := s.settings.GetStringOk("locale"); ok { - i18n.Init(locale) + locales.Init(locale) } return nil diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go index aa4f82edd33..f1941297b00 100644 --- a/internal/i18n/i18n.go +++ b/internal/i18n/i18n.go @@ -15,31 +15,28 @@ package i18n -// Init initializes the i18n module, setting the locale according to this order of preference: -// 1. Locale specified via the function call -// 2. OS Locale -// 3. en (default) -func Init(configLocale string) { - locales := supportedLocales() - if configLocale != "" { - if locale := findMatchingLocale(configLocale, locales); locale != "" { - setLocale(locale) - return - } - } - - if osLocale := getLocaleIdentifierFromOS(); osLocale != "" { - if locale := findMatchingLocale(osLocale, locales); locale != "" { - setLocale(locale) - return - } - } - - setLocale("en") +import "fmt" + +type Locale interface { + Get(msg string, args ...interface{}) string +} + +type nullLocale struct{} + +func (n nullLocale) Parse([]byte) {} + +func (n nullLocale) Get(msg string, args ...interface{}) string { + return fmt.Sprintf(msg, args...) +} + +var locale Locale = &nullLocale{} + +func SetLocale(l Locale) { + locale = l } // Tr returns msg translated to the selected locale // the msg argument must be a literal string func Tr(msg string, args ...interface{}) string { - return po.Get(msg, args...) + return locale.Get(msg, args...) } diff --git a/internal/i18n/i18n_test.go b/internal/i18n/i18n_test.go index 1ec157db01e..a2ae4c4d7b0 100644 --- a/internal/i18n/i18n_test.go +++ b/internal/i18n/i18n_test.go @@ -25,8 +25,9 @@ import ( ) func setPo(poFile string) { - po = gotext.NewPo() - po.Parse([]byte(poFile)) + dict := gotext.NewPo() + dict.Parse([]byte(poFile)) + SetLocale(dict) } func TestPoTranslation(t *testing.T) { @@ -39,7 +40,7 @@ func TestPoTranslation(t *testing.T) { } func TestNoLocaleSet(t *testing.T) { - po = gotext.NewPo() + locale = gotext.NewPo() require.Equal(t, "test-key", Tr("test-key")) } diff --git a/internal/i18n/README.md b/internal/locales/README.md similarity index 100% rename from internal/i18n/README.md rename to internal/locales/README.md diff --git a/internal/i18n/cmd/ast/parser.go b/internal/locales/cmd/ast/parser.go similarity index 97% rename from internal/i18n/cmd/ast/parser.go rename to internal/locales/cmd/ast/parser.go index d8d558cd975..69648c70c1b 100644 --- a/internal/i18n/cmd/ast/parser.go +++ b/internal/locales/cmd/ast/parser.go @@ -24,7 +24,7 @@ import ( "path/filepath" "strconv" - "github.com/arduino/arduino-cli/internal/i18n/cmd/po" + "github.com/arduino/arduino-cli/internal/locales/cmd/po" ) // GenerateCatalog generates the i18n message catalog for the go source files diff --git a/internal/i18n/cmd/commands/catalog/catalog.go b/internal/locales/cmd/commands/catalog/catalog.go similarity index 100% rename from internal/i18n/cmd/commands/catalog/catalog.go rename to internal/locales/cmd/commands/catalog/catalog.go diff --git a/internal/i18n/cmd/commands/catalog/generate_catalog.go b/internal/locales/cmd/commands/catalog/generate_catalog.go similarity index 95% rename from internal/i18n/cmd/commands/catalog/generate_catalog.go rename to internal/locales/cmd/commands/catalog/generate_catalog.go index 1c65c640ffc..27ac0e76d45 100644 --- a/internal/i18n/cmd/commands/catalog/generate_catalog.go +++ b/internal/locales/cmd/commands/catalog/generate_catalog.go @@ -19,7 +19,7 @@ import ( "os" "path/filepath" - "github.com/arduino/arduino-cli/internal/i18n/cmd/ast" + "github.com/arduino/arduino-cli/internal/locales/cmd/ast" "github.com/spf13/cobra" ) diff --git a/internal/i18n/cmd/commands/root.go b/internal/locales/cmd/commands/root.go similarity index 87% rename from internal/i18n/cmd/commands/root.go rename to internal/locales/cmd/commands/root.go index 14559dcfee9..fc5d8eb37e0 100644 --- a/internal/i18n/cmd/commands/root.go +++ b/internal/locales/cmd/commands/root.go @@ -16,8 +16,8 @@ package commands import ( - "github.com/arduino/arduino-cli/internal/i18n/cmd/commands/catalog" - "github.com/arduino/arduino-cli/internal/i18n/cmd/commands/transifex" + "github.com/arduino/arduino-cli/internal/locales/cmd/commands/catalog" + "github.com/arduino/arduino-cli/internal/locales/cmd/commands/transifex" "github.com/spf13/cobra" ) diff --git a/internal/i18n/cmd/commands/transifex/pull_transifex.go b/internal/locales/cmd/commands/transifex/pull_transifex.go similarity index 100% rename from internal/i18n/cmd/commands/transifex/pull_transifex.go rename to internal/locales/cmd/commands/transifex/pull_transifex.go diff --git a/internal/i18n/cmd/commands/transifex/push_transifex.go b/internal/locales/cmd/commands/transifex/push_transifex.go similarity index 100% rename from internal/i18n/cmd/commands/transifex/push_transifex.go rename to internal/locales/cmd/commands/transifex/push_transifex.go diff --git a/internal/i18n/cmd/commands/transifex/transifex.go b/internal/locales/cmd/commands/transifex/transifex.go similarity index 100% rename from internal/i18n/cmd/commands/transifex/transifex.go rename to internal/locales/cmd/commands/transifex/transifex.go diff --git a/internal/i18n/cmd/main.go b/internal/locales/cmd/main.go similarity index 93% rename from internal/i18n/cmd/main.go rename to internal/locales/cmd/main.go index 8752c9d668e..d31f1589e09 100644 --- a/internal/i18n/cmd/main.go +++ b/internal/locales/cmd/main.go @@ -19,7 +19,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/internal/i18n/cmd/commands" + "github.com/arduino/arduino-cli/internal/locales/cmd/commands" ) func main() { diff --git a/internal/i18n/cmd/po/catalog.go b/internal/locales/cmd/po/catalog.go similarity index 100% rename from internal/i18n/cmd/po/catalog.go rename to internal/locales/cmd/po/catalog.go diff --git a/internal/i18n/cmd/po/catalog_test.go b/internal/locales/cmd/po/catalog_test.go similarity index 100% rename from internal/i18n/cmd/po/catalog_test.go rename to internal/locales/cmd/po/catalog_test.go diff --git a/internal/i18n/cmd/po/merge.go b/internal/locales/cmd/po/merge.go similarity index 100% rename from internal/i18n/cmd/po/merge.go rename to internal/locales/cmd/po/merge.go diff --git a/internal/i18n/cmd/po/merge_test.go b/internal/locales/cmd/po/merge_test.go similarity index 100% rename from internal/i18n/cmd/po/merge_test.go rename to internal/locales/cmd/po/merge_test.go diff --git a/internal/i18n/cmd/po/parser.go b/internal/locales/cmd/po/parser.go similarity index 100% rename from internal/i18n/cmd/po/parser.go rename to internal/locales/cmd/po/parser.go diff --git a/internal/i18n/cmd/po/parser_test.go b/internal/locales/cmd/po/parser_test.go similarity index 100% rename from internal/i18n/cmd/po/parser_test.go rename to internal/locales/cmd/po/parser_test.go diff --git a/internal/i18n/convert.go b/internal/locales/convert.go similarity index 98% rename from internal/i18n/convert.go rename to internal/locales/convert.go index 7b6d2e8dbdf..e7aa8227571 100644 --- a/internal/i18n/convert.go +++ b/internal/locales/convert.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales import ( "regexp" diff --git a/internal/i18n/convert_test.go b/internal/locales/convert_test.go similarity index 99% rename from internal/i18n/convert_test.go rename to internal/locales/convert_test.go index 618fa977b3b..723d6118c2b 100644 --- a/internal/i18n/convert_test.go +++ b/internal/locales/convert_test.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales import ( "fmt" diff --git a/internal/i18n/data/.gitkeep b/internal/locales/data/.gitkeep similarity index 100% rename from internal/i18n/data/.gitkeep rename to internal/locales/data/.gitkeep diff --git a/internal/i18n/data/README.md b/internal/locales/data/README.md similarity index 100% rename from internal/i18n/data/README.md rename to internal/locales/data/README.md diff --git a/internal/i18n/data/ar.po b/internal/locales/data/ar.po similarity index 100% rename from internal/i18n/data/ar.po rename to internal/locales/data/ar.po diff --git a/internal/i18n/data/be.po b/internal/locales/data/be.po similarity index 100% rename from internal/i18n/data/be.po rename to internal/locales/data/be.po diff --git a/internal/i18n/data/de.po b/internal/locales/data/de.po similarity index 100% rename from internal/i18n/data/de.po rename to internal/locales/data/de.po diff --git a/internal/i18n/data/en.po b/internal/locales/data/en.po similarity index 100% rename from internal/i18n/data/en.po rename to internal/locales/data/en.po diff --git a/internal/i18n/data/es.po b/internal/locales/data/es.po similarity index 100% rename from internal/i18n/data/es.po rename to internal/locales/data/es.po diff --git a/internal/i18n/data/fr.po b/internal/locales/data/fr.po similarity index 100% rename from internal/i18n/data/fr.po rename to internal/locales/data/fr.po diff --git a/internal/i18n/data/he.po b/internal/locales/data/he.po similarity index 100% rename from internal/i18n/data/he.po rename to internal/locales/data/he.po diff --git a/internal/i18n/data/it_IT.po b/internal/locales/data/it_IT.po similarity index 100% rename from internal/i18n/data/it_IT.po rename to internal/locales/data/it_IT.po diff --git a/internal/i18n/data/ja.po b/internal/locales/data/ja.po similarity index 100% rename from internal/i18n/data/ja.po rename to internal/locales/data/ja.po diff --git a/internal/i18n/data/kk.po b/internal/locales/data/kk.po similarity index 100% rename from internal/i18n/data/kk.po rename to internal/locales/data/kk.po diff --git a/internal/i18n/data/ko.po b/internal/locales/data/ko.po similarity index 100% rename from internal/i18n/data/ko.po rename to internal/locales/data/ko.po diff --git a/internal/i18n/data/lb.po b/internal/locales/data/lb.po similarity index 100% rename from internal/i18n/data/lb.po rename to internal/locales/data/lb.po diff --git a/internal/i18n/data/mn.po b/internal/locales/data/mn.po similarity index 100% rename from internal/i18n/data/mn.po rename to internal/locales/data/mn.po diff --git a/internal/i18n/data/my_MM.po b/internal/locales/data/my_MM.po similarity index 100% rename from internal/i18n/data/my_MM.po rename to internal/locales/data/my_MM.po diff --git a/internal/i18n/data/ne.po b/internal/locales/data/ne.po similarity index 100% rename from internal/i18n/data/ne.po rename to internal/locales/data/ne.po diff --git a/internal/i18n/data/pl.po b/internal/locales/data/pl.po similarity index 100% rename from internal/i18n/data/pl.po rename to internal/locales/data/pl.po diff --git a/internal/i18n/data/pt.po b/internal/locales/data/pt.po similarity index 100% rename from internal/i18n/data/pt.po rename to internal/locales/data/pt.po diff --git a/internal/i18n/data/pt_BR.po b/internal/locales/data/pt_BR.po similarity index 100% rename from internal/i18n/data/pt_BR.po rename to internal/locales/data/pt_BR.po diff --git a/internal/i18n/data/ru.po b/internal/locales/data/ru.po similarity index 100% rename from internal/i18n/data/ru.po rename to internal/locales/data/ru.po diff --git a/internal/i18n/data/zh.po b/internal/locales/data/zh.po similarity index 100% rename from internal/i18n/data/zh.po rename to internal/locales/data/zh.po diff --git a/internal/i18n/data/zh_TW.po b/internal/locales/data/zh_TW.po similarity index 100% rename from internal/i18n/data/zh_TW.po rename to internal/locales/data/zh_TW.po diff --git a/internal/i18n/detect.go b/internal/locales/detect.go similarity index 98% rename from internal/i18n/detect.go rename to internal/locales/detect.go index f35d4f1b94e..36279f1388b 100644 --- a/internal/i18n/detect.go +++ b/internal/locales/detect.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales import ( "os" diff --git a/internal/i18n/detect_cgo_darwin.go b/internal/locales/detect_cgo_darwin.go similarity index 98% rename from internal/i18n/detect_cgo_darwin.go rename to internal/locales/detect_cgo_darwin.go index c9fd4ecefb9..7c613cf6135 100644 --- a/internal/i18n/detect_cgo_darwin.go +++ b/internal/locales/detect_cgo_darwin.go @@ -15,7 +15,7 @@ //go:build darwin && cgo -package i18n +package locales /* #cgo CFLAGS: -x objective-c diff --git a/internal/i18n/detect_freebsd.go b/internal/locales/detect_freebsd.go similarity index 98% rename from internal/i18n/detect_freebsd.go rename to internal/locales/detect_freebsd.go index 759509c5abe..4d03b8f3c92 100644 --- a/internal/i18n/detect_freebsd.go +++ b/internal/locales/detect_freebsd.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales func getLocaleIdentifier() string { return getLocaleIdentifierFromEnv() diff --git a/internal/i18n/detect_linux.go b/internal/locales/detect_linux.go similarity index 98% rename from internal/i18n/detect_linux.go rename to internal/locales/detect_linux.go index 759509c5abe..4d03b8f3c92 100644 --- a/internal/i18n/detect_linux.go +++ b/internal/locales/detect_linux.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales func getLocaleIdentifier() string { return getLocaleIdentifierFromEnv() diff --git a/internal/i18n/detect_nocgo_darwin.go b/internal/locales/detect_nocgo_darwin.go similarity index 98% rename from internal/i18n/detect_nocgo_darwin.go rename to internal/locales/detect_nocgo_darwin.go index f7ae977b19f..689b5e864c5 100644 --- a/internal/i18n/detect_nocgo_darwin.go +++ b/internal/locales/detect_nocgo_darwin.go @@ -15,7 +15,7 @@ //go:build darwin && !cgo -package i18n +package locales func getLocaleIdentifier() string { return getLocaleIdentifierFromEnv() diff --git a/internal/i18n/detect_windows.go b/internal/locales/detect_windows.go similarity index 98% rename from internal/i18n/detect_windows.go rename to internal/locales/detect_windows.go index f0adbce7718..42c667447d6 100644 --- a/internal/i18n/detect_windows.go +++ b/internal/locales/detect_windows.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales import ( "strings" diff --git a/internal/locales/i18n.go b/internal/locales/i18n.go new file mode 100644 index 00000000000..8c7120a1771 --- /dev/null +++ b/internal/locales/i18n.go @@ -0,0 +1,39 @@ +// This file is part of arduino-cli. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package locales + +// Init initializes the i18n module, setting the locale according to this order of preference: +// 1. Locale specified via the function call +// 2. OS Locale +// 3. en (default) +func Init(configLocale string) { + locales := supportedLocales() + if configLocale != "" { + if locale := findMatchingLocale(configLocale, locales); locale != "" { + setLocale(locale) + return + } + } + + if osLocale := getLocaleIdentifierFromOS(); osLocale != "" { + if locale := findMatchingLocale(osLocale, locales); locale != "" { + setLocale(locale) + return + } + } + + setLocale("en") +} diff --git a/internal/i18n/locale.go b/internal/locales/locale.go similarity index 93% rename from internal/i18n/locale.go rename to internal/locales/locale.go index 35a43e42e03..646b44059f7 100644 --- a/internal/i18n/locale.go +++ b/internal/locales/locale.go @@ -13,24 +13,19 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales import ( "embed" "strings" + "github.com/arduino/arduino-cli/internal/i18n" "github.com/leonelquinteros/gotext" ) -var po *gotext.Po - //go:embed data/*.po var contents embed.FS -func init() { - po = gotext.NewPo() -} - func supportedLocales() []string { var locales []string files, err := contents.ReadDir("data") @@ -75,6 +70,7 @@ func setLocale(locale string) { if err != nil { panic("Error reading embedded i18n data: " + err.Error()) } - po = gotext.NewPo() - po.Parse(poFile) + dict := gotext.NewPo() + dict.Parse(poFile) + i18n.SetLocale(dict) } diff --git a/internal/i18n/locale_test.go b/internal/locales/locale_test.go similarity index 98% rename from internal/i18n/locale_test.go rename to internal/locales/locale_test.go index 6212258e8f8..dc0ff7ad897 100644 --- a/internal/i18n/locale_test.go +++ b/internal/locales/locale_test.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package i18n +package locales import ( "testing" diff --git a/main.go b/main.go index 84896ac19fe..0ead329254a 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ import ( "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/internal/locales" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" @@ -67,7 +68,7 @@ func main() { config := resp.GetConfiguration() // Setup i18n - i18n.Init(config.GetLocale()) + locales.Init(config.GetLocale()) // Setup command line parser with the server and settings arduinoCmd := cli.NewCommand(srv) From ea29e0290007df0b02f37d28b678827a7b625afa Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Nov 2024 11:59:47 +0100 Subject: [PATCH 6/6] updated doc --- docs/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 3d801613a01..7f615230da6 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -303,7 +303,7 @@ package main import ( "fmt" - "github.com/arduino/arduino-cli/i18n" + "github.com/arduino/arduino-cli/internal/i18n" ) func main() { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy