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.71k stars 1.06k forks source link

Using Microsoft.NET.Sdk.Publish differs per platform #16469

Open kaylumah opened 3 years ago

kaylumah commented 3 years ago

Hi,

Not sure If the question should be asked here, or over at MSBuild.

We are in the progress of upgrading our applications to .netcore. Being .netcore does not necessarly mean we can run cross platform. One of the issues we have is that we use TransformXML as a build task. For this we use

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

This works fine when building with VisualStudio or at least visual studio installed. It won't however work on a Mac or Unix machine. I shared a solution which works for the moment here https://stackoverflow.com/questions/16646698/the-transformxml-task-was-not-found-error-msb4036-on-teamcity-build/66748601#66748601

The solution I came up with consistst of the following (got lucky finding the DLL on google, the rest was done using MSBuild knowledge and peaking at dotnet/arcade)

 <PropertyGroup>
    <XmlTransformDllPath Condition="'$(XmlTransformDllPath)' == '' AND '$(MSBuildRuntimeType)' == 'core'">$(MSBuildSDKsPath)/Microsoft.NET.Sdk.Publish/tools/net5.0/Microsoft.NET.Sdk.Publish.Tasks.dll</XmlTransformDllPath>
    <XmlTransformDllPath Condition="'$(XmlTransformDllPath)' == '' AND '$(MSBuildRuntimeType)' != 'core'">$(MSBuildSDKsPath)/Microsoft.NET.Sdk.Publish/tools/net472/Microsoft.NET.Sdk.Publish.Tasks.dll</XmlTransformDllPath>
    <XmlTransformDllPath Condition="!Exists($(XmlTransformDllPath))">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll</XmlTransformDllPath>
  </PropertyGroup>
  <UsingTask TaskName="TransformXml" AssemblyFile="$(XmlTransformDllPath)" />

I have two problems with this solution

1) For some reason MSBuildSDKsPath and MSBuildExtensionsPath32 are different on windows when using CLI vs VS2019

CLI MSBuildSDKsPath = C:\Program Files\dotnet\sdk\5.0.103\Sdks MSBuildExtensionsPath32 = C:\Program Files\dotnet\sdk\5.0.103

Vs2019 MSBuildSDKsPath = C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Sdks MSBuildExtensionsPath32 = C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild

Which on my Mac returns /usr/local/share/dotnet/sdk/5.0.201

Is there a consistent way of getting the SDK directory? Since if I can get that directory I can load the assembly

2) The TFM change on each version

If I have the NET5 SDK installed I get the directories

C:\Program Files\dotnet\sdk\5.0.103\Sdks\Microsoft.NET.Sdk.Publish\tools\net5.0 C:\Program Files\dotnet\sdk\5.0.103\Sdks\Microsoft.NET.Sdk.Publish\tools\net472

If I have the netcore3 sdk installed I get the directories

C:\Program Files\dotnet\sdk\3.1.406\Sdks\Microsoft.NET.Sdk.Publish\tools\netcoreapp2.1 C:\Program Files\dotnet\sdk\3.1.406\Sdks\Microsoft.NET.Sdk.Publish\tools\net46

Is there a safe way to get the full path to this dll?

dotnet-issue-labeler[bot] commented 3 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

dasMulli commented 3 years ago

These differences are to be expected as the SDKs wil be resoved from different consumers (full-framwork msbuild aka msbuild.exe or in-process in Visual Stodio vs the .NET Core based msbuild version avilable via dotnet msbuild and used by other CLI commands) and can also exist in multiple versions and/or locations.

Anyway, if your project is already using a web SDK (asp.net core apps, blazor, razor library, worker service, ...) then the TransformXml task should already be available. If not, you should be able to use this SDK via <Project SDK="Microsoft.NET.Sdk;Microsoft.NET.Sdk.Publish"> in the csproj and the task should be available.

kaylumah commented 3 years ago

Cool thanks @dasMulli I can confirm this works, even better than the hack I came up.

We also have couple of sfproj files in there.

As far as I know the <Project Sdk="Microsoft.NET.Sdk"> bit imports Sdk.props before your project and Sdk.targets after your project.

Since SFProj is not SDK style I am doing this manual

Do you see any issues with this approach ?

dasMulli commented 3 years ago

As I have not used sfproj much before, I cannot really comment on that. I suppose it shouldn't do much damage but I can't guarantee it. Most of this SDK's logic is triggered by using publish profiles.

As I see you're also using a Mac: Building .sfproj from the dotnet CLI may work by accident but it's not really a "supported" scenario.

kaylumah commented 3 years ago

Unfortunately it did not work, do I succeeded in building it on all platforms importing the sdk had a side effect which broke the build in vs2019. (not sure yet what exactly, but since sdk can change many msbuild variables like output folders not sure if it is the right approach). My original approach with the direct dll and fallback scenarios does work, but it feels very hacky

anon17 commented 3 years ago

Does work only in asp projects? Is there an easy way to use in a console app project?

kaylumah commented 3 years ago

@anon17 if you only need xml transform (not the service fabric bits etc) this will work with console so long its sdk style project. Let me know if you need any help with that

theCuriousOne commented 1 year ago

@kaylumah I am currently facing similar issue where I want to use the XMLTransform form a Build.targets file, but so far I can't find any documentation on that. Could you help?

kaylumah commented 1 year ago

@theCuriousOne there is no documentation :(

So long as visual studio and dotnet cli are not in sync, you need undocumented workarounds

MarvinNorway commented 1 year ago

What solved it for me was moving to SDK-style .csproj files and having

<Project Sdk="Microsoft.NET.Sdk;Microsoft.NET.Sdk.Publish">

at the top (the task is included in the Publish SDK). No other imports are needed then. Not sure if it helps in your specific cases, but maybe this helps someone.

kaylumah commented 1 year ago

yeah considered that as well, but there is a reason its two sdks so there must be other differences that now get imported

theCuriousOne commented 1 year ago

After quite a lot of digging (and to save someone else the trouble)...

I had the following inside Directory.Build.targets <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

this line of code behaves differently if the solution is build from VisualStudio (it works as expected), vs if it is build via command line with dotnet build (it doesn't work). The issue is that different MSBuild(s) are used: one comes packed with dotnet cli, and the other is from VisualStudio. The simplest way to resolve this is to remove the line and after the tag add <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.Publish"/>

What this does is to load all the tasks recursively from the SDK the respectable point of the executable MSBuild, making it possible for both VisualStudio and dotnet cli to find the Task.