Skip to content

Refactor packaging and remove Fody.nuspec.#1312

Closed
teo-tsirpanis wants to merge 9 commits into
Fody:masterfrom
teo-tsirpanis:nuspec
Closed

Refactor packaging and remove Fody.nuspec.#1312
teo-tsirpanis wants to merge 9 commits into
Fody:masterfrom
teo-tsirpanis:nuspec

Conversation

@teo-tsirpanis

@teo-tsirpanis teo-tsirpanis commented Mar 15, 2025

Copy link
Copy Markdown
Contributor

You should already be a Patron

I am!

Description

The Fody package is currently being created with a .nuspec file which explicitly specifies the locations of every file in the package. This is not ideal because it relies in the exact output paths of the library and its dependencies, and also relies on the solution file to enforce a build order for some projects.

The solution

This PR removes Fody.nuspec and uses MSBuild facilities to define the structure of the Fody package. Some of the techniques used are described here.

Validated by manually inspecting that the files in the Fody package built from this branch are the same, with the following exceptions:

  • The task assemblies were moved from directories netclassictask and netstandardtask to tasks/net472 and tasks/netstandard2.0 for .NET Framework and .NET Standard respectively.
  • Next to the .NET Standard 2.0 task assembly there is a .deps.json file. This helps MSBuild resolve the task's dependencies.
  • FodyHelpers.xml was included in the package (I can remove it if I try more).

Todos

  • Related issues
  • Tests
  • Documentation


<ItemGroup Condition="$(TargetFramework) == 'netstandard2.0'">
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" ExcludeAssets="Runtime" />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes System.Runtime.Loader.dll to not get included. It wasn't included before and it wasn't causing any problem.

@teo-tsirpanis

Copy link
Copy Markdown
Contributor Author

I'm not sure why AzDo fails. 🤔

@teo-tsirpanis
teo-tsirpanis marked this pull request as ready for review March 15, 2025 23:40
@ltrzesniewski
ltrzesniewski self-requested a review March 16, 2025 14:55
@tom-englert

Copy link
Copy Markdown
Member

I'm not sure why AzDo fails. 🤔

Seems to be a server problem, after re-starting they all ran successful

Comment thread Fody/Fody.csproj Outdated
Comment thread Directory.Build.targets
@@ -0,0 +1,16 @@
<Project>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the benefit from putting this into a global scope, and then only enabling for one single project?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is enabled for both Fody and FodyHelpers.

Comment thread Fody/Fody.csproj Outdated

@ltrzesniewski ltrzesniewski left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a comparison of dotnet pack between the old and new:

image

That's obviously not good, and these changes seem more complicated than they should be. I'll do a second review after the package contains everything needed.

Comment thread Fody/Fody.targets
@teo-tsirpanis

Copy link
Copy Markdown
Contributor Author

@ltrzesniewski the dotnet pack behavior seems to be by design (I cleaned-up the repo, ran it and it failed). We set GeneratePackageOnBuild=true in Fody.csproj, which for some reason means that the Build target does not implicitly run.

I've been making the packages on my machine with dotnet build -c Release and it works fine.

@ltrzesniewski

Copy link
Copy Markdown
Member

Hmm, that's linked to NuGet/NuGet.Client#2822 and NuGet/Home#7801 which is supposed to be fixed... I think I'll play with the changes in this PR a bit and try to simplify them.

Do we even want to keep GeneratePackageOnBuild BTW?

@teo-tsirpanis

Copy link
Copy Markdown
Contributor Author

With my latest commits, the list of files in Fody.nupkg is identical to the package released in NuGet, modulo the different folders of task assemblies of each framework.

Comment thread FodyHelpers/FodyHelpers.csproj Outdated
Comment thread Fody/Fody.csproj
<None Include="Fody.targets" Pack="true" PackagePath="build" />
</ItemGroup>

<!-- ExcludeAssets="compile" doesn't work on ProjectReference, and ReferenceOutputAssembly entirely

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's so much hacking with compiler internals. Wouldn't it be much clearer and easier to understand what's going on when we use `ReferenceOutputAssembly=false and then just copy the files manually?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. like this:

  <Target Name="CopyFodyIsolatedToOutput" AfterTargets="CopyFilesToOutputDirectory">
    <ItemGroup>
      <_FodyIsolatedFile Include="..\FodyIsolated\bin\$(Configuration)\$(TargetFramework)\*.*" />
    </ItemGroup>
    <Copy SourceFiles="@(_FodyIsolatedFile)" DestinationFolder="$(OutputPath)" />
  </Target>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My proposed solution does precisely what we want to achieve (remove FodyIsolated from the references we pass to the compiler), and I believe it is less fragile and more maintainable down the road.

Manually copying the output of FodyIsolated assumes that it gets written to a specific location (which could change in the future), and that the project targets the same frameworks as Fody. My proposed solution makes none of these assumptions. Hardcoding output paths also brings back what we were doing in the .nuspec file, the removal of which is the objective of this PR.

I will file a feature request to MSBuild to make this scenario easier (a CopyLocalOutputAssembly metadata on ProjectReference).

@tom-englert

tom-englert commented Mar 23, 2025

Copy link
Copy Markdown
Member

What about moving the NuGet generation to a separate project? This would make all the workarounds obsolete:

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <IncludeBuildOutput>false</IncludeBuildOutput>
    <BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
    <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
    <PackageId>Fody</PackageId>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\FodyIsolated\FodyIsolated.csproj" />
    <ProjectReference Include="..\Fody\Fody.csproj" />
  </ItemGroup>

</Project>

@ltrzesniewski

Copy link
Copy Markdown
Member

I thought about it, but didn't have time to try it yet. I have used this approach in a few projects of mine and it works well.

BTW I'd put <IsPackable>false</IsPackable> in Directory.Build.props - Fody produces lots of useless packages today.

@tom-englert

Copy link
Copy Markdown
Member

Fody produces lots of useless packages today.

Really? Which ones?

@ltrzesniewski

Copy link
Copy Markdown
Member

When I run dotnet pack, I get this:

image

@tom-englert

Copy link
Copy Markdown
Member

I see, I never ran dotnet pack, since build already does the job

@ltrzesniewski

Copy link
Copy Markdown
Member

Oh, and here are examples of how I use a separate project to produce a package:

@tom-englert

Copy link
Copy Markdown
Member

The above sample does exactly what we need in terms of binaries, just metadata and Fody.targets need to be added.

@ltrzesniewski

Copy link
Copy Markdown
Member

I haven't tried it yet, but doesn't that <PackageId>Fody</PackageId> property interfere with <ProjectReference Include="..\Fody\Fody.csproj" />?

@tom-englert

Copy link
Copy Markdown
Member

Yes, there is a warning during restore, that project needs to be renamed.

@tom-englert

Copy link
Copy Markdown
Member

It's only a POC that this is sufficient to output the binaries as we need them

@ltrzesniewski

Copy link
Copy Markdown
Member

I like it if it gets rid of the more complex stuff.

Do you intend to work on it a bit more and push it to this PR?

@tom-englert

Copy link
Copy Markdown
Member

@teo-tsirpanis what do you think? Maybe you can do an adaption, since I probably could continue earliest next weekend.

@teo-tsirpanis

Copy link
Copy Markdown
Contributor Author

I took a first stab at this and created a separate Fody.Package project referencing Fody and FodyIsolated. I got an error : Ambiguous project name 'Fody'., which means that we would have to rename the Fody project to something like Fody.Tasks, which significantly expands the scope of the PR, and by doing this I feel I will spend more time on this PR than I originally planned.

@tom-englert, feel free to push on my branch whenever you have time, thanks.


Also the approach used by RazorBlade and ZeroLog is IMHO an overkill. These are "simple" packages that do not bundle their dependencies and have just an analyzer and some MSBuild files added on top, and the extra packaging project causes the library to be compiled twice as many times.

@ltrzesniewski

ltrzesniewski commented Mar 23, 2025

Copy link
Copy Markdown
Member

Also the approach used by RazorBlade and ZeroLog is IMHO an overkill. These are "simple" packages that do not bundle their dependencies and have just an analyzer and some MSBuild files added on top, and the extra packaging project causes the library to be compiled twice as many times.

Yes, I'm aware. Both of these libraries have good reasons to be set up like that, and they're not as simple as they look like at first glance. The multiple compilation is not an issue at all (both of these projects even have some files be compiled 3 times).

Of course, I wouldn't use this approach for more standard libraries though.

@tom-englert

Copy link
Copy Markdown
Member

Superseded by #1313

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants