dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.73k stars 1.07k forks source link

Attempting to run a NetStandard class library that's been turned into a console app shows an error about "RunCommand" missing #833

Open mavasani opened 7 years ago

mavasani commented 7 years ago

Ported from https://github.com/dotnet/roslyn-project-system/issues/1309

  1. Create a new C# .NetStandard library.
  2. Open properties pages and change the Application type to Console Application.
  3. Add a static Main method to the class.
  4. F5
---------------------------
Microsoft Visual Studio
---------------------------
Unable to run your project. The "RunCommand" property is not defined.
---------------------------
OK   
---------------------------
mavasani commented 7 years ago

There are bunch of places in the SDK targets where we set RunCommand property, but none of them seems to be doing it the target framework identifier is .NetStandard.

srivatsn commented 7 years ago

What does running on netstandard mean though? What CLR will it use to run?

srivatsn commented 7 years ago

Maybe we should just give a better error message here?

tannergooding commented 7 years ago

I agree. .NETStandard just means that it is runnable on either Desktop or .NETCore. Without some additional context, nothing can actual get run (as the runtime bits well never be copied to the output by NuGet).

mavasani commented 7 years ago

Yes, probably SDK or the compiler should report an error when attempting to build netstandard exe.

jnm2 commented 7 years ago

What's the eventual plan for building netstandard exes? Specify runtime in the project debug properties or when you set the startup project? Or disallow netstandard exes? I'm very curious because a similar conversation comes up around running netstandard tests when we talk about adding netstandard support. How do you specify which runtimes and framework versions to test on?

tannergooding commented 7 years ago

@jnm2, I would guess you target each framework you are meant to test/run on.

For example: In my own projects all my libraries target .NETStandard: <TargetFramework>netstandard1.3</TargetFramework>

All of my tests and executables Multi-Target to Desktop and NETCore: <TargetFrameworks>net46;netcoreapp1.0</TargetFrameworks>

I then have logic to explicitly run the tests using the appropriate runner: once on Desktop and once more on NETCore.

jnm2 commented 7 years ago

@tannergooding Makes sense, so tests or exes targeting netstandard don't make a whole lot of sense and should probably error with a helpful diagnostic message telling you to multitarget to individual platform TFMs?

This is good to know because people are asking for netstandard support in NUnit. The guidance would then be: specify netcoreapp, not netstandard, and if you want to test multiple runtimes and versions then multitarget. Is that the best direction to take?

tannergooding commented 7 years ago

That is my opinion on it, but it might not be the right opinion 😄, someone like @srivatsn would be better suited as to the actual stance here.

This is good to know because people are asking for netstandard support in NUnit.

If the request is that the NUnit libraries support .NETStandard, then I see no reason why they shouldn't (and I thought they already did...). Users should be able to create Utility libraries that the actual test projects can reference, for example.

If the request is that the NUnit runner support executing .NETStandard libraries, then that depends on what the official stance is 😄

dsplaisted commented 7 years ago

I think it makes sense for test projects to be able to target .NET Standard, and then have a way to tell the test runner which frameworks it should run the tests on. There's been some discussion of this previously, but I can't find it at the moment. Currently, the best solution is probably to multitarget your tests as @tannergooding suggests.

For this issue, I would prefer having a better error message rather than entirely preventing you from compiling an Exe targeting .NET Standard, if that's possible.

jnm2 commented 7 years ago

@tannergooding Yes, we're talking about NUnit runner support for actually executing netstandard libraries. NUnit does support executing netstandard libraries, but only netstandard1.6 and only on .NET Core.

An official documented stance that I can point people to would be fabulous. For example:

  • Until we have a standard way to specify which runtimes and versions to execute netstandard tests against (maybe in the .csproj), do not target tests execution libraries to netstandard; instead, multitarget specific runtimes and versions

And keep everyone posted about where the execution runtimes and versions should be specified, and how the test runner will be able to determine what they are for each given test assembly. (Also I can see wanting to be able to test both x86 and x64 for things like performance or interop.) Should I start a new issue for documentation for test runners?

sharwell commented 7 years ago

We just hit this for a net45 project in Microsoft/perfview#346.

sakher-sawan commented 7 years ago

Same for me netstandard1.6 + net47

rjperes commented 7 years ago

Any news on this? How can we go around it?

Lakritzator commented 7 years ago

@rjperes A workaround would be to add the "RunCommand" element to your csproj.

I use the following:

    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
    <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
    <RunCommand>bin\$(Configuration)\$(AssemblyName).exe</RunCommand>

Some info on the two Append elements: https://www.tabsoverspaces.com/233608-stopping-msbuild-appending-targetframework-to-outputpath/

ms0713 commented 7 years ago

Where to specify runcommand? I m not getting inside my PropertGroup tag.

binki commented 6 years ago

Why can’t .netstandard just output an executable that could be activated by either coreclr or netfx (like how netfx executables can be activated by either mono or netfx)? If an assembly is targeting .netstandard, that is only to avoid accidentally referencing nonportable APIs during compiletime but shouldn’t affect the emitted image (beyond getting things like default method parameters and constants from a different assembly than if it were targeting corefx or netfx—but that shouldn’t be any more or less of an issue than it is for the portable class libraries which target .netstandard and support both runtimes with one binary assembly).

dasMulli commented 6 years ago

There are differences in how .net core and netfx/mono resolve native assets (so/dylib/dll) and runtimes. So the most direct need is that the selected target framework triggers build logic to generate runtime artifacts and execute any necessary conflict resolution (e.g. .runtimeconfig.json/.deps.json/runtimes/** for .net core, .exe.config with binding redirects and copied NuGet dlls for net*)

If you are brave (DO NOT USE THIS IN PRODUCTION!!), you can change your csproj to the following:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <OutputType>exe</OutputType>
    <TargetExt>.exe</TargetExt>
  </PropertyGroup>
</Project>

Then copy over a .runtimeconfig.json (renaming it to match the target) from a working .net core app and a .exe.config from a working net* app, you should be able to create simple (!) console apps that can run via some.exe / dotnet some.exe / mono some.exe, but things could go to hell quickly with any RID-specific or TFM-specific packages ("bait-and-switch" packages or nupkgs containing ).

GrahamTheCoder commented 5 years ago

Relatedly, ASP NET non-core works so long as RunComand property is defined and a few other things like this:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <OutputType>Library</OutputType>
    <OutputPath>bin\</OutputPath>
    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
    <RunCommand>$(MSBuildExtensionsPath64)\..\IIS Express\iisexpress</RunCommand>
    <RunArguments>/path:&quot;$(MSBuildProjectDirectory)&quot; /port:18082</RunArguments>
  </PropertyGroup>
  ...rest of file...
</Project>

Obviously use whatever port and IIS bitness you want

(Improved thanks to @rainersigwald)

rainersigwald commented 5 years ago

Ideally we'd be able to use $(ProjectDir) instead of $(SolutionDir)Your\Project\Folder\Path, but that comes back empty for me. Let me know if you find a fix for that.

@GrahamTheCoder Are you looking for $(MSBuildProjectDirectory)? Documentation here.

GrahamTheCoder commented 5 years ago

@rainersigwald Ah yes, that's the one, thanks (I'll update the example for copy and pasting ease)