Embedded assemblies should not be published
0xced opened this issue · comments
Please check all of the platforms you are having the issue on (if platform is not listed, it is not supported)
- WPF
- UWP
- iOS
- Android
- .NET Standard
- .NET Core
Component
What component is this issue occurring in?
Costura.Fody
Version of Library
Costura 5.1.0
Version of OS(s) listed above with issue
Windows (any version, but not really relevant)
Steps to Reproduce
- Publish a project which is using Costura
MyApp.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
<DebugType>embedded</DebugType>
<GenerateSupportedRuntime>false</GenerateSupportedRuntime>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="5.1.0" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
</ItemGroup>
</Project>
Program.cs:
class Program
{
static void Main()
{
System.Console.WriteLine("Hello World!");
}
}
Publish command:
dotnet publish --configuration Release
Expected Behavior
The publish directory contains exclusively the MyApp.exe
file.
Expected content of the bin
directory:
bin
└───Release
└───net472
│ MyApp.exe
└───publish
MyApp.exe
Actual Behavior
The publish directory contains all referenced assemblies (Newtonsoft.Json.dll
, Serilog.dll
and Serilog.Sinks.Console.dll
) in addition to MyApp.exe
, although they are properly embedded in the executable.
Actual content of the bin
directory:
bin
└───Release
└───net472
│ MyApp.exe
└───publish
MyApp.exe
Newtonsoft.Json.dll
Serilog.dll
Serilog.Sinks.Console.dll
Additional notes
In order to workaround this issue, I have to add this MSBuild target in all my projects using Costura:
<Target Name="RemoveAlreadyEmbeddedFilesFromPublish" AfterTargets="ComputeResolvedFilesToPublishList">
<ItemGroup>
<!-- In order not to publish dll files (already embedded by Costura.Fody) -->
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="%(ResolvedFileToPublish.CopyLocal) == 'true'" />
</ItemGroup>
</Target>
It would be nice if Costura would perform this workaround itself.
Root cause
Although Fody synchronizes the removed CopyLocal references by Costura, they end up in the publish directory anyway. Here are the relevant (stripped down) targets that explain why the dlls end up in the publish directory:
<Target Name="FodyUpdateCopyLocalFilesTarget" AfterTargets="FodyTarget">
<Fody.UpdateReferenceCopyLocalTask ReferenceCopyLocalFiles="@(ReferenceCopyLocalPaths)" IntermediateCopyLocalFilesCache="$(IntermediateOutputPath)$(MSBuildProjectFile).Fody.CopyLocal.cache">
<Output TaskParameter="UpdatedReferenceCopyLocalFiles" ItemName="FodyUpdatedReferenceCopyLocalPaths" />
</Fody.UpdateReferenceCopyLocalTask>
<ItemGroup>
<ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" />
<ReferenceCopyLocalPaths Include="@(FodyUpdatedReferenceCopyLocalPaths)" />
</ItemGroup>
</Target>
<Target Name="ResolvePackageAssets" Condition=" '$(DesignTimeBuild)' != 'true' Or Exists('$(ProjectAssetsFile)')" DependsOnTargets="ProcessFrameworkReferences;_DefaultMicrosoftNETPlatformLibrary;_ComputePackageReferencePublish">
<ResolvePackageAssets ProjectAssetsFile="$(ProjectAssetsFile)" ProjectAssetsCacheFile="$(ProjectAssetsCacheFile)" ProjectPath="$(MSBuildProjectFullPath)" ProjectLanguage="$(Language)" PackageReferences="@(PackageReference)">
<Output TaskParameter="RuntimeAssemblies" ItemName="RuntimeCopyLocalItems" />
</ResolvePackageAssets>
</Target>
<Target Name="ResolveLockFileCopyLocalFiles" DependsOnTargets="ResolvePackageAssets;RunProduceContentAssets">
<ItemGroup>
<_ResolvedCopyLocalBuildAssets Include="@(RuntimeCopyLocalItems)" Condition="'%(RuntimeCopyLocalItems.CopyLocal)' == 'true'" />
<ReferenceCopyLocalPaths Include="@(_ResolvedCopyLocalBuildAssets)" Condition="'$(CopyLocalLockFileAssemblies)' == 'true'" />
</ItemGroup>
</Target>
<Target Name="_ResolveCopyLocalAssetsForPublish" DependsOnTargets="ResolveLockFileCopyLocalFiles;_ComputeUseBuildDependencyFile;_DefaultMicrosoftNETPlatformLibrary;ResolveRuntimePackAssets;_ComputePackageReferencePublish">
<ItemGroup Condition="'$(PreserveStoreLayout)' != 'true' And '@(RuntimeStorePackages)' == ''">
<_ResolvedCopyLocalPublishAssets Include="@(_ResolvedCopyLocalBuildAssets)" Condition="'%(_ResolvedCopyLocalBuildAssets.CopyToPublishDirectory)' != 'false' "/>
</ItemGroup>
</Target>
<Target Name="ComputeResolvedFilesToPublishList" DependsOnTargets="_ComputeResolvedCopyLocalPublishAssets;_ComputeCopyToPublishDirectoryItems;ComputeRefAssembliesToPublish">
<ItemGroup>
<ResolvedFileToPublish Include="@(_ResolvedCopyLocalPublishAssets)">
<RelativePath>%(_ResolvedCopyLocalPublishAssets.DestinationSubDirectory)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
We can see where the ResolvedFileToPublish
comes from:
RuntimeCopyLocalItems
→ _ResolvedCopyLocalBuildAssets
→ _ResolvedCopyLocalPublishAssets
→ ResolvedFileToPublish
Unfortunately, it does not use the ReferenceCopyLocalPaths
build items (properly updated by Fody) but it uses RuntimeCopyLocalItems
direclty, from the result of the ResolvePackageAssets
target.
I think the "workaround" is to set the assemblies as "privateassets". when they are set as private assets, .net will not publish them.
I think the "workaround" is to set the assemblies as "privateassets". when they are set as private assets, .net will not publish them.
Indeed, adding PrivateAssets="all"
works, the dlls do not end up in the publish directory. But this really feels fighting against the tooling: none of Visual Studio, Rider or the dotnet cli sets PrivateAssets on package references which are not development dependencies. This means I would have to manually add PrivateAssets="all"
each time a dependency is added. 😞
I think the proposed solution in #690 is a better way to solve this issue.
Great ticket, great PR, thank you.