runtime: System.PlatformNotSupportedException: System.IO.Ports is currently only supported on Windows. on Archlinux

Description

When using IO Ports on ArchLinux System and dotnet 7.0 or 6.0 I get an exception System.PlatformNotSupportedException: System.IO.Ports is currently only supported on Windows.

SerialPort.GetPortNames() works but not SerialPort()

Reproduction Steps

  1. Make new console application on dotnet 6 or 7
  2. Add the code snippet below
  3. Run on different Linux systems

Expected behavior

Access to UART Serial ports

Actual behavior

Exception thrown

Regression?

No response

Known Workarounds

None so far

Configuration

Dotnet Version

╰─ dotnet --list-sdks    
6.0.111 [/usr/share/dotnet/sdk]
7.0.100 [/usr/share/dotnet/sdk]

╰─ dotnet --list-runtimes 
Microsoft.AspNetCore.App 6.0.11 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.11 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

OS Version, kernel

╰─ lsb_release -a
LSB Version:    n/a
Distributor ID: EndeavourOS
Description:    EndeavourOS Linux
Release:        rolling
Codename:       rolling


╰─ uname -a
Linux keelah-dellG15 6.0.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 16 Nov 2022 17:01:17 +0000 x86_64 GNU/Linux

CPU Architecture :

╰─ uname -m
x86_64

From what I’ve seen on the forums, it works on certain linux distros but not on others such as Manjaro and EndeavourOS (both running archlinux) and might be the same on other more obscure linux distros

Other information

A few lines of code I used on my console program :

//DEFAULT port sur /dev/ttyUSB0
string SERIALPORT = "/dev/ttyUSB0";

//Line below works
List<string> ports = SerialPort.GetPortNames().ToList();
SERIALPORT = ports.Last();

//Crash on line below
SerialPort _serialPort = new SerialPort(SERIALPORT, BAUDRATE, Parity.None, 8, StopBits.One);

_serialPort.ReadTimeout = -1;       // |Pas de timeout
_serialPort.WriteTimeout = -1;      // |
_serialPort.Handshake = Handshake.None;
_serialPort.DataReceived += _serialPort_DataReceived;

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 29 (22 by maintainers)

Commits related to this issue

Most upvoted comments

@am11 Done, patched package is up 😃

@ViktorHofer, I think it does not warrant a backport in our release/7.0 branch simply because this is not a regression. 😃

The non-portable flavor is there since .NET Core 1.0, it was portable that came later around .NET Core 2 when we realized having separate builds for each distro is not scalable model. Plus, this is the first report of the exception which is limited to nuget-with-native-binary on Arch Linux; a-rolling-release distro with VERSION_ID line in /etc/os-release which our sensitive parsing wasn’t expecting.

Thanks @alucryd! 🎉 I ran a quick test and the exception does not reproduce with the latest package dotnet-sdk-7.0.2.sdk102-2-x86_64.

$ docker run --rm amd64/archlinux sh -c \
  'yes | pacman -Syu dotnet-sdk-7.0;

   dotnet new console -n TestIo;
   cd TestIo;

   cat > TestIo.csproj <<"EOF"
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net7.0</TargetFramework>
  <ImplicitUsings>enable</ImplicitUsings>
  <Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="System.IO.Ports" Version="7.0.0" />
</ItemGroup>

</Project>
EOF

  cat >Program.cs <<"EOF"
using System.IO.Ports;

System.Console.WriteLine(typeof(SerialPort).Assembly.Location);

List<string> ports = SerialPort.GetPortNames().ToList();

System.Console.WriteLine("Ports :");
foreach (var port in ports)
{ System.Console.WriteLine(port); }
EOF

  dotnet run;'

result:

...snip...
/TestIo/bin/Debug/net7.0/runtimes/unix/lib/net7.0/System.IO.Ports.dll
Ports :

I was just also looking into this.

In both cases you see:

HostRID is arch.TEMPLATE_VERSION_ID-x64
Falling back to base HostRID: linux-x64

The difference is in the fallback graph:

linux-x64 => [
linux, 
unix-x64, 
unix, 
any, 
base, 
]

This is missing for the non-portable build, and that is a source-build issue.

The information comes from Microsoft.NETCore.App.deps.json runtimes section. For the non-portable build, grep -F '"linux-x64": [' Microsoft.NETCore.App.deps.json comes up empty.

We need to verify this section is present.

https://github.com/dotnet/runtime/issues/78563 is a similar issue, also due to a broken runtimes section. It is fixed on main, 7.0 and maybe 6.0.

@tmds, the fallback RID is in the graph of portable build and missing in non-portable build.

$ COREHOST_TRACE=1 COREHOST_TRACE_VERBOSITY=4 dotnet run -v:diag 2>&1 |\
    curl -F"sprunge=<-" http://sprunge.us

non-portable http://sprunge.us/TypCOf portable http://sprunge.us/QlIRXQ

(search for The rid fallback graph is)


/etc/os-release contract is bad and should be avoided because it can be avoided:

This reliance on/etc/os-release is a weakness in our design and I’m very happy that NativeAOT hosting layer does not use all that (hope it stays this way). The design should be so that we shouldn’t be parsing /etc/os-release file in the first place for anything (more serious than dotnet --info); let alone failing hard if /etc/os-release does not have some expected values on the user system. This file is not part of or owned by dotnet, so we should not monitor and object to random stuff on user system and fail to run because of that.

@krwq, by building source-build tarball, extracting+building code from that tarball and testing with its artifacts. Source-build is currently moving to VMR (dotnet/dotnet repo) and it isn’t quite stable. Once it is stable and there is a tag such as preview 1 at https://github.com/dotnet/dotnet/tags (currently it is empty), it would be:

$ git clone https://github.com/dotnet/dotnet vmr --single-branch --depth 1 --branch FUTURE_TAG # doesn't exist yet!

$ docker run --rm -w /vmr -v$(pwd)/vmr:/vmr amd64/archlinux sh -c \
  'yes | pacman -Syu bash clang cmake git icu inetutils krb5 libgit2 libunwind libxml2 lldb llvm lttng-ust2.12 openssl zlib;
   echo en_US.UTF-8 UTF-8 > /etc/locale.gen;
   locale-gen;
   echo LANG="en_US.UTF-8" > /etc/locale.conf;
   ./prep.sh;
   ./build.sh --clean-while-building --online -- -p:TargetRid=arch-x64'

then install that ‘built from source in a non-portable way’ SDK (step 4 https://github.com/dotnet/dotnet#building) that has arch-64 RID in a fresh container or real ArchLinux host and try publishing @Foxlider’s project with it (after changing net7.0 to net8.0 in csproj). If that published app runs without throwing PlatformNotSupportedException from top post, test passed! Otherwise, we would need to figure out the next issue blocking it.

Currently, with VMR’s main branch, the build is failing to restore packages due to some out-of-sync situation. Figuring that out is a no-go because the main branch is a fast moving target (with no CI hooked to validate the “known good” state).

@ViktorHofer, unfortunately it will not prove that the fix works end-to-end because the issue occurs only on a non-portable build and daily builds are portable builds. If we download portable .NET 7 from https://dot.net, the issue doesn’t repro today.

It would be good if @Foxlider would try .NET 8 Preview 1 when it releases (or the daily builds today - if that’s possible). We are fairly certain that this should fix the wrong runtime assets being picked up but as it didn’t repro for me (presumably because the installation that I used differed) an additional validation would be great.

Let’s keep this open until P1 shipped and someone verified the fix.

@Foxlider I’m guessing some of the native components in different areas need some arch specific binaries. Having said that I think arch should extend linux RID and IMO we should be picking up linux-x64 package if arch-x64 is not present. I’ll see if I can find someone who knows what needs to be changed to fix this.