diff --git a/docs/README.md b/docs/README.md index 0d7a97a..b79feaf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -57,7 +57,7 @@ See [Firmware Update](https://harp-tech.org/toolkit/articles/update.html) for th `harp.toolkit` can also generate device interface and firmware code from a `device.yml` metadata file. With a `device.yml` in the current directory, the following generates the .NET device interface, targeting [Bonsai.Harp](https://harp-tech.org/api/Bonsai.Harp.html): ```text -dotnet harp.toolkit generate interface +dotnet harp.toolkit generate interface csharp ``` To generate the [Harp Python](https://harp-tech.org/python) interface instead: diff --git a/docs/articles/generate.md b/docs/articles/generate.md index 01488ce..412a3d3 100644 --- a/docs/articles/generate.md +++ b/docs/articles/generate.md @@ -30,10 +30,10 @@ A device interface can be generated from the `device.yml` metadata file, in eith A register or payload member may also declare a `converter`, for an `interfaceType` the generator cannot synthesize from the metadata alone. The implementation is then written by hand and referenced by the generated code. -The .NET interface is the default target. The language can also be named explicitly, and a metadata path given on the command line precedes it. +The target language is always named, and an optional metadata path follows it. Without a path the generator reads `device.yml` from the current directory. ```text -dotnet harp.toolkit generate interface path/to/device.yml csharp +dotnet harp.toolkit generate interface csharp path/to/device.yml ``` ### .NET interface @@ -41,7 +41,7 @@ dotnet harp.toolkit generate interface path/to/device.yml csharp An interface for reactive programming targeting [Bonsai.Harp](https://harp-tech.org/api/Bonsai.Harp.html). ```text -dotnet harp.toolkit generate interface +dotnet harp.toolkit generate interface csharp ``` Registers are additionally exposed as [operators](https://harp-tech.org/articles/operators.html), alongside an asynchronous API for use from .NET applications. @@ -55,7 +55,7 @@ The following options are available to configure the generated output. -ns, --namespace ``` -Specifies the namespace for the generated code. The default namespace is `Harp.DeviceName` where `DeviceName` is the name of the device specified in the `device.yml` file. This option applies only to the .NET interface. +Specifies the namespace for the generated code. The default namespace is `Harp.DeviceName` where `DeviceName` is the name of the device specified in the `device.yml` file. ### Python interface @@ -67,7 +67,7 @@ dotnet harp.toolkit generate interface python Custom converters are supplied in a companion `converters` module, which the generated module imports from. -The `--namespace` option does not apply to this target, since the generated module declares no namespace, and is rejected if supplied. +The `--namespace` option is not available for this target, since the generated module declares no namespace. ## Generating device firmware code diff --git a/src/Harp.Toolkit/Generate/GenerateCSharpInterfaceCommand.cs b/src/Harp.Toolkit/Generate/GenerateCSharpInterfaceCommand.cs index 3c3fe81..67d035b 100644 --- a/src/Harp.Toolkit/Generate/GenerateCSharpInterfaceCommand.cs +++ b/src/Harp.Toolkit/Generate/GenerateCSharpInterfaceCommand.cs @@ -1,21 +1,35 @@ using System.CommandLine; +using Harp.Generators; namespace Harp.Toolkit.Generate; class GenerateCSharpInterfaceCommand : Command { - public GenerateCSharpInterfaceCommand( - Argument metadataPathArgument, - Option namespaceOption, - Option outputPathOption) - : base("csharp", "Generate reactive programming API and async API. This is the default.") + public GenerateCSharpInterfaceCommand() + : base("csharp", "Generate reactive programming API and async API.") { + MetadataPathArgument metadataPathArgument = new(); + Option namespaceOption = new("-ns", "--namespace") + { + Description = "The namespace for the generated code. The default is `Harp.DeviceName`." + }; + OutputPathOption outputPathOption = new(); + + Arguments.Add(metadataPathArgument); + Options.Add(namespaceOption); + Options.Add(outputPathOption); + SetAction(parseResult => { var outputPath = parseResult.GetRequiredValue(outputPathOption); var metadataPath = parseResult.GetRequiredValue(metadataPathArgument); var ns = parseResult.GetValue(namespaceOption); - GenerateInterfaceCommand.GenerateCSharpInterface(metadataPath, ns, outputPath); + + var deviceMetadata = GeneratorHelper.ReadDeviceMetadata(metadataPath.FullName); + var generator = new InterfaceGenerator(deviceMetadata, ns ?? $"Harp.{deviceMetadata.Device}"); + var implementation = generator.GenerateImplementation(); + if (GeneratorHelper.AssertNoGeneratorErrors(generator.Errors)) + GenerateCommand.WriteFileContents(outputPath.FullName, implementation); }); } } diff --git a/src/Harp.Toolkit/Generate/GenerateInterfaceCommand.cs b/src/Harp.Toolkit/Generate/GenerateInterfaceCommand.cs index 427cbfa..60266dd 100644 --- a/src/Harp.Toolkit/Generate/GenerateInterfaceCommand.cs +++ b/src/Harp.Toolkit/Generate/GenerateInterfaceCommand.cs @@ -1,44 +1,13 @@ using System.CommandLine; -using Harp.Generators; namespace Harp.Toolkit.Generate; class GenerateInterfaceCommand : Command { public GenerateInterfaceCommand() - : base("interface", "Generate reactive programming API and async API.") + : base("interface", "Generate device interface code for a target language.") { - MetadataPathArgument metadataPathArgument = new(); - Option namespaceOption = new("-ns", "--namespace") - { - Description = "The namespace for the generated code. The default is `Harp.DeviceName`.", - Recursive = true - }; - OutputPathOption outputPathOption = new() { Recursive = true }; - - Arguments.Add(metadataPathArgument); - Options.Add(namespaceOption); - Options.Add(outputPathOption); - Subcommands.Add(new GenerateCSharpInterfaceCommand( - metadataPathArgument, namespaceOption, outputPathOption)); - Subcommands.Add(new GeneratePythonInterfaceCommand( - metadataPathArgument, namespaceOption, outputPathOption)); - - SetAction(parseResult => - { - var outputPath = parseResult.GetRequiredValue(outputPathOption); - var metadataPath = parseResult.GetRequiredValue(metadataPathArgument); - var ns = parseResult.GetValue(namespaceOption); - GenerateCSharpInterface(metadataPath, ns, outputPath); - }); - } - - internal static void GenerateCSharpInterface(FileInfo metadataPath, string? ns, DirectoryInfo outputPath) - { - var deviceMetadata = GeneratorHelper.ReadDeviceMetadata(metadataPath.FullName); - var generator = new InterfaceGenerator(deviceMetadata, ns ?? $"Harp.{deviceMetadata.Device}"); - var implementation = generator.GenerateImplementation(); - if (GeneratorHelper.AssertNoGeneratorErrors(generator.Errors)) - GenerateCommand.WriteFileContents(outputPath.FullName, implementation); + Subcommands.Add(new GenerateCSharpInterfaceCommand()); + Subcommands.Add(new GeneratePythonInterfaceCommand()); } } diff --git a/src/Harp.Toolkit/Generate/GeneratePythonInterfaceCommand.cs b/src/Harp.Toolkit/Generate/GeneratePythonInterfaceCommand.cs index 6c9924c..d556b2b 100644 --- a/src/Harp.Toolkit/Generate/GeneratePythonInterfaceCommand.cs +++ b/src/Harp.Toolkit/Generate/GeneratePythonInterfaceCommand.cs @@ -5,18 +5,14 @@ namespace Harp.Toolkit.Generate; class GeneratePythonInterfaceCommand : Command { - public GeneratePythonInterfaceCommand( - Argument metadataPathArgument, - Option namespaceOption, - Option outputPathOption) + public GeneratePythonInterfaceCommand() : base("python", "Generate the Harp Python device interface.") { - Validators.Add(commandResult => - { - var namespaceResult = commandResult.GetResult(namespaceOption); - if (namespaceResult is not null && !namespaceResult.Implicit) - commandResult.AddError("The --namespace option does not apply to the Python interface."); - }); + MetadataPathArgument metadataPathArgument = new(); + OutputPathOption outputPathOption = new(); + + Arguments.Add(metadataPathArgument); + Options.Add(outputPathOption); SetAction(parseResult => { diff --git a/src/Harp.Toolkit/Generate/MetadataPathArgument.cs b/src/Harp.Toolkit/Generate/MetadataPathArgument.cs index b8a3acd..4738850 100644 --- a/src/Harp.Toolkit/Generate/MetadataPathArgument.cs +++ b/src/Harp.Toolkit/Generate/MetadataPathArgument.cs @@ -5,7 +5,7 @@ namespace Harp.Toolkit.Generate; public class MetadataPathArgument : Argument { public MetadataPathArgument() - : base("metadataPath") + : base("metadata") { ArgumentValidation.AcceptExistingOnly(this); Description = "The path to the file describing the device registers.";