Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Producing an MSBuild task package #5063

Open
livarcocc opened this issue Apr 19, 2017 · 18 comments
Open

Producing an MSBuild task package #5063

livarcocc opened this issue Apr 19, 2017 · 18 comments
Labels
Functionality:Pack help wanted Considered good issues for community contributions. Priority:3 Issues under consideration. With enough upvotes, will be reconsidered to be added to the backlog. Type:DCR Design Change Request

Comments

@livarcocc
Copy link

From @natemcmaster on April 14, 2017 18:41

Can we get better support for projects designed to produce MSBuild tasks?

Pain points

  • packing. You can get a task assembly and files into the package, but requires deep understanding of how to alter the default layout of the package in csproj.
    • TargetFrameworks. A package that only has task assemblies/targets is compiled for net46 and netcoreapp1.0, but the package itself can be compatible with any project, regardless of its TFM.
  • task references. When loading a task assembly, MSBuild does not use the NuGet cache to find dependencies. This means we have to package assemblies to sit side-by-side on disk so task loading works.
  • tasks with native dependencies. Never been able to make this work.

Workarounds
Task assembly projects end up looking like this

  <PropertyGroup>
    <!--
      The netstandard1.0 and net45 TFMs don't actually compile tasks.
      Only here so the generated nuspec includes the TFM so it appears as "compatible" on NuGet.org
      and in VS NuGet GUI.

      netstandard1.6 => loaded on dotnet.exe
      net46 => loaded on MSBuild.exe
    -->
    <TargetFrameworks>netstandard1.6;net46;netstandard1.0;net45</TargetFrameworks>
    <!-- Be quiet NuGet. I don't want assemblies in lib/ and yes I'm sure this is right. -->
    <NoPackageAnalysis>true</NoPackageAnalysis>
    <!-- forces SDK to copy dependencies into build output to make packing easier -->
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
  </PropertyGroup>

  <ItemGroup>
    <!-- ensure nuspec doesn't contain any package references as we bundle their assemblies in our package -->
    <PackageReference Update="@(PackageReference)" PrivateAssets="All" />
  </ItemGroup>

  <!-- 
    Copy dependencies into the tools/$(TargetFramework)/ package folders.
    MSBuild does not resolve task runtime dependencies from PackageReference
  -->
  <Target Name="PackTaskDependencies" BeforeTargets="GenerateNuspec">
    <!--
    The include needs to happen after output has been copied to build output folder
    but before NuGet generates a nuspec.
    -->
    <ItemGroup>
      <_PackageFiles Include="bin\$(Configuration)\*\Newtonsoft.Json.dll;bin\$(Configuration)\*\NUglify.dll">
        <PackagePath>tools\%(RecursiveDir)</PackagePath>
        <Visible>false</Visible>
        <BuildAction>Content</BuildAction>
      </_PackageFiles>
    </ItemGroup>
  </Target>

Some MSBuild task projects in the wild:

madskristensen/BundlerMinifier
aspnet/BuildTools
natemcmaster/Yarn.MSBuild

Copied from original issue: dotnet/sdk#1125

@livarcocc
Copy link
Author

cc @emgarten @rohit21agrawal

@livarcocc
Copy link
Author

From @rohit21agrawal on April 14, 2017 21:27

we already added some extension points that should make this easier : NuGet/NuGet.Client#1255

We can't make a generic Task package type since all packages would differ widely. Do you have any concrete suggestions on what you would like to see that would make it easier for you to create task packages?

@livarcocc
Copy link
Author

From @natemcmaster on April 14, 2017 21:59

In a world of unlimited resources, I'd love to see this:

<Project Sdk="Microsoft.Build.Task">
</Project>

Microsoft.NET.Sdk is geared towards runtime packages, not MSBuild task packages. Packages designed to carry MSBuild tasks are fundamentally different from packages that only carry runtime bits.

@livarcocc
Copy link
Author

From @natemcmaster on April 14, 2017 22:18

@rohit21agrawal if you're looking for short-term fixes to the pack task...

One of the problems today is that package authors have to choose between nuspec and csproj=>nuspec generation. Keeping csproj and nuspec aligned is difficult, esp. for users new to MSbuild. Wrangling MSBuild to make csproj=>nuspec generation work right is difficult, even for advanced MSBuild users. I had to read NuGet's source code to figure some of this out.

Anything you can do to make nuspec + csproj easier would help with this ask.

One idea:
Make all msbuild properties available in nuspec. Currently you have to manually marshal these via <NuspecProperties>

<package>
  <metadata>
    <id>${MSBuild.PackageId}</id>
    <version>${MSBuild.PackageVersion}</version>
  </metadata>
</package>

@livarcocc
Copy link
Author

From @rohit21agrawal on April 16, 2017 19:19

@natemcmaster instead of making csproj + nuspec easier, i'd like to understand more about what we can do to make the need for nuspec go away.
what are some of the things that you can't achieve via csproj currently and for which you absolutely do need the nuspec?

@livarcocc
Copy link
Author

From @natemcmaster on April 17, 2017 18:14

@rohit21agrawal

@livarcocc
Copy link
Author

From @conniey on April 18, 2017 20:17

I think this issue overlaps with: dotnet/msbuild#1756

@livarcocc
Copy link
Author

Given that this conversation is happening around pack, I am moving this issue over to NuGet. Please, re-activate or file new issues for specific asks for the SDK to support this.

@clairernovotny
Copy link

clairernovotny commented Jul 6, 2017

Here's another example of this with nasty workarounds:
https://github.com/paulcbetts/refit/blob/04db09ffb67ba500ee2bc3370bc68b81437b46a0/Refit/Refit.csproj#L28-L46

We need to call dotnet publish on the task package we want to include in our library package (we do build-time code generation). It leads to build-time weirdness and is far too hard to figure out. One of the issues is ensuring that the dependent publish is only called once, so that's why it's in a tfm condition. Was all trial-and-error to figure out.

@mishra14 mishra14 added Functionality:Pack Priority:2 Issues for the current backlog. labels Oct 17, 2017
@mishra14 mishra14 added this to the Backlog milestone Oct 17, 2017
@mishra14 mishra14 added Triage:Investigate Triage:NeedsTriageDiscussion Type:DCR Design Change Request and removed Triage:Investigate Priority:2 Issues for the current backlog. labels Oct 17, 2017
@matkoch
Copy link
Contributor

matkoch commented Dec 1, 2017

@onovotny I'm trying to do something similar. Could you please explain how you ended up with build\MSBuild<TargetFramework> being the destination folder? Is this documented somewhere? Unfortunately, the way you've created the package didn't work for me at first try. Have to check later :/

@clairernovotny
Copy link

There is nothing special about the directory name or location; it comes from detecting core msbuild vs full here:

https://github.com/paulcbetts/refit/blob/master/Refit/targets/refit.targets#L12-L13

We also needed a custom msbuild task base type (ContextAwareTask) to handle assembly loading in the correct directory:
https://github.com/paulcbetts/refit/tree/master/InterfaceStubGenerator.BuildTasks

Then in our main library we call publish on each TFM to get all the files we need in the publish output directory:
https://github.com/paulcbetts/refit/blob/master/Refit/Refit.csproj#L39-L50

And finally, include them in the package in the right location.

Hope this helps!

@clairernovotny
Copy link

The point of all of this though is that's far too complicated and painful to create MSBuild tasks that work on both Full and Core MSBuild.

@matkoch
Copy link
Contributor

matkoch commented Dec 1, 2017

@onovotny Thanks for the explanation. It seems that my target file is not getting included. Are there any obstacles you know about that? First tried with build\net45 and build\netstandard1.4 (as in refit), then with just build\ ... but still nothing. For now I'm not using any task assembly; just a <Error Text="..." />.

@clairernovotny
Copy link

Your props/targets file has to match the package id for it to be automatically included. For Refit, that'd be Refit.targets. Do yours match?

@matkoch
Copy link
Contributor

matkoch commented Dec 1, 2017

🤦‍♂️

Thanks to both of you, @onovotny and @jnm2 .

@jnm2
Copy link

jnm2 commented Dec 1, 2017

I remember tripping over the same magic early this year when I wrote my first MSBuild target and tried to package it.

@matkoch
Copy link
Contributor

matkoch commented Dec 1, 2017

This should be part of NuGet package analysis actually. /cc @emgarten

C0nquistadore pushed a commit to Serviceware/Dibix that referenced this issue Jun 17, 2021
C0nquistadore added a commit to Serviceware/Dibix that referenced this issue Jun 18, 2021
@nkolev92 nkolev92 added Priority:3 Issues under consideration. With enough upvotes, will be reconsidered to be added to the backlog. and removed Priority:2 Issues for the current backlog. labels Apr 3, 2023
@jeffkl jeffkl added help wanted Considered good issues for community contributions. and removed Triage:NeedsTriageDiscussion labels May 4, 2023
@jeffkl jeffkl removed this from the Backlog milestone May 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Functionality:Pack help wanted Considered good issues for community contributions. Priority:3 Issues under consideration. With enough upvotes, will be reconsidered to be added to the backlog. Type:DCR Design Change Request
Projects
None yet
Development

No branches or pull requests