From 6df4dc4d4d31bb5da9103c3060967d7f706557c7 Mon Sep 17 00:00:00 2001 From: Patrick Grawehr Date: Mon, 17 Jun 2024 23:56:30 +0200 Subject: [PATCH] Reorganize dependencies and infrastructure for ACS (#2278) * Reorganize dependencies * Doesn't compile yet * Add some methods that are missing when compiling with the full library However, this now includes a lot of code that's very likely not really used. Need to find a way around this. * Remove unused step This feature isn't used anywhere * Separate debug and release also for the samples So that compiling them works * Now release builds again * Extend workflow options * Update runtime version * Map file contains a summary * Make sure the test assemblies are linked For some reason, the linux build fails now due to these apparently missing. * Fix build issue * Using a reference now fails on Windows * Undo this again * New attempt at solving that strange issue * Tool packs in release mode Problem: Would pack with the previous release * Use current version of full package * Ensure tool is really built * Undo general exclusion of launchsettings.json Can still commit some, if useful * Added comments about experimental state * Return to 1.0.x version Better keep the low version as long as this is experimental * Minor review findings * Use Arcade on tools and devices directories to fix ACS package not staying as preview * Fix build issue due to trying to generate symbols for tool package * Also print the runtime version * bin path has changed * Also update settings --------- Co-authored-by: Jose Perez Rodriguez --- .github/workflows/arduino.yml | 6 +- build.proj | 1 + eng/ArduinoCsCI.cmd | 20 +- eng/Versions.external.props | 5 +- .../Arduino/tests/Arduino.Tests.csproj | 1 - .../Camera/tests/TestCamera/TestCamera.csproj | 1 - src/devices/Directory.Build.props | 6 +- .../Display/Large4Digit7SegmentDisplay.cs | 2 +- .../ArduinoCsCompiler.csproj | 6 + tools/ArduinoCsCompiler/ArduinoCsCompiler.sln | 6 + .../CompilerCommandHandler.cs | 16 +- tools/ArduinoCsCompiler/CompilerSettings.cs | 8 + tools/ArduinoCsCompiler/Directory.build.props | 7 +- tools/ArduinoCsCompiler/ExecutionSet.cs | 12 +- .../ArduinoCsCompiler/Frontend/CompilerRun.cs | 33 ++- tools/ArduinoCsCompiler/Frontend/ExecRun.cs | 2 +- .../Frontend/Frontend.csproj | 39 +++- tools/ArduinoCsCompiler/Frontend/Program.cs | 4 +- .../Frontend/Properties/launchSettings.json | 24 +++ .../Hal/ArduinoNativeGpioDriver.cs | 3 +- tools/ArduinoCsCompiler/IlCodeParser.cs | 194 +++++++++--------- tools/ArduinoCsCompiler/IlInstruction.cs | 70 +++---- tools/ArduinoCsCompiler/MicroCompiler.cs | 20 +- tools/ArduinoCsCompiler/README.md | 2 + .../Runtime/MiniCultureInfo.cs | 8 +- .../Runtime/MiniInterop.Kernel32.cs | 80 ++++++++ .../ArduinoCsCompiler/Runtime/MiniMonitor.cs | 24 +++ tools/ArduinoCsCompiler/Runtime/MiniObject.cs | 2 +- .../Runtime/MiniSpanHelpers.Char.cs | 52 ++--- .../Runtime/MiniVector128.cs | 4 +- .../samples/BlinkingLed/BlinkingLed.csproj | 7 +- .../WeatherStation/WeatherStation.csproj | 9 +- .../tests/ArduinoCsCompiler.Tests.csproj | 19 +- .../tests/ArduinoTestBase.cs | 2 +- .../tests/FirmataIlExecutorTests.cs | 3 +- tools/ArduinoCsCompiler/tests/TestMethods.cs | 8 +- .../Commands/Gpio/GpioButtonEvent.cs | 6 +- .../Commands/Gpio/GpioButtonWait.cs | 3 +- .../Commands/I2c/I2cReadBytes.cs | 2 +- .../Infrastructure/CommandLineProgram.cs | 22 +- .../Infrastructure/DriverFactory.cs | 10 +- .../Infrastructure/HexStringUtilities.cs | 7 +- tools/DevicesApiTester/Program.cs | 7 +- tools/Directory.Build.props | 4 +- tools/GenerateDocFxStructure/MessageHelper.cs | 6 +- tools/GenerateDocFxStructure/Program.cs | 13 +- tools/device-listing/DeviceInfo.cs | 24 +-- .../tests/_DeviceBindingTests.cs | 2 +- 48 files changed, 539 insertions(+), 273 deletions(-) create mode 100644 tools/ArduinoCsCompiler/Frontend/Properties/launchSettings.json diff --git a/.github/workflows/arduino.yml b/.github/workflows/arduino.yml index b1f0aff801..c29bcd6835 100644 --- a/.github/workflows/arduino.yml +++ b/.github/workflows/arduino.yml @@ -17,6 +17,9 @@ jobs: build: # The type of runner that the job will run on runs-on: windows-latest + strategy: + matrix: + configuration: [Release] # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -24,5 +27,6 @@ jobs: uses: actions/checkout@master - run: | - eng/ArduinoCsCI.cmd %USERPROFILE% Debug + eng/ArduinoCsCI.cmd %USERPROFILE% ${{ matrix.configuration }} + diff --git a/build.proj b/build.proj index 63a0eba529..e80696c788 100644 --- a/build.proj +++ b/build.proj @@ -36,6 +36,7 @@ <_ProjectsToPackage Include="$(MSBuildThisFileDirectory)src\Iot.Device.Bindings\Iot.Device.Bindings.csproj" /> <_ProjectsToPackage Include="$(MSBuildThisFileDirectory)src\System.Device.Gpio\System.Device.Gpio.csproj" /> <_ProjectsToPackage Include="$(MSBuildThisFileDirectory)src\Iot.Device.Bindings.SkiaSharpAdapter\Iot.Device.Bindings.SkiaSharpAdapter.csproj" /> + <_ProjectsToPackage Include="$(MSBuildThisFileDirectory)tools\ArduinoCsCompiler\Frontend\Frontend.csproj"/> diff --git a/eng/ArduinoCsCI.cmd b/eng/ArduinoCsCI.cmd index 2262bd3f9c..f047cbcd0c 100644 --- a/eng/ArduinoCsCI.cmd +++ b/eng/ArduinoCsCI.cmd @@ -5,7 +5,7 @@ REM Second argument is either "Debug" or "Release" if %1!==! goto :usage REM Defines the revision to check out in the ExtendedConfigurableFirmata repo -set FIRMATA_SIMULATOR_CHECKOUT_REVISION=4a3b895c062c8e48685b9018d642d2c5ea84c354 +set FIRMATA_SIMULATOR_CHECKOUT_REVISION=66b1863f3a4b0999f2c1cb7133332cbd25730252 set RUN_COMPILER_TESTS=FALSE choco install -y --no-progress arduino-cli @@ -20,7 +20,7 @@ REM directly execute PS, we can ignore any test errors. powershell -ExecutionPolicy ByPass -command "%~dp0common\Build.ps1" -restore -build -ci -configuration %2 -preparemachine set ArduinoRootDir=%1\Documents\Arduino -set acspath=%~dp0\..\tools\ArduinoCsCompiler\Frontend\bin\%2\net6.0\acs.exe +set acspath=%~dp0\..\artifacts\bin\Frontend\%2\net6.0\acs.exe git clone https://github.com/firmata/ConfigurableFirmata %ArduinoRootDir%\libraries\ConfigurableFirmata git clone https://github.com/pgrawehr/ExtendedConfigurableFirmata %ArduinoRootDir%\ExtendedConfigurableFirmata @@ -35,6 +35,8 @@ if %errorlevel%==0 set RUN_COMPILER_TESTS=TRUE dir %ArduinoRootDir% +if NOT exist %acspath% goto error + %acspath% --help rem Write runtime data to ExtendedConfigurableFirmata directory, before building @@ -70,14 +72,18 @@ REM information about all tests being executed (as this test run can take 30 min echo Starting basic arduino tests dotnet test -c %2 --no-build --no-restore --filter feature=firmata -l "console;verbosity=normal" -maxcpucount:1 -echo on -if %RUN_COMPILER_TESTS%==TRUE ( -echo Starting extended Arduino compiler tests -dotnet test -c %2 --no-build --no-restore --filter feature=firmata-compiler -l "console;verbosity=normal" -maxcpucount:1 -) +if errorlevel 1 goto error +echo on +echo Run full compiler against simple example +REM The current directory is tools/ArduinoCsCompiler +%acspath% compile --run %~dp0\..\artifacts\bin\BlinkingLed\%2\net6.0\BlinkingLed.exe --network localhost --mapfile BlinkingLed-tokenmap.txt if errorlevel 1 goto error +REM copy token map to output (so we have something to compare the history of sizes, if something changes unexpectedly) +type BlinkingLed-tokenmap.txt + + popd taskkill /im ExtendedConfigurableFirmataSim.exe /f diff --git a/eng/Versions.external.props b/eng/Versions.external.props index ecbd9d60ad..dab2904596 100644 --- a/eng/Versions.external.props +++ b/eng/Versions.external.props @@ -4,6 +4,10 @@ + + 17.8.0 + + @@ -13,7 +17,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/devices/Arduino/tests/Arduino.Tests.csproj b/src/devices/Arduino/tests/Arduino.Tests.csproj index 06f63aaf01..1f644c4b7a 100644 --- a/src/devices/Arduino/tests/Arduino.Tests.csproj +++ b/src/devices/Arduino/tests/Arduino.Tests.csproj @@ -11,7 +11,6 @@ - diff --git a/src/devices/Camera/tests/TestCamera/TestCamera.csproj b/src/devices/Camera/tests/TestCamera/TestCamera.csproj index e6282d82dd..7f444ef168 100644 --- a/src/devices/Camera/tests/TestCamera/TestCamera.csproj +++ b/src/devices/Camera/tests/TestCamera/TestCamera.csproj @@ -10,7 +10,6 @@ - runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/devices/Directory.Build.props b/src/devices/Directory.Build.props index d1cdf94120..5efed1aff3 100644 --- a/src/devices/Directory.Build.props +++ b/src/devices/Directory.Build.props @@ -1,8 +1,6 @@ - - - - + + net6.0;netstandard2.0; diff --git a/src/devices/Display/Large4Digit7SegmentDisplay.cs b/src/devices/Display/Large4Digit7SegmentDisplay.cs index 7505ef9bf3..6d8326d7bd 100644 --- a/src/devices/Display/Large4Digit7SegmentDisplay.cs +++ b/src/devices/Display/Large4Digit7SegmentDisplay.cs @@ -254,7 +254,7 @@ public void Write(string value, Alignment alignment = Alignment.Left) break; case 5 when value[2] != ':': #pragma warning disable CA2208 // False positive because the message uses the parameter name "value" - throw new ArgumentException("value[2] must be a ':'", nameof(value)); + throw new ArgumentException($"{nameof(value)}[2] must be a ':'", nameof(value)); #pragma warning restore CA2208 case 5: Dots |= Dot.CenterColon; diff --git a/tools/ArduinoCsCompiler/ArduinoCsCompiler.csproj b/tools/ArduinoCsCompiler/ArduinoCsCompiler.csproj index c546512e74..042ebd9798 100644 --- a/tools/ArduinoCsCompiler/ArduinoCsCompiler.csproj +++ b/tools/ArduinoCsCompiler/ArduinoCsCompiler.csproj @@ -25,9 +25,15 @@ + + + + + + diff --git a/tools/ArduinoCsCompiler/ArduinoCsCompiler.sln b/tools/ArduinoCsCompiler/ArduinoCsCompiler.sln index 2c1c2fdcce..921a1fa8ab 100644 --- a/tools/ArduinoCsCompiler/ArduinoCsCompiler.sln +++ b/tools/ArduinoCsCompiler/ArduinoCsCompiler.sln @@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Button", "..\..\src\devices EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlinkingLed", "samples\BlinkingLed\BlinkingLed.csproj", "{072A16C3-61D5-429D-85BC-09EF4A8CEE8D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iot.Device.Bindings", "..\..\src\Iot.Device.Bindings\Iot.Device.Bindings.csproj", "{AD276307-C242-45FE-AAC4-E6713E8C9AC3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +107,10 @@ Global {072A16C3-61D5-429D-85BC-09EF4A8CEE8D}.Debug|Any CPU.Build.0 = Debug|Any CPU {072A16C3-61D5-429D-85BC-09EF4A8CEE8D}.Release|Any CPU.ActiveCfg = Release|Any CPU {072A16C3-61D5-429D-85BC-09EF4A8CEE8D}.Release|Any CPU.Build.0 = Release|Any CPU + {AD276307-C242-45FE-AAC4-E6713E8C9AC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD276307-C242-45FE-AAC4-E6713E8C9AC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD276307-C242-45FE-AAC4-E6713E8C9AC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD276307-C242-45FE-AAC4-E6713E8C9AC3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/ArduinoCsCompiler/CompilerCommandHandler.cs b/tools/ArduinoCsCompiler/CompilerCommandHandler.cs index 175e1f6466..c8e2daa2ce 100644 --- a/tools/ArduinoCsCompiler/CompilerCommandHandler.cs +++ b/tools/ArduinoCsCompiler/CompilerCommandHandler.cs @@ -30,7 +30,21 @@ public CompilerCommandHandler(MicroCompiler compiler) _logger = this.GetCurrentClassLogger(); } - public IlCapabilities? IlCapabilities => _ilCapabilities; + /// + /// Query the hardware capabilities. + /// Use the setter to set the value to null, to force an update + /// + public IlCapabilities? IlCapabilities + { + get + { + return _ilCapabilities; + } + set + { + _ilCapabilities = value; + } + } protected override void OnErrorMessage(string message, Exception? exception) { diff --git a/tools/ArduinoCsCompiler/CompilerSettings.cs b/tools/ArduinoCsCompiler/CompilerSettings.cs index db4bb12ee5..ad3d772f9c 100644 --- a/tools/ArduinoCsCompiler/CompilerSettings.cs +++ b/tools/ArduinoCsCompiler/CompilerSettings.cs @@ -94,8 +94,16 @@ public bool ForceFlashWrite set; } + /// + /// True to support language preview features + /// public bool UsePreviewFeatures { get; set; } + /// + /// The name of the process, if applicable + /// + public string? ProcessName { get; set; } + object ICloneable.Clone() { return MemberwiseClone(); diff --git a/tools/ArduinoCsCompiler/Directory.build.props b/tools/ArduinoCsCompiler/Directory.build.props index 03550b61ed..a1216ee17f 100644 --- a/tools/ArduinoCsCompiler/Directory.build.props +++ b/tools/ArduinoCsCompiler/Directory.build.props @@ -1,8 +1,7 @@ - - - + + - enable + enable \ No newline at end of file diff --git a/tools/ArduinoCsCompiler/ExecutionSet.cs b/tools/ArduinoCsCompiler/ExecutionSet.cs index 43416f0ef4..89f277dbd1 100644 --- a/tools/ArduinoCsCompiler/ExecutionSet.cs +++ b/tools/ArduinoCsCompiler/ExecutionSet.cs @@ -1504,10 +1504,20 @@ public void SuppressNamespace(string namespacePrefix, bool includingSubNamespace } } - public void WriteMapFile(string tokenMapFile) + public void WriteMapFile(string tokenMapFile, IlCapabilities ilCapabilities) { using StreamWriter w = new StreamWriter(tokenMapFile); + w.WriteLine("Program data summary"); + w.WriteLine("===================="); + w.WriteLine($"Program Name: {CompilerSettings.ProcessName ?? "N/A"}"); + w.WriteLine(ilCapabilities); + w.WriteLine($"Total number of classes: {_classes.Count}"); + w.WriteLine($"Total number of methods: {_methods.Count}"); + w.WriteLine($"Total number of static fields: {_staticFieldsUsed.Count}"); + w.WriteLine($"Total number of string constants: {_strings.Count}"); + w.WriteLine(); + w.WriteLine("Token map file, ordered by token number"); w.WriteLine("======================================="); w.WriteLine(); diff --git a/tools/ArduinoCsCompiler/Frontend/CompilerRun.cs b/tools/ArduinoCsCompiler/Frontend/CompilerRun.cs index 095987b5cb..f3c823d32f 100644 --- a/tools/ArduinoCsCompiler/Frontend/CompilerRun.cs +++ b/tools/ArduinoCsCompiler/Frontend/CompilerRun.cs @@ -57,7 +57,7 @@ public override bool RunCommand() _compiler = new MicroCompiler(board, true); - if (!_compiler.QueryBoardCapabilities(out var caps)) + if (!_compiler.QueryBoardCapabilities(true, out var caps)) { Logger.LogError("Couldn't query board capabilities. Possibly incompatible firmware"); return false; @@ -144,6 +144,7 @@ private void RunCompiler(FileInfo inputInfo) UseFlashForProgram = true, UsePreviewFeatures = CommandLineOptions.UsePreviewFeatures, AdditionalSuppressions = CommandLineOptions.Suppressions, + ProcessName = inputInfo.Name, }; Logger.LogInformation("Collecting method information and metadata..."); @@ -158,17 +159,15 @@ private void RunCompiler(FileInfo inputInfo) Logger.LogDebug($"Class {stat.Key.FullName}: {stat.Value.TotalBytes} Bytes"); } - if (!string.IsNullOrEmpty(CommandLineOptions.TokenMapFile)) - { - set.WriteMapFile(CommandLineOptions.TokenMapFile); - } - Logger.LogInformation($"Compile successful. {ErrorManager.NumErrors} Errors, {ErrorManager.NumWarnings} Warnings"); if (!CommandLineOptions.CompileOnly) { set.Load(false); + // Call this after load, so we have an updated flash usage value available. + WriteTokenMap(set); + if (CommandLineOptions.Run == false) { Logger.LogInformation("Program uploaded successfully. Execution not started. Reset the board to start execution."); @@ -290,6 +289,28 @@ private void RunCompiler(FileInfo inputInfo) } } } + else + { + WriteTokenMap(set); // If we don't load, do this anyway + } + } + + private void WriteTokenMap(ExecutionSet set) + { + if (_compiler == null) + { + return; + } + + if (!string.IsNullOrEmpty(CommandLineOptions.TokenMapFile)) + { + if (!_compiler.QueryBoardCapabilities(true, out var caps)) + { + caps = new IlCapabilities(); // Illegal, but that should be obvious to anyone + } + + set.WriteMapFile(CommandLineOptions.TokenMapFile, caps); + } } private void PrintStateFromDebuggerData((DebuggerDataKind Kind, byte[] Data) x) diff --git a/tools/ArduinoCsCompiler/Frontend/ExecRun.cs b/tools/ArduinoCsCompiler/Frontend/ExecRun.cs index 5a9e2df212..40e8b9ae74 100644 --- a/tools/ArduinoCsCompiler/Frontend/ExecRun.cs +++ b/tools/ArduinoCsCompiler/Frontend/ExecRun.cs @@ -25,7 +25,7 @@ public override bool RunCommand() var compiler = new MicroCompiler(_board, true); - if (!compiler.QueryBoardCapabilities(out var caps)) + if (!compiler.QueryBoardCapabilities(true, out var caps)) { Logger.LogError("Couldn't query board capabilities. Possibly incompatible firmware"); return false; diff --git a/tools/ArduinoCsCompiler/Frontend/Frontend.csproj b/tools/ArduinoCsCompiler/Frontend/Frontend.csproj index 2a427713b9..57b75f6d5e 100644 --- a/tools/ArduinoCsCompiler/Frontend/Frontend.csproj +++ b/tools/ArduinoCsCompiler/Frontend/Frontend.csproj @@ -9,20 +9,53 @@ acs ArduinoCsCompiler Arduino CS Compiler - A translation compiler that retargets C# applications so they can run on a microcontroller using the unnamed firmware. + A translation compiler that retargets C# applications so they can run on a microcontroller using a special firmware. See Readme for further information. The .NET Foundation community en + True + + false + latest + True + dotnet-acs + ..\..\..\artifacts\packages\$(Configuration)\Shipping + 1 + 0 + 2 + + true + + + + + + + + + - - + + + + + + + + + + + diff --git a/tools/ArduinoCsCompiler/Frontend/Program.cs b/tools/ArduinoCsCompiler/Frontend/Program.cs index 00e01c723f..f2e3e46b7d 100644 --- a/tools/ArduinoCsCompiler/Frontend/Program.cs +++ b/tools/ArduinoCsCompiler/Frontend/Program.cs @@ -15,9 +15,7 @@ using System.Threading; using CommandLine; using Iot.Device.Arduino; -using Iot.Device.Common; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using UnitsNet; namespace ArduinoCsCompiler @@ -34,6 +32,8 @@ private static int Main(string[] args) } Console.WriteLine($"ArduinoCsCompiler - Version {version.Version}"); + Console.WriteLine("This tool is experimental - expect many missing features and that the behavior will change."); + Console.WriteLine($"Active runtime version {RuntimeInformation.FrameworkDescription}"); bool runResult = false; var parser = new Parser(x => diff --git a/tools/ArduinoCsCompiler/Frontend/Properties/launchSettings.json b/tools/ArduinoCsCompiler/Frontend/Properties/launchSettings.json new file mode 100644 index 0000000000..145c5d6db7 --- /dev/null +++ b/tools/ArduinoCsCompiler/Frontend/Properties/launchSettings.json @@ -0,0 +1,24 @@ +{ + "profiles": { + "WSL": { + "commandName": "WSL2", + "distributionName": "" + }, + "Prepare": { + "commandName": "Project", + "commandLineArgs": "prepare --flashsize 4MiB --firmwaresize 1.2MiB" + }, + "BlinkingLed": { + "commandName": "Project", + "commandLineArgs": "compile --run \r\n..\\..\\..\\BlinkingLed\\$(Configuration)\\net6.0\\BlinkingLed.exe --network localhost \r\n--mapfile ..\\..\\..\\BlinkingLed\\$(Configuration)\\net6.0\\BlinkingLed-tokenmap.txt" + }, + "Help": { + "commandName": "Project", + "commandLineArgs": "--version" + }, + "WeatherStation": { + "commandName": "Project", + "commandLineArgs": "compile --run ..\\..\\..\\WeatherStation\\$(Configuration)\\net6.0\\WeatherStation.exe --network localhost --mapfile ..\\..\\..\\WeatherStation\\$(Configuration)\\net6.0\\WeatherStation-tokenmap.txt --debug" + } + } +} \ No newline at end of file diff --git a/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs b/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs index 3147ee542b..fe1c5bff24 100644 --- a/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs +++ b/tools/ArduinoCsCompiler/Hal/ArduinoNativeGpioDriver.cs @@ -103,7 +103,8 @@ protected override WaitForEventResult WaitForEvent(int pinNumber, PinEventTypes return new WaitForEventResult() { - EventTypes = PinEventTypes.None, TimedOut = true + EventTypes = PinEventTypes.None, + TimedOut = true }; } diff --git a/tools/ArduinoCsCompiler/IlCodeParser.cs b/tools/ArduinoCsCompiler/IlCodeParser.cs index 974dbaef9b..132c63f890 100644 --- a/tools/ArduinoCsCompiler/IlCodeParser.cs +++ b/tools/ArduinoCsCompiler/IlCodeParser.cs @@ -301,26 +301,26 @@ public static IlCode FindAndPatchTokens(ExecutionSet set, EquatableMethod method switch (opCode) { case OpCode.CEE_LDSTR: - { - String data = m.Module.ResolveString(token); - patchValue = set.GetOrAddString(data); - break; - } + { + String data = m.Module.ResolveString(token); + patchValue = set.GetOrAddString(data); + break; + } case OpCode.CEE_CALL: case OpCode.CEE_CALLVIRT: case OpCode.CEE_NEWOBJ: case OpCode.CEE_LDFTN: case OpCode.CEE_LDVIRTFTN: - { - // These opcodes are followed by a method token - var methodTarget = ResolveMember(m, token)!; - MethodBase mb = (MethodBase)methodTarget; // This must work, or we're trying to call a field(?) - patchValue = set.GetOrAddMethodToken(mb, method); - // Do an inverse lookup again - might have changed due to replacement - methodsUsed.Add((MethodBase)set.InverseResolveToken(patchValue)!); - break; - } + { + // These opcodes are followed by a method token + var methodTarget = ResolveMember(m, token)!; + MethodBase mb = (MethodBase)methodTarget; // This must work, or we're trying to call a field(?) + patchValue = set.GetOrAddMethodToken(mb, method); + // Do an inverse lookup again - might have changed due to replacement + methodsUsed.Add((MethodBase)set.InverseResolveToken(patchValue)!); + break; + } // These instructions take field tokens case OpCode.CEE_STSFLD: @@ -329,103 +329,103 @@ public static IlCode FindAndPatchTokens(ExecutionSet set, EquatableMethod method case OpCode.CEE_STFLD: case OpCode.CEE_LDFLDA: case OpCode.CEE_LDSFLDA: - { - var fieldTarget = ResolveMember(m, token)!; - FieldInfo mb = (FieldInfo)fieldTarget; // This must work, or the IL is invalid - var replacementClassForField = set.GetReplacement(mb.DeclaringType); - if (replacementClassForField != null) { - // The class whose member this is was replaced - replace the member, too. - // Note that this will only apply when a class that is being replaced has a public field (an example is MiniBitConverter.IsLittleEndian) - var members = replacementClassForField.FindMembers(MemberTypes.Field, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, (x, y) => x.Name == mb.Name, null); - if (members.Length != 1) + var fieldTarget = ResolveMember(m, token)!; + FieldInfo mb = (FieldInfo)fieldTarget; // This must work, or the IL is invalid + var replacementClassForField = set.GetReplacement(mb.DeclaringType); + if (replacementClassForField != null) { - // If this crashes, our replacement class misses a public field - throw new InvalidOperationException($"Class {replacementClassForField.MemberInfoSignature()} is missing field {mb.Name}"); + // The class whose member this is was replaced - replace the member, too. + // Note that this will only apply when a class that is being replaced has a public field (an example is MiniBitConverter.IsLittleEndian) + var members = replacementClassForField.FindMembers(MemberTypes.Field, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, (x, y) => x.Name == mb.Name, null); + if (members.Length != 1) + { + // If this crashes, our replacement class misses a public field + throw new InvalidOperationException($"Class {replacementClassForField.MemberInfoSignature()} is missing field {mb.Name}"); + } + + mb = (FieldInfo)members.Single(); } - mb = (FieldInfo)members.Single(); - } - - byte[]? data = null; + byte[]? data = null; - if (opCode == OpCode.CEE_LDSFLDA && mb.DeclaringType != null && mb.DeclaringType.Name.Contains(MicroCompiler.PrivateImplementationDetailsName)) - { - data = TryReadInitializerData(mb); - } + if (opCode == OpCode.CEE_LDSFLDA && mb.DeclaringType != null && mb.DeclaringType.Name.Contains(MicroCompiler.PrivateImplementationDetailsName)) + { + data = TryReadInitializerData(mb); + } - if (data != null) - { - patchValue = set.GetOrAddFieldToken(mb, data); - } - else - { - // We're currently expecting that we don't need to patch fields, because system functions don't generally allow public access to them - patchValue = set.GetOrAddFieldToken(mb); - } + if (data != null) + { + patchValue = set.GetOrAddFieldToken(mb, data); + } + else + { + // We're currently expecting that we don't need to patch fields, because system functions don't generally allow public access to them + patchValue = set.GetOrAddFieldToken(mb); + } - fieldsUsed.Add((FieldInfo)set.InverseResolveToken(patchValue)!); + fieldsUsed.Add((FieldInfo)set.InverseResolveToken(patchValue)!); - if (MicroCompiler.HasReplacementAttribute(mb.DeclaringType!, out var attribute) && attribute.ReplaceEntireType == false) - { - // If this _is_ the replacement class already, and we're not replacing the full type, add the original type, or we end up with - // both the original and the replacement types in the execution set. - if (attribute.TypeToReplace != null) + if (MicroCompiler.HasReplacementAttribute(mb.DeclaringType!, out var attribute) && attribute.ReplaceEntireType == false) { - typesUsed.Add(attribute.TypeToReplace.GetTypeInfo()); + // If this _is_ the replacement class already, and we're not replacing the full type, add the original type, or we end up with + // both the original and the replacement types in the execution set. + if (attribute.TypeToReplace != null) + { + typesUsed.Add(attribute.TypeToReplace.GetTypeInfo()); + } + } + else + { + // Add the fields' class to the list of used classes, or that one will be missing if the class consists of only fields (rare, but happens) + typesUsed.Add(mb.DeclaringType!.GetTypeInfo()); } - } - else - { - // Add the fields' class to the list of used classes, or that one will be missing if the class consists of only fields (rare, but happens) - typesUsed.Add(mb.DeclaringType!.GetTypeInfo()); - } - break; - } + break; + } case OpCode.CEE_NEWARR: - { - var typeTarget = ResolveMember(m, token)!; - TypeInfo mb = (TypeInfo)typeTarget; // This must work, or the IL is invalid - - // If we create an array instance, we also need to provide this special iterator - var getEnumeratorCall = typeof(Runtime.MiniArray).GetMethod("GetEnumerator")!.MakeGenericMethod(mb); - methodsUsed.Add(getEnumeratorCall); - patchValue = set.GetOrAddClassToken(mb); - typesUsed.Add((TypeInfo)set.InverseResolveToken(patchValue)!); - set.AddArrayImplementation(mb, getEnumeratorCall); - break; - } + { + var typeTarget = ResolveMember(m, token)!; + TypeInfo mb = (TypeInfo)typeTarget; // This must work, or the IL is invalid + + // If we create an array instance, we also need to provide this special iterator + var getEnumeratorCall = typeof(Runtime.MiniArray).GetMethod("GetEnumerator")!.MakeGenericMethod(mb); + methodsUsed.Add(getEnumeratorCall); + patchValue = set.GetOrAddClassToken(mb); + typesUsed.Add((TypeInfo)set.InverseResolveToken(patchValue)!); + set.AddArrayImplementation(mb, getEnumeratorCall); + break; + } // LDTOKEN takes typically types, but can also take virtual stuff (whatever that means) case OpCode.CEE_LDTOKEN: case OpCode.CEE_CASTCLASS: - { - var resolved = ResolveMember(m, token); - if (resolved is TypeInfo ti) { - bool isEnum = ti.IsEnum; - patchValue = set.GetOrAddClassToken(ti); - typesUsed.Add(ti); - } - else if (resolved is FieldInfo mi) - { - byte[]? array = TryReadInitializerData(mi); - if (array == null) + var resolved = ResolveMember(m, token); + if (resolved is TypeInfo ti) { - throw new InvalidOperationException($"Field {mi.Name} is expected to have a constant initializer, but it was not found"); + bool isEnum = ti.IsEnum; + patchValue = set.GetOrAddClassToken(ti); + typesUsed.Add(ti); } + else if (resolved is FieldInfo mi) + { + byte[]? array = TryReadInitializerData(mi); + if (array == null) + { + throw new InvalidOperationException($"Field {mi.Name} is expected to have a constant initializer, but it was not found"); + } - patchValue = set.GetOrAddFieldToken(mi, array); - } - else - { - throw new InvalidOperationException($"Unknown token type {resolved}"); - } + patchValue = set.GetOrAddFieldToken(mi, array); + } + else + { + throw new InvalidOperationException($"Unknown token type {resolved}"); + } - break; - } + break; + } case OpCode.CEE_STELEM: case OpCode.CEE_LDELEM: @@ -439,14 +439,14 @@ public static IlCode FindAndPatchTokens(ExecutionSet set, EquatableMethod method case OpCode.CEE_INITOBJ: case OpCode.CEE_ISINST: case OpCode.CEE_SIZEOF: - { - // These take a type as argument - var typeTarget = ResolveMember(m, token)!; - TypeInfo mb = (TypeInfo)typeTarget; // This must work, or the IL is invalid - patchValue = set.GetOrAddClassToken(mb); - typesUsed.Add((TypeInfo)set.InverseResolveToken(patchValue)!); - break; - } + { + // These take a type as argument + var typeTarget = ResolveMember(m, token)!; + TypeInfo mb = (TypeInfo)typeTarget; // This must work, or the IL is invalid + patchValue = set.GetOrAddClassToken(mb); + typesUsed.Add((TypeInfo)set.InverseResolveToken(patchValue)!); + break; + } default: throw new InvalidOperationException($"Opcode {opCode} has a token argument, but is unhandled in {method.MethodSignature()}."); diff --git a/tools/ArduinoCsCompiler/IlInstruction.cs b/tools/ArduinoCsCompiler/IlInstruction.cs index e906a82575..45903260d2 100644 --- a/tools/ArduinoCsCompiler/IlInstruction.cs +++ b/tools/ArduinoCsCompiler/IlInstruction.cs @@ -131,57 +131,57 @@ private int DecodeIntegerArgument() { case OpCodeType.InlineI: case OpCodeType.ShortInlineI: - { - int arg = DecodeIntegerArgument(); - return $"{arg} (0x{arg:X})"; - } + { + int arg = DecodeIntegerArgument(); + return $"{arg} (0x{arg:X})"; + } case OpCodeType.ShortInlineVar: case OpCodeType.InlineVar: - { - int arg = DecodeIntegerArgument(); - return $"{arg}"; - } + { + int arg = DecodeIntegerArgument(); + return $"{arg}"; + } case OpCodeType.ShortInlineBrTarget: case OpCodeType.InlineBrTarget: - { - int offset = DecodeIntegerArgument(); - return $"Offset {offset}, --> 0x{(offset + Pc + Size):X}"; // Offset is from beginning of next instruction - } - - case OpCodeType.InlineField: - { - int token = DecodeIntegerArgument(); - var field = set.InverseResolveToken(token); - if (field != null) { - return $"{token} - {field.Name}"; + int offset = DecodeIntegerArgument(); + return $"Offset {offset}, --> 0x{(offset + Pc + Size):X}"; // Offset is from beginning of next instruction } - else + + case OpCodeType.InlineField: { - return $"{token} - (unknown field)"; + int token = DecodeIntegerArgument(); + var field = set.InverseResolveToken(token); + if (field != null) + { + return $"{token} - {field.Name}"; + } + else + { + return $"{token} - (unknown field)"; + } } - } case OpCodeType.InlineMethod: - { - int token = DecodeIntegerArgument(); - var method = set.InverseResolveToken(token); - if (method == null) { - return $"{token} - Unable to resolve"; + int token = DecodeIntegerArgument(); + var method = set.InverseResolveToken(token); + if (method == null) + { + return $"{token} - Unable to resolve"; + } + + return $"{token} - {method.MemberInfoSignature(false)}"; } - return $"{token} - {method.MemberInfoSignature(false)}"; - } - case OpCodeType.InlineString: - { - int token = DecodeIntegerArgument(); - string value = set.GetString(token); - return $"{token} \"{value}\""; - } + { + int token = DecodeIntegerArgument(); + string value = set.GetString(token); + return $"{token} \"{value}\""; + } } return null; diff --git a/tools/ArduinoCsCompiler/MicroCompiler.cs b/tools/ArduinoCsCompiler/MicroCompiler.cs index ee2a550353..910c33a41c 100644 --- a/tools/ArduinoCsCompiler/MicroCompiler.cs +++ b/tools/ArduinoCsCompiler/MicroCompiler.cs @@ -994,7 +994,7 @@ private void CompleteClasses(ExecutionSet set) { // Or if the method is implementing an interface List methodsBeingImplemented = new List(); - MicroCompiler.CollectBaseImplementations(set, m, methodsBeingImplemented); + CollectBaseImplementations(set, m, methodsBeingImplemented); if (methodsBeingImplemented.Any()) { PrepareMethod(set, m, null); @@ -1237,7 +1237,7 @@ private static int CompareInternal(ClassDeclaration x, ClassDeclaration y) return 1; } - compareByName: + compareByName: return string.Compare(x.Name, y.Name, StringComparison.Ordinal); } } @@ -1474,7 +1474,7 @@ internal void SendMethods(IProgress progress, ExecutionSet set, Executio /// The method instance /// Returns the list of methods (from interfaces or base classes) that this method implements /// True if the method shall be part of the class declaration - private static bool MemberLinkRequired(ExecutionSet set, MemberInfo method, out List methodsBeingImplemented) + private bool MemberLinkRequired(ExecutionSet set, MemberInfo method, out List methodsBeingImplemented) { methodsBeingImplemented = new List(); @@ -1497,13 +1497,13 @@ private static bool MemberLinkRequired(ExecutionSet set, MemberInfo method, out CollectBaseImplementations(set, new EquatableMethod(m), methodsBeingImplemented); // We need the implementation if at least one base implementation is being called and is used - return methodsBeingImplemented.Count > 0 && methodsBeingImplemented.Any(x => set.HasMethod(x, m, out _, out _)); + return methodsBeingImplemented.Any(x => set.HasMethod(x, m, out _, out _)); } return false; } - internal static void CollectBaseImplementations(ExecutionSet set, EquatableMethod method, List methodsBeingImplemented) + internal void CollectBaseImplementations(ExecutionSet set, EquatableMethod method, List methodsBeingImplemented) { Type? cls = method.DeclaringType?.BaseType; while (cls != null) @@ -1539,6 +1539,7 @@ internal static void CollectBaseImplementations(ExecutionSet set, EquatableMetho EquatableMethod equatableCandidate = new EquatableMethod(candidate); if (EquatableMethod.IsOverriddenImplementation(equatableCandidate, method, true)) { + _logger.LogDebug($"Need to include {method.MethodSignature()} in execution set because it implements {interf}"); methodsBeingImplemented.Add(equatableCandidate); } } @@ -3053,7 +3054,9 @@ public void Dispose() if (_commandHandler != null) { _commandHandler.Dispose(); +#if DEBUG // TODO: Re-enable (method is not yet available in the released package) _board?.RemoveCommandHandler(_commandHandler); +#endif } _commandHandler = null!; @@ -3090,10 +3093,15 @@ internal void WriteFlashHeader(ExecutionSet.SnapShot snapShot, int startupToken, _commandHandler.WriteFlashHeader(DataVersion, snapShot.GetHashCode(), startupToken, flags); } - public bool QueryBoardCapabilities( + public bool QueryBoardCapabilities(bool force, [NotNullWhen(true)] out IlCapabilities ilCapabilities) { + if (force) + { + _commandHandler.IlCapabilities = null; + } + ilCapabilities = null!; _commandHandler.QueryCapabilities(); diff --git a/tools/ArduinoCsCompiler/README.md b/tools/ArduinoCsCompiler/README.md index d3d7f1c6c6..b8676c8494 100644 --- a/tools/ArduinoCsCompiler/README.md +++ b/tools/ArduinoCsCompiler/README.md @@ -10,6 +10,8 @@ At the moment, the following microcontrollers are supported: Additionally, the microcontroler can be simulated on the PC for additional testing and debugging possibilities. +This tool is in an experimental phase. Behavior and features may still significantly change. + ## Overview To execute .NET code, the microcontroler needs to be programmed with a firmware that is able to understand the IL (Intermediate language) code generated by the C# (or VB) compiler. We call this part the "Firmware". It is loaded using tools supplied from the microcontroler vendors. For compiling and uploading, the instructions use the Arduino command line interface. The Arduino IDE can also be used after some initial steps. diff --git a/tools/ArduinoCsCompiler/Runtime/MiniCultureInfo.cs b/tools/ArduinoCsCompiler/Runtime/MiniCultureInfo.cs index 064d914d6d..8cc5bd7545 100644 --- a/tools/ArduinoCsCompiler/Runtime/MiniCultureInfo.cs +++ b/tools/ArduinoCsCompiler/Runtime/MiniCultureInfo.cs @@ -353,10 +353,10 @@ public static MiniCultureInfo? DefaultThreadCurrentCulture { get => InvariantCulture; set - // If you add pre-conditions to this method, check to see if you also need to - // add them to Thread.CurrentCulture.set. - { - } + // If you add pre-conditions to this method, check to see if you also need to + // add them to Thread.CurrentCulture.set. + { + } } public static MiniCultureInfo? DefaultThreadCurrentUICulture diff --git a/tools/ArduinoCsCompiler/Runtime/MiniInterop.Kernel32.cs b/tools/ArduinoCsCompiler/Runtime/MiniInterop.Kernel32.cs index a55c5adb92..bf06009363 100644 --- a/tools/ArduinoCsCompiler/Runtime/MiniInterop.Kernel32.cs +++ b/tools/ArduinoCsCompiler/Runtime/MiniInterop.Kernel32.cs @@ -431,6 +431,18 @@ internal static unsafe Int32 ReadFileInternal(IntPtr fileHandle, Byte* bytes, Sy return 0; } + internal static unsafe Int32 ReadFile(IntPtr handle, Byte* bytes, System.Int32 numBytesToRead, ref System.Int32 numBytesRead, System.IntPtr mustBeZero) + { + numBytesRead = ReadFileInternal(handle, bytes, numBytesToRead); + if (numBytesRead < 0) + { + numBytesRead = 0; + return 0; + } + + return 1; + } + internal static unsafe Int32 ReadFile(System.Runtime.InteropServices.SafeHandle handle, Byte* bytes, System.Int32 numBytesToRead, ref System.Int32 numBytesRead, System.IntPtr mustBeZero) { numBytesRead = ReadFileInternal(handle.DangerousGetHandle(), bytes, numBytesToRead); @@ -443,6 +455,12 @@ internal static unsafe Int32 ReadFile(System.Runtime.InteropServices.SafeHandle return 1; } + internal static unsafe bool ReadConsole(System.IntPtr hConsoleInput, Byte* lpBuffer, Int32 nNumberOfCharsToRead, ref Int32 lpNumberOfCharsRead, System.IntPtr pInputControl) + { + lpNumberOfCharsRead = 0; + return true; + } + [ArduinoImplementation("Interop_Kernel32CancelIoEx", 0x20B)] internal static unsafe Boolean CancelIoEx(System.Runtime.InteropServices.SafeHandle handle, System.Threading.NativeOverlapped* lpOverlapped) { @@ -608,6 +626,68 @@ public static UInt32 GetTimeZoneInformation(out TIME_ZONE_INFORMATION lpTimeZone lpTimeZoneInformation = default; return 1; } + + private static unsafe int StrLen(byte* cstr) + { + int ret = 0; + while (*cstr != 0) + { + ret++; + cstr++; + } + + return ret; + } + + [ArduinoImplementation] + internal static unsafe Int32 MultiByteToWideChar(System.UInt32 CodePage, System.UInt32 dwFlags, Byte* lpMultiByteStr, Int32 cbMultiByte, + Char* lpWideCharStr, System.Int32 cchWideChar) + { + int bytesToConvert = cbMultiByte; + if (cbMultiByte == -1) + { + bytesToConvert = StrLen(lpMultiByteStr); + } + + if (cchWideChar == 0) + { + return bytesToConvert + 1; + } + + int bytesRemaining = bytesToConvert; + // May be -1 to run until *lpMultiByteStr is 0 + int idx = 0; + while (bytesRemaining > 0) + { + byte input = lpMultiByteStr[idx]; + lpWideCharStr[idx] = (Char)input; + bytesRemaining--; + idx++; + } + + return bytesToConvert; + } + + [ArduinoImplementation] + internal static int GetLeadByteRanges(int codePage, byte[] leadByteRanges) + { + /* + int count = 0; + CPINFOEXW cpInfo; + if (GetCPInfoExW((uint)codePage, 0, &cpInfo) != false) + { + // we don't care about the last 2 bytes as those are nulls + for (int i = 0; i < 10 && leadByteRanges[i] != 0; i += 2) + { + leadByteRanges[i] = cpInfo.LeadByte[i]; + leadByteRanges[i + 1] = cpInfo.LeadByte[i + 1]; + count++; + } + } + return count; + */ + return 0; + } } #pragma warning disable CS0169 diff --git a/tools/ArduinoCsCompiler/Runtime/MiniMonitor.cs b/tools/ArduinoCsCompiler/Runtime/MiniMonitor.cs index 3b65f4d923..0ce857ebc4 100644 --- a/tools/ArduinoCsCompiler/Runtime/MiniMonitor.cs +++ b/tools/ArduinoCsCompiler/Runtime/MiniMonitor.cs @@ -50,12 +50,24 @@ public static bool Wait(Object obj, Int32 millisecondsTimeout) throw new NotImplementedException(); } + [ArduinoImplementation] + public static bool Wait(Object obj, TimeSpan timeOut) + { + return Wait(obj, (int)timeOut.TotalMilliseconds); + } + [ArduinoImplementation("MonitorTryEnter")] public static bool TryEnter(object obj, Int32 millisecondsTimeout) { throw new NotImplementedException(); } + [ArduinoImplementation] + public static bool TryEnter(object obj, TimeSpan timeOut) + { + return TryEnter(obj, (int)timeOut.TotalMilliseconds); + } + [ArduinoImplementation] public static bool TryEnter(object obj, Int32 millisecondsTimeout, ref bool lockTaken) { @@ -67,5 +79,17 @@ public static bool TryEnter(object obj, Int32 millisecondsTimeout, ref bool lock return false; } + + [ArduinoImplementation] + public static bool TryEnter(object obj, TimeSpan timeout, ref bool lockTaken) + { + if (TryEnter(obj, (int)timeout.TotalMilliseconds)) + { + lockTaken = true; + return true; + } + + return false; + } } } diff --git a/tools/ArduinoCsCompiler/Runtime/MiniObject.cs b/tools/ArduinoCsCompiler/Runtime/MiniObject.cs index c377b18fae..80e404b0fe 100644 --- a/tools/ArduinoCsCompiler/Runtime/MiniObject.cs +++ b/tools/ArduinoCsCompiler/Runtime/MiniObject.cs @@ -34,7 +34,7 @@ public override int GetHashCode() [ArduinoImplementation("ObjectToString", 4)] public override string ToString() { - throw new NotImplementedException(); + throw new NotImplementedException(); } [ArduinoImplementation("ObjectGetType", 5)] diff --git a/tools/ArduinoCsCompiler/Runtime/MiniSpanHelpers.Char.cs b/tools/ArduinoCsCompiler/Runtime/MiniSpanHelpers.Char.cs index 5e745fcc39..1388fde607 100644 --- a/tools/ArduinoCsCompiler/Runtime/MiniSpanHelpers.Char.cs +++ b/tools/ArduinoCsCompiler/Runtime/MiniSpanHelpers.Char.cs @@ -84,7 +84,7 @@ public static unsafe int SequenceCompareTo(ref char first, int firstLength, ref i += 1; } - Equal: + Equal: return lengthDelta; } @@ -123,7 +123,7 @@ public static unsafe bool Contains(ref char searchSpace, char value, int length) return false; - Found: + Found: return true; } } @@ -177,13 +177,13 @@ public static unsafe int IndexOf(ref char searchSpace, char value, int length) } return -1; - Found3: + Found3: return (int)(offset + 3); - Found2: + Found2: return (int)(offset + 2); - Found1: + Found1: return (int)(offset + 1); - Found: + Found: return (int)(offset); } @@ -225,13 +225,13 @@ public static unsafe int IndexOfAny(ref char searchStart, char value0, char valu } return -1; - Found3: + Found3: return (int)(offset + 3); - Found2: + Found2: return (int)(offset + 2); - Found1: + Found1: return (int)(offset + 1); - Found: + Found: return (int)offset; } @@ -273,13 +273,13 @@ public static unsafe int IndexOfAny(ref char searchStart, char value0, char valu } return -1; - Found3: + Found3: return (int)(offset + 3); - Found2: + Found2: return (int)(offset + 2); - Found1: + Found1: return (int)(offset + 1); - Found: + Found: return (int)offset; } @@ -322,13 +322,13 @@ public static unsafe int IndexOfAny(ref char searchStart, char value0, char valu } return -1; - Found3: + Found3: return (int)(offset + 3); - Found2: + Found2: return (int)(offset + 2); - Found1: + Found1: return (int)(offset + 1); - Found: + Found: return (int)offset; } @@ -371,13 +371,13 @@ public static unsafe int IndexOfAny(ref char searchStart, char value0, char valu } return -1; - Found3: + Found3: return (int)(offset + 3); - Found2: + Found2: return (int)(offset + 2); - Found1: + Found1: return (int)(offset + 1); - Found: + Found: return (int)offset; } @@ -413,13 +413,13 @@ public static unsafe int LastIndexOf(ref char searchSpace, char value, int lengt } return -1; - Found: + Found: return (int)(pCh - pEndCh); - Found1: + Found1: return (int)(pCh - pEndCh) + 1; - Found2: + Found2: return (int)(pCh - pEndCh) + 2; - Found3: + Found3: return (int)(pCh - pEndCh) + 3; } } diff --git a/tools/ArduinoCsCompiler/Runtime/MiniVector128.cs b/tools/ArduinoCsCompiler/Runtime/MiniVector128.cs index 2e9b7d1e6f..e1261879cc 100644 --- a/tools/ArduinoCsCompiler/Runtime/MiniVector128.cs +++ b/tools/ArduinoCsCompiler/Runtime/MiniVector128.cs @@ -3,14 +3,14 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; using System.Text; using System.Threading.Tasks; diff --git a/tools/ArduinoCsCompiler/samples/BlinkingLed/BlinkingLed.csproj b/tools/ArduinoCsCompiler/samples/BlinkingLed/BlinkingLed.csproj index 11aa5e6dda..a844287a46 100644 --- a/tools/ArduinoCsCompiler/samples/BlinkingLed/BlinkingLed.csproj +++ b/tools/ArduinoCsCompiler/samples/BlinkingLed/BlinkingLed.csproj @@ -12,12 +12,15 @@ - + - + + + + diff --git a/tools/ArduinoCsCompiler/samples/WeatherStation/WeatherStation.csproj b/tools/ArduinoCsCompiler/samples/WeatherStation/WeatherStation.csproj index 22d8d9df52..7f2743ba10 100644 --- a/tools/ArduinoCsCompiler/samples/WeatherStation/WeatherStation.csproj +++ b/tools/ArduinoCsCompiler/samples/WeatherStation/WeatherStation.csproj @@ -12,7 +12,7 @@ - + @@ -20,8 +20,9 @@ - - - + + + + diff --git a/tools/ArduinoCsCompiler/tests/ArduinoCsCompiler.Tests.csproj b/tools/ArduinoCsCompiler/tests/ArduinoCsCompiler.Tests.csproj index 8420bc70cc..112a9a12cf 100644 --- a/tools/ArduinoCsCompiler/tests/ArduinoCsCompiler.Tests.csproj +++ b/tools/ArduinoCsCompiler/tests/ArduinoCsCompiler.Tests.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -8,21 +8,28 @@ requires!=hardware ArduinoCsCompiler.Tests - true + true - + - + + + + + + + + + + - - diff --git a/tools/ArduinoCsCompiler/tests/ArduinoTestBase.cs b/tools/ArduinoCsCompiler/tests/ArduinoTestBase.cs index 783c0b39dd..71c0f97de6 100644 --- a/tools/ArduinoCsCompiler/tests/ArduinoTestBase.cs +++ b/tools/ArduinoCsCompiler/tests/ArduinoTestBase.cs @@ -32,7 +32,7 @@ public ArduinoTestBase(FirmataTestFixture fixture) _compiler = new MicroCompiler(_fixture.Board!, true); - if (!_compiler.QueryBoardCapabilities(out IlCapabilities data)) + if (!_compiler.QueryBoardCapabilities(false, out IlCapabilities data)) { throw new NotSupportedException("No valid IL execution firmware found on board"); } diff --git a/tools/ArduinoCsCompiler/tests/FirmataIlExecutorTests.cs b/tools/ArduinoCsCompiler/tests/FirmataIlExecutorTests.cs index b7e1dbe69e..ed05b277e2 100644 --- a/tools/ArduinoCsCompiler/tests/FirmataIlExecutorTests.cs +++ b/tools/ArduinoCsCompiler/tests/FirmataIlExecutorTests.cs @@ -37,7 +37,8 @@ private void LoadCodeMethod(Type testClass, string methodName, T1 a, { settings = new CompilerSettings() { - CreateKernelForFlashing = false, UseFlashForKernel = false + CreateKernelForFlashing = false, + UseFlashForKernel = false }; settings.AdditionalSuppressions.Add("System.Number"); settings.AdditionalSuppressions.Add("System.SR"); diff --git a/tools/ArduinoCsCompiler/tests/TestMethods.cs b/tools/ArduinoCsCompiler/tests/TestMethods.cs index d1e4ad781d..25dfd70ce3 100644 --- a/tools/ArduinoCsCompiler/tests/TestMethods.cs +++ b/tools/ArduinoCsCompiler/tests/TestMethods.cs @@ -687,7 +687,9 @@ public static int SpanImplementationBehavior(int a, int b) { Span span = stackalloc byte[] { - (byte)a, (byte)b, (byte)(a + 1), + (byte)a, + (byte)b, + (byte)(a + 1), }; MiniAssert.That(span.Length == 3); @@ -1099,8 +1101,8 @@ public static int NotMuchToDo(int a1, int a2, out int test) int result = 0; try { - result = a1 + a2; - test = 2; + result = a1 + a2; + test = 2; } catch (OverflowException) { diff --git a/tools/DevicesApiTester/Commands/Gpio/GpioButtonEvent.cs b/tools/DevicesApiTester/Commands/Gpio/GpioButtonEvent.cs index 6d4596edf3..61971541d3 100644 --- a/tools/DevicesApiTester/Commands/Gpio/GpioButtonEvent.cs +++ b/tools/DevicesApiTester/Commands/Gpio/GpioButtonEvent.cs @@ -70,13 +70,13 @@ public Task ExecuteAsync() // Set the event handler for changes to the pin value. PinEventTypes bothPinEventTypes = PinEventTypes.Falling | PinEventTypes.Rising; - controller.RegisterCallbackForPinValueChangedEvent(ButtonPin, bothPinEventTypes, valueChangeHandler); + controller.RegisterCallbackForPinValueChangedEvent(ButtonPin, bothPinEventTypes, ValueChangeHandler); // Wait for the cancel (Ctrl+C) console event. cancelEvent.WaitOne(); // Unregister the event handler for changes to the pin value - controller.UnregisterCallbackForPinValueChangedEvent(ButtonPin, valueChangeHandler); + controller.UnregisterCallbackForPinValueChangedEvent(ButtonPin, ValueChangeHandler); controller.ClosePin(ButtonPin); if (LedPin != null) @@ -90,7 +90,7 @@ public Task ExecuteAsync() return Task.FromResult(0); // Declare a local function to handle the pin value changed events. - void valueChangeHandler(object sender, PinValueChangedEventArgs pinValueChangedEventArgs) + void ValueChangeHandler(object sender, PinValueChangedEventArgs pinValueChangedEventArgs) { if (LedPin != null) { diff --git a/tools/DevicesApiTester/Commands/Gpio/GpioButtonWait.cs b/tools/DevicesApiTester/Commands/Gpio/GpioButtonWait.cs index eb6c50183d..621358c71a 100644 --- a/tools/DevicesApiTester/Commands/Gpio/GpioButtonWait.cs +++ b/tools/DevicesApiTester/Commands/Gpio/GpioButtonWait.cs @@ -79,7 +79,8 @@ public async Task ExecuteAsync() controller.Write(LedPin.Value, ledValue); } } - } while (!waitResult.TimedOut); + } + while (!waitResult.TimedOut); controller.ClosePin(ButtonPin); if (LedPin != null) diff --git a/tools/DevicesApiTester/Commands/I2c/I2cReadBytes.cs b/tools/DevicesApiTester/Commands/I2c/I2cReadBytes.cs index 0aa1b0b9a6..a74cddf820 100644 --- a/tools/DevicesApiTester/Commands/I2c/I2cReadBytes.cs +++ b/tools/DevicesApiTester/Commands/I2c/I2cReadBytes.cs @@ -30,7 +30,7 @@ public int Execute() } else { - Console.WriteLine($"BusId={BusId}, DeviceAddress={DeviceAddress} (0x{DeviceAddress:X2}), FirstRegister={ FirstRegister} (0x{FirstRegister:X2}), ByteCount={ByteCount}"); + Console.WriteLine($"BusId={BusId}, DeviceAddress={DeviceAddress} (0x{DeviceAddress:X2}), FirstRegister={FirstRegister} (0x{FirstRegister:X2}), ByteCount={ByteCount}"); } var connectionSettings = new I2cConnectionSettings(BusId, DeviceAddress); diff --git a/tools/DevicesApiTester/Infrastructure/CommandLineProgram.cs b/tools/DevicesApiTester/Infrastructure/CommandLineProgram.cs index 13f9144469..f1966b3269 100644 --- a/tools/DevicesApiTester/Infrastructure/CommandLineProgram.cs +++ b/tools/DevicesApiTester/Infrastructure/CommandLineProgram.cs @@ -11,8 +11,17 @@ namespace DeviceApiTester.Infrastructure { - abstract class CommandLineProgram + internal abstract class CommandLineProgram { + protected static Type[] GetAllCommandsInAssembly() + { + return Assembly.GetExecutingAssembly().GetTypes() + .Where(t => typeof(ICommandVerb).IsAssignableFrom(t) || typeof(ICommandVerbAsync).IsAssignableFrom(t)) + .Where(t => t.GetCustomAttributes().Any()) + .OrderBy(t => t.GetCustomAttribute()?.Name) + .ToArray(); + } + /// /// Parses the command line and executes the specified command. /// @@ -42,15 +51,6 @@ protected virtual int Run(string[] args) /// An array of types for the commands supported by the program. protected virtual Type[] GetCommandTypes() => GetAllCommandsInAssembly(); - protected static Type[] GetAllCommandsInAssembly() - { - return Assembly.GetExecutingAssembly().GetTypes() - .Where(t => typeof(ICommandVerb).IsAssignableFrom(t) || typeof(ICommandVerbAsync).IsAssignableFrom(t)) - .Where(t => t.GetCustomAttributes().Any()) - .OrderBy(t => t.GetCustomAttribute()?.Name) - .ToArray(); - } - protected virtual async Task ExecuteCommandAsync(ICommandVerbAsync verbCommand) { WaitForDebuggerIfRequested(verbCommand); @@ -86,7 +86,9 @@ protected virtual void WaitForDebuggerIfRequested(object verbCommand) { Console.WriteLine("Waiting for a Debugger to be attached . . . "); while (!Debugger.IsAttached) + { Thread.Sleep(400); + } } } diff --git a/tools/DevicesApiTester/Infrastructure/DriverFactory.cs b/tools/DevicesApiTester/Infrastructure/DriverFactory.cs index 09547fea9c..679bd47182 100644 --- a/tools/DevicesApiTester/Infrastructure/DriverFactory.cs +++ b/tools/DevicesApiTester/Infrastructure/DriverFactory.cs @@ -8,25 +8,25 @@ namespace DeviceApiTester.Infrastructure { public static class DriverFactory { - public static InstanceType? CreateFromEnum(EnumType driver, params object[] parameters) - where InstanceType : class + public static TInstanceType? CreateFromEnum(TEnumType driver, params object[] parameters) + where TInstanceType : class { try { string name = driver?.ToString() ?? "Foo"; - ImplementationTypeAttribute creatorAttribute = typeof(EnumType) + ImplementationTypeAttribute creatorAttribute = typeof(TEnumType) .GetMember(name)[0] .GetCustomAttributes(typeof(ImplementationTypeAttribute), false) .OfType() .FirstOrDefault() - ?? throw new InvalidOperationException($"The {typeof(EnumType).Name}.{driver} enum value is not attributed with an {nameof(ImplementationTypeAttribute)}."); + ?? throw new InvalidOperationException($"The {typeof(TEnumType).Name}.{driver} enum value is not attributed with an {nameof(ImplementationTypeAttribute)}."); if (creatorAttribute.ImplementationType is null) { return null; } - return Activator.CreateInstance(creatorAttribute.ImplementationType, parameters) as InstanceType; + return Activator.CreateInstance(creatorAttribute.ImplementationType, parameters) as TInstanceType; } catch (Exception ex) { diff --git a/tools/DevicesApiTester/Infrastructure/HexStringUtilities.cs b/tools/DevicesApiTester/Infrastructure/HexStringUtilities.cs index 799d36a1b1..66b8d297f2 100644 --- a/tools/DevicesApiTester/Infrastructure/HexStringUtilities.cs +++ b/tools/DevicesApiTester/Infrastructure/HexStringUtilities.cs @@ -17,6 +17,7 @@ public static string FormatByteData(byte[] data, int perGroup = 4, int perLine = { throw new ArgumentOutOfRangeException(nameof(perGroup)); } + if (perLine < 1) { throw new ArgumentOutOfRangeException(nameof(perLine)); @@ -28,9 +29,9 @@ public static string FormatByteData(byte[] data, int perGroup = 4, int perLine = const string groupDelimeter = " "; var sb = new StringBuilder( - dataLength * 2 // 2 characters per byte - + dataLength / perGroup * groupDelimeter.Length // group delimiter string - + lineCount * Environment.NewLine.Length // 1 new-line string between each line + dataLength * 2 // 2 characters per byte + + dataLength / perGroup * groupDelimeter.Length // group delimiter string + + lineCount * Environment.NewLine.Length // 1 new-line string between each line + perLine); // some extra calculation padding int groupsPerLine = perLine / perGroup; diff --git a/tools/DevicesApiTester/Program.cs b/tools/DevicesApiTester/Program.cs index e3636a21b4..e024f7c56f 100644 --- a/tools/DevicesApiTester/Program.cs +++ b/tools/DevicesApiTester/Program.cs @@ -1,8 +1,11 @@ -using DeviceApiTester.Infrastructure; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using DeviceApiTester.Infrastructure; namespace DeviceApiTester { - class Program : CommandLineProgram + internal class Program : CommandLineProgram { private static int Main(string[] args) { diff --git a/tools/Directory.Build.props b/tools/Directory.Build.props index fc9f07c6ce..532f2541b4 100644 --- a/tools/Directory.Build.props +++ b/tools/Directory.Build.props @@ -1,7 +1,7 @@ - - + + $(NoWarn);CS8321 false diff --git a/tools/GenerateDocFxStructure/MessageHelper.cs b/tools/GenerateDocFxStructure/MessageHelper.cs index 28a165258c..334311fe59 100644 --- a/tools/GenerateDocFxStructure/MessageHelper.cs +++ b/tools/GenerateDocFxStructure/MessageHelper.cs @@ -10,7 +10,7 @@ namespace GenerateDocFxStructure.Helpers /// public class MessageHelper { - private readonly CommandlineOptions options; + private readonly CommandlineOptions _options; /// /// Initializes a new instance of the class. @@ -18,7 +18,7 @@ public class MessageHelper /// Command line options. public MessageHelper(CommandlineOptions options) { - this.options = options; + _options = options; } /// @@ -36,7 +36,7 @@ public void Output(string message) /// Message to show in verbose mode. public void Verbose(string message) { - if (this.options == null || this.options.Verbose) + if (_options == null || _options.Verbose) { Console.WriteLine(message); } diff --git a/tools/GenerateDocFxStructure/Program.cs b/tools/GenerateDocFxStructure/Program.cs index 5bd5740102..c8645c9118 100644 --- a/tools/GenerateDocFxStructure/Program.cs +++ b/tools/GenerateDocFxStructure/Program.cs @@ -12,7 +12,7 @@ namespace GenerateDocFxStructure { - class Program + internal class Program { private static readonly string[] _mediaExtentions = new string[] { ".png", ".jpg", ".mpg", ".mpeg", ".mov", ".mp4", ".bmp", ".gif", ".svg" }; private static CommandlineOptions? _options; @@ -89,7 +89,6 @@ private static void RunLogic(CommandlineOptions o) allFiles.Add(file); } - // we start at the root to generate the items DirectoryInfo rootDir = new DirectoryInfo(_options.SourceFolder); WalkDirectoryTree(rootDir); @@ -134,7 +133,7 @@ private static void ProcessFiles(DirectoryInfo folder) _message.Verbose($"Processing {fi.FullName}"); string content = File.ReadAllText(fi.FullName); - // first see if there are links in this file + // first see if there are links in this file if (_rxContent.Matches(content).Any()) { _message.Verbose($" Links detected."); @@ -282,12 +281,12 @@ private static void ProcessFile(DirectoryInfo folder, string filepath) newRelative = $"~/{nameOfMediaFolder}/{relativeMediaPath.Replace('\\', '/')}"; needAbsoluteLink = false; } - else if(extention == ".md") + else if (extention == ".md") { // Case 2: it's a Markdown file in what's been moved // we will do a relative link on it // if not an absolute link on the repo - if((_options.SourceFolder.Length + 1) >= absolute.Length) + if ((_options.SourceFolder.Length + 1) >= absolute.Length) { needAbsoluteLink = true; } @@ -299,8 +298,8 @@ private static void ProcessFile(DirectoryInfo folder, string filepath) needAbsoluteLink = false; } } - - if(needAbsoluteLink) + + if (needAbsoluteLink) { // Case 2: it's a link on a directory in the repo or a file // Adjust the link diff --git a/tools/device-listing/DeviceInfo.cs b/tools/device-listing/DeviceInfo.cs index 59c40e52a5..749b30e815 100644 --- a/tools/device-listing/DeviceInfo.cs +++ b/tools/device-listing/DeviceInfo.cs @@ -9,7 +9,7 @@ namespace Iot.Tools.DeviceListing { - class DeviceInfo : IComparable + internal class DeviceInfo : IComparable { public string Title { get; private set; } public string ReadmePath { get; private set; } @@ -32,6 +32,17 @@ public int CompareTo(DeviceInfo? other) return Title.CompareTo(other?.Title); } + private static string? GetTitle(string readmePath) + { + string[] lines = File.ReadAllLines(readmePath); + if (lines[0].StartsWith("# ")) + { + return lines[0].Substring(2); + } + + return null; + } + private void ImportCategories() { if (!CategoriesFileExists) @@ -53,16 +64,5 @@ private void ImportCategories() } } } - - private static string? GetTitle(string readmePath) - { - string[] lines = File.ReadAllLines(readmePath); - if (lines[0].StartsWith("# ")) - { - return lines[0].Substring(2); - } - - return null; - } } } diff --git a/tools/templates/DeviceBindingTemplate/dotnet_new_device-binding_csharp/tests/_DeviceBindingTests.cs b/tools/templates/DeviceBindingTemplate/dotnet_new_device-binding_csharp/tests/_DeviceBindingTests.cs index 9b06be750e..104d7451ea 100644 --- a/tools/templates/DeviceBindingTemplate/dotnet_new_device-binding_csharp/tests/_DeviceBindingTests.cs +++ b/tools/templates/DeviceBindingTemplate/dotnet_new_device-binding_csharp/tests/_DeviceBindingTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Xunit;