Fody / Costura

Embed references as resources

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

  1. 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_ResolvedCopyLocalPublishAssetsResolvedFileToPublish

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.