Closed dasMulli closed 4 years ago
Would the SDK allow running of xunit tests against desktop .NET/Mono when hosted under MSBuild 15 (the one that runs on the latter CLRs)? AFAIK this is only supported under the CLI as things stand.
@borgdylan this is best filed at https://github.com/Microsoft/vstest for dotnet test
or https://github.com/xunit/xunit for dotnet xunit
(which already has an option to use the system msbuild)
I asked on the xunit repo, since I use linux and therefore use VS Code not VS proper.
despite the confusing name, vstest isn't part of visual studio - it's used by "dotnet test"
Although I can get around normal building by using msbuild
from mono 5.0 directly is there a work-around for this issue when using dotnet pack
? Even if I pass --no-build
it complains about the missing reference assemblies?
@kjnilsson you can use a call like msbuild /t:Pack /p:Configuration=Release
@dasMulli thanks - I get:
error MSB4057: The target "Pack" does not exist in the project
Then i believe this is not an SDK-based project? (<Project Sdk="...">
) This would mean that dotnet pack
also wouldn't work on windows.. (or the same msbuild invocation).
@dasMulli dotnet pack
works if I don't multi-target. No idea if it works on Windows.
See: https://github.com/rabbitmq/rabbitmq-dotnet-client/blob/rabbitmq-dotnet-client-308/projects/client/RabbitMQ.Client/RabbitMQ.Client.csproj
Looks like the stable mono 5 doesn't have a NuGet.Build.Tasks.Pack
"Sdk"
Looks like it. I probably need to look at alternative solutions for now until it can be "properly" supported by the dotnet core tooling. Perhaps paket would work.
@kjnilsson you can add a <PackageReference Include="NuGet.Build.Tasks.Pack" Version="4.0.0" PrivateAssets="All" />
in the meantime. This will make it work with mono msbuild but you should pass /p:NuGetBuildTasksPackTargets=junk-value
to avoid conflicts with the sdk-imported one on windows when using dotnet pack
.
Or fail-proof your project like this:
<Project>
<PropertyGroup>
<NuGetBuildTasksPackTargets>junk-value-to-avoid-conflicts</NuGetBuildTasksPackTargets>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- All your project's other content here -->
<ItemGroup>
<PackageReference Include="NuGet.Build.Tasks.Pack" Version="4.0.0" PrivateAssets="All" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
(i cloned your branch to make sure this works)
Thank you for your help - that does appear to work.
error MSB4057: The target "Pack" does not exist in the project
I filed a bug here and it appears to be fixed now: https://bugzilla.xamarin.com/show_bug.cgi?id=57463
The error still there. @dasMulli I tried the two solutions you claim to work. None works.
Anyone else got this to work?
@Jose-CF which error? I'm also on the ASP.NET / .NET Core community slack (http://tattoocoder.com/aspnet-slack-sign-up/) so we could chat about your setup.
@akoeplinger informed me that mono 5.2 is going to add pack support. And AFAIK mono 5.2 will also use the installed .NET Core SDK (>= 2.0) when installed by means of the same MSBuild SDK resolver that VS 2017 15.3 is going to ship.
@dasMulli and whoever this might help:
In my systems I got to solve this issue by issuing:
export FrameworkPathOverride=$(dirname $(which mono))/../lib/mono/4.5/ #this is just a workaround for a bug that dotnet cli has
So, for me that works everywhere. Dudes I hope you can fix this bug because it is terrible, even with the workaround it is quite bad bug hanging around. So many people losing so many hours of works because of that. And wondering if should still continue using dotnet or use other alternatives. I hope that helps.
/lib/mono/4.5/
doesn't always work. For me it fails when project tries to reference a netstandard lib. I had to set it to /lib/mono/4.6.1-api
. Obviously, that doesn't properly work if solution has projects targeting different framework versions.
You could try setting TargetFrameworkRootPath
to /lib/mono/xbuild-frameworks
. That's where the target frameworks are actually defined.
@kekekeks thank you dude! That will be for sure helpful when I will build NetStandard libs because this bug seems to not to be going to fix.
@DustinCampbell sounds good, but did you try if that really works?
I don't know if it will work being set as an environment variable, but it's what we do in OmniSharp to support Xamarin and .NET Framework projects on OSX/Linux.
@DustinCampbell but I guess that only works for msbuild running on mono since the ToolLocationHelper
only resolves the mono ref assemblies correctly in a few scenarios when actually running on mono and not from the dotnet
CLI.. https://github.com/Microsoft/msbuild/blob/2d8a4341875e532bdf25a14f208f62a7bb547774/src/Utilities/ToolLocationHelper.cs#L3204-L3209
In the meantime, mono 5.2 has been released with support for resolving installed .NET Core SDKs so building directly from mono's msbuild is probably the safest method at the moment.
ah yes, that makes sense @dasMulli.
dotnet run
/usr/local/share/dotnet/sdk/2.0.0/Microsoft.Common.CurrentVersion.targets(1122,5): error MSB3644: The reference assemblies for framework ".NETFramework,Version=v4.5" were not found. To resolve this, install the SDK or Targeting Pack for this framework version or retarget your application to a version of the framework for which you have the SDK or Targeting Pack installed. Note that assemblies will be resolved from the Global Assembly Cache (GAC) and will be used in place of reference assemblies. Therefore your assembly may not be correctly targeted for the framework you intend. [/Users/hatajie/Projects/crud/crud/crud.csproj]
The build failed. Please fix the build errors and run again.
this run in osx 10.12.6
Just tried mono 5.2.0 and .NET Core 2.0 but still cannot build using dotnet build
. Am I correct in thinking that I need to use msbuild
and the dotnet cli
is not yet shipping support.
The dotnet
CLI uses its bundled .NET Core build of MSBuild. Mono now ships its own build of MSBuild which contains the necessary components to discover and use the MSBuild targets and tasks that ship with the dotnet
CLI ("SDKs").
@RehanSaeed do msbuild /t:Restore
, msbuild /t:Build
etc work for you? (assuming msbuild /version
shows a 15.3
version)
Thanks @TheAngryByrd et al. for FrameworkPathOverride
solution! Took a while to assembly the whole set of TFMs work with dotnet build
on TravisCI: https://github.com/neris/NGettext/blob/a701ac6/.travis.yml#L37. Note that for the PCL targets, the catch was to use TargetFrameworkRootPath
instead of FrameworkPathOverride
. Now it works with Mono 5.2 and dotnet 2.0.0 on both Ubuntu and macOS. (with the exception of net35
, which was working with 2.0-api
but not its own dir, so we skip it).
@dasMulli not sure why, but on Ubuntu 17.04 I could only get your workaround to work if I hardcoded the full path for
@ivanatpr can you try $(NuGetPackageRoot)
instead? There are more than 1 package folders by default on 2.0 AFAIK so it will have pathA;pathB
in it. (updated previous post)
@dasMulli yup that fixed it thanks!
Can confirm I have net461
and netstandard2.0
building on Linux (Travis CI) via a mix of approaches in this issue. If it's helpful for anyone to have a reference, here's a (modified for confidentiality) csproj which builds for the two targets, and includes some packages/references conditionally (eg EventLog for net461). For netstandard2.0 I really only needed to change to $(NuGetPackageRoot) from $(NuGetPackageFolders) as dasMulli indicated.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Best thing ever</Description>
<VersionPrefix>2.0.0</VersionPrefix>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<AssemblyName>Best.ThingEver</AssemblyName>
<PackageId>Best.ThingEver</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
<DefineConstants>$(DefineConstants);BESTTHINGEVER_ON_WINDOWS</DefineConstants>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<FrameworkPathOverride>$(NuGetPackageRoot)microsoft.targetingpack.netframework.v4.6.1\1.0.1\lib\net461\</FrameworkPathOverride>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<PackageReference Include="Microsoft.TargetingPack.NETFramework.v4.6.1" Version="1.0.1" ExcludeAssets="All" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="2.0.0" />
<Reference Include="System" />
<Reference Include="System.Runtime" />
<Reference Include="System.ComponentModel" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.XML" />
</ItemGroup>
</Project>
@kierenj Thank you man... really useful for me.
@matkoch
I'm reading through and having a hard time judging because @DustinCampbell's solution for OmniSharp is... a lot to take in. Is there a way to build a new-SDK csproj targeting net20;net35;net40;net45;netstandard1.6
on Linux, given some minimum version of Mono and some workaround paste in the .csproj?
@jnm2 : The minimum version of Mono is likely around 5.2.0. What distro are you using? Mono installation instructions for various distros are available here: http://www.mono-project.com/download/#download-lin. (Note that the CentOS instructions work pretty well for Fedora as well.)
Ubuntu Trusty (it's Travis). Last night I got partway there with Mono 4.6.2 by using:
<PropertyGroup Condition="'$(BaseFrameworkPathOverrideForNetfx)' != ''">
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net20'">$(BaseFrameworkPathOverrideForNetfx)/2.0-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">$(BaseFrameworkPathOverrideForNetfx)/2.0-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net40'">$(BaseFrameworkPathOverrideForNetfx)/4.0-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net45'">$(BaseFrameworkPathOverrideForNetfx)/4.5-api</FrameworkPathOverride>
</PropertyGroup>
<PropertyGroup Condition="'$(BaseFrameworkPathOverrideForNetfx)' == ''">
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\nunit.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
(I'm forced to use the 2.0-api folder for both net20 and net35.)
And build script:
if (IsRunningOnWindows())
{
MSBuild(SOLUTION_FILE, new MSBuildSettings
{
Verbosity = Verbosity.Minimal,
ToolVersion = MSBuildToolVersion.VS2017,
Configuration = configuration
});
}
else
{
var currentFrameworkPath = System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location);
var frameworksBasePath = System.IO.Path.GetDirectoryName(currentFrameworkPath);
DotNetCoreMSBuild(SOLUTION_FILE, new DotNetCoreMSBuildSettings()
.SetConfiguration(configuration)
.WithProperty("BaseFrameworkPathOverrideForNetfx", frameworksBasePath));
}
The only error left is that it's trying to find types in System.Runtime when it should be looking in the referenced System.ValueTuple package.
Another issue now is getting this whole thing to build on OSX because the .NET CLI is refusing to run saying the OS is not supported. Not sure what it needs or if Travis gives me control over that.
In honesty I was expecting the new csproj SDK to be more of a slam dunk given my experiences with it on Windows.
@jnm2 : I suspect that Mono 4.6.2 is a bit too early for this. Using a newer Mono version, you can install both the mono-complete
and msbuild
packages. Once that's done, you can just run msbuild
from the terminal to build your project.
@DustinCampbell And that allows me to drop the FrameworkPathOverride
workaround? Right now Mono latest is crashing when Cake 0.22.2 tries to compile the build script but I'll see what I can do then from the angle of testing the newest releases.
The OmniSharp CI uses Travis's Trusty Ubuntu images without any changes to our csproj files. Project files look like this. Prior to recent changes, they were multi-targeting net46 and netstandard1.6 on Linux, Mac and Windows.
In addition, OmniSharp uses Cake 0.22, so I'm not sure what the trouble is there. However, I've noticed significant differences in some of the Cake semantics on Mono vs. Windows (e.g. Mono scripting turns script variables into local variables of the Main function, whereas Roslyn fields make them fields). @mholo65 or one of the other Cake folks might be able to help out.
Well Cake 0.22 dropped the Mono Scripting engine and uses Roslyn on all platforms. We use Mono 5.2 to build Cake on Linux/MacOS and have worked around the multitarget issue in dotnet
by specifying the FrameworkPathOverride
. Since Cake runs on Mono, it’s a piece of Cake (pun intended) to resolve the path to the Mono libs. This is how we solve this issue: https://github.com/cake-build/cake/blob/develop/build.cake#L54
Using Mono 5.2, I don't need any workaround other than MSBuildSettings.ToolPath = Context.Tools.Resolve("msbuild")
! Unlike the FrameworkPathOverride
approach, this time I don't get ValueTuple/System.Runtime compilation errors. Thanks both of you for the tips; I learned a lot.
I did the quick start tutorial https://www.microsoft.com/net/learn/get-started/macos and changed the TargetFramework to net451. That is when the quick start became rabbit hole. Alot of the information that inched me forward I got from this ticket, or the linked tickets. However, I don't know if this is really all relevant or all these manual workarounds are still needed. Just to get it to build, I had to export FrameworkPathOverride=/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5/
. That allowed me to dotnet build. However, when I do dotnet run
, I got a permission issue. To get past that and to see the real error, I had to sudo dotnet run
. Then I get "The reference assemblies for framework ".NETFramework,Version=v4.5.1" were not found". Why can I build this library and it sees the assemblies, and yet, dotnet run does not? Finally, the other pieces of information is that Visual Studio Community for Mac runs this app just fine when I hit 'run' AND I can run the generated exe by typing mono bin/Debug/net451/myApp.exe
.
I know this ticket started in 2016 and now its 2018. Is this where we are at?
myApp: dotnet --info .NET Command Line Tools (2.1.4)
Runtime Environment: OS Name: Mac OS X OS Version: 10.13 OS Platform: Darwin RID: osx.10.12-x64 Base Path: /usr/local/share/dotnet/sdk/2.1.4/
Microsoft .NET Core Shared Framework Host
Version : 2.0.5 Build : 17373eb129b3b05aa18ece963f8795d65ef8ea54
@patrickjamesbarry I ended making a dotnet tool to help run mono apps: https://github.com/TheAngryByrd/dotnet-mono
I got mileage from this, placed in a netfx.props
and imported in each relevant project.
It looks for Mono reference assemblies in the usual Xamarin and /usr install locations
<PropertyGroup>
<!-- When compiling .NET SDK 2.0 projects targeting .NET 4.x on Mono using 'dotnet build' you -->
<!-- have to teach MSBuild where the Mono copy of the reference asssemblies is -->
<TargetIsMono Condition="$(TargetFramework.StartsWith('net4')) and '$(OS)' == 'Unix'">true</TargetIsMono>
<!-- Look in the standard install locations -->
<BaseFrameworkPathOverrideForMono Condition="'$(BaseFrameworkPathOverrideForMono)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/Library/Frameworks/Mono.framework/Versions/Current/lib/mono')">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono</BaseFrameworkPathOverrideForMono>
<BaseFrameworkPathOverrideForMono Condition="'$(BaseFrameworkPathOverrideForMono)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/usr/lib/mono')">/usr/lib/mono</BaseFrameworkPathOverrideForMono>
<BaseFrameworkPathOverrideForMono Condition="'$(BaseFrameworkPathOverrideForMono)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/usr/local/lib/mono')">/usr/local/lib/mono</BaseFrameworkPathOverrideForMono>
<!-- If we found Mono reference assemblies, then use them -->
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net45'">$(BaseFrameworkPathOverrideForMono)/4.5-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net451'">$(BaseFrameworkPathOverrideForMono)/4.5.1-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net452'">$(BaseFrameworkPathOverrideForMono)/4.5.2-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net46'">$(BaseFrameworkPathOverrideForMono)/4.6-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net461'">$(BaseFrameworkPathOverrideForMono)/4.6.1-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net462'">$(BaseFrameworkPathOverrideForMono)/4.6.2-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net47'">$(BaseFrameworkPathOverrideForMono)/4.7-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net471'">$(BaseFrameworkPathOverrideForMono)/4.7.1-api</FrameworkPathOverride>
<EnableFrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != ''">true</EnableFrameworkPathOverride>
<!-- Add the Facades directory. Not sure how else to do this. Necessary at least for .NET 4.5 -->
<AssemblySearchPaths Condition="'$(BaseFrameworkPathOverrideForMono)' != ''">$(FrameworkPathOverride)/Facades;$(AssemblySearchPaths)</AssemblySearchPaths>
</PropertyGroup>
I also had to add a couple of explicit references to Facade assemblies such as System.Runtime and System.IO.
I'm not sure if I met the same problem here, but I solved it as follows.
Here's the content of my paket.references
group Reference
source https://dotnet.myget.org/F/dotnet-core/api/v3/index.json
nuget Microsoft.TargetingPack.NETFramework.v4.6.1
then (after paket install
) build the project by:
dotnet build MyProject.csproj -c Release /p:FrameworkPathOverride=${PROJECT_DIR}/packages/reference/Microsoft.TargetingPack.NETFramework.v4.6.1/lib/net461 /p:RuntimeIdentifier=win7-x64 --output $BUILD_OUT
See @cwe1ss's better solution below
I've reached this issue searching for a way to build the same multi-target csproj file on Windows and Linux skipping unsupported targets. If you need to build on Linux/Mac but avoid building net4* targets, the solution is:
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">netstandard1.3;netstandard2.0;net451;net47</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netstandard1.3;netstandard2.0</TargetFrameworks>
Kudos goes to @nguerrera for helping with this.
@CIPop. Not sure if it's still true but at one point VS was not happy with not having at least one TargetFrameworks without conditions. I worked around that by having the first one be unconditional (Windows case) and then overwriting it in non Windows case:
<TargetFrameworks>netstandard1.3;netstandard2.0;net451;net47</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netstandard1.3;netstandard2.0</TargetFrameworks>
It's all basically the same but I prefer having the universal targets first and then adding the windows targets. This ensures that targets don't have to be defined multiple times:
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net451</TargetFrameworks>
VS was not happy with not having at least one TargetFrameworks without conditions
I confirm that it is still not happy at all (and the issue is not obvious at all). One nit-pick @cwe1ss - looks like VS picks by default the first TFM (in the above case the default would be netstandard1.3). Since I normally want to develop on the netstandard2.0 TFM most of the times, I'm reversing the order in the first TargetFrameworks statement.
[edited]
One other nit-pick: if it's defined as above, you won't be able to use the new VS menu that allows TFM selection in Visual Studio:
Side-effect: There's a chance having net47/451 in the default won't play well with VS for Mac...
Just coming to this issue after trying ages ago... it feels to me like Mono is a red herring here, or at least could be.
In my situation, I don't need to execute any code targeting net45; I just want to be able to build it. As such, it feels like I shouldn't need anything other than reference assemblies, and some way of telling the dotnet SDK where they are. Is that a hopelessly naive position?
The dotnet CLI allowed to build
net*
targets on mac and linux using the reference assemblies installed with mono. It seems this is not supported by the targets inMicrosoft.NET.Sdk
. For fun, I also tried referencing the targeting pack NuGets which also didn't work.Is there a plan for bringing back support for building
net*
projects on linux/mac? Or should it already work?