Embedding resources after build? - c#

I currently have a build setup as follows, allowing me to embed all references DLLs as embedded resources in my assembly. This operates at the AfterResolveReferences target and works flawlessly. It also allows me to produce a single executable which doesn't need any additional DLLs to launch (since it loads these at runtime).
Now, I would like to include the PDB information as well. I already do this with all referenced assemblies, but not the assembly I am building, since that is (for obvious reasons) produced after that target.
To recap:
I am building AssemblyA.exe.
It has AssemblyB.dll and AssemblyC.dll as references, so these are included in AssemblyA.exe as embedded resources during build.
After building AssemblyA.exe, MSBuild also produces a AssemblyA.pdb file.
This is where I want to then also embed AssemblyA.pdb into AssemblyA.exe as embedded resource.
Is that possible somehow? I am aware that this may trigger a double-build.

I ended up writing the following to my project file - works flawlessly. It does a double-build, but it works.
<Target Name="Prebuild">
<CallTarget Targets="Clean" />
<MSBuild Projects="$(SolutionPath)" Targets="Build" Properties="Configuration=Debug;IgnoreRecursion=true" />
</Target>
<Target Name="BeforeBuild">
<ItemGroup>
<_IgnoreRecursion Include="$(IgnoreRecursion)"/>
</ItemGroup>
<CallTarget Targets="Prebuild" Condition="'%(_IgnoreRecursion.Identity)' != 'true'" />
<CreateItem Include="$(TargetDir)\**\*.*">
<Output TaskParameter="Include" ItemName="OutputFiles" />
</CreateItem>
<ItemGroup>
<EmbeddedResource Include="#(OutputFiles)" Condition="('%(OutputFiles.Extension)' == '.dll' Or '%(OutputFiles.Extension)' == '.pdb')">
<LogicalName>%(OutputFiles.DestinationSubDirectory)%(OutputFiles.Filename)%(OutputFiles.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Message Importance="high" Text="Embedding: #(OutputFiles->'%(Filename)%(Extension)', ', ')" />
</Target>

If a double compile is not a problem you can create your own target, compile to a temporay folder via msbuild task and then embed the files you need from this temporary folder.
You have to do a rebuild because otherwise it will cache the assemblies.
Your target to compile in the .proj file would look like this:
<Target Name="YourBuild">
<MSBuild Projects="YourProject.csproj" Targets="Build"
Properties="Configuration=Debug;OutputPath=tmp"/>
<MSBuild Projects="YourProject.csproj" Targets="Rebuild"
Properties="Configuration=Debug"/>
</Target>
Files that are included as EmbeddedResoucre in BeforeBuild target in the project:
<Target Name="BeforeBuild">
<ItemGroup>
<YourFiles Include="tmp\*.pdb" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="#(YourFiles ->'%(Relativedir)%(filename)%(extension)')"/>
</ItemGroup>
</Target>

Related

Visual Studio - Change target framework when multitargeting

IS there a way to change what framework a project (that has multiple target frameworks) is compiled in without updating the csproj?
I'm writing a NuGet package that supports both .NET 4.8 and .NET 6. (I can't use .NET Standard 2.0.)
I have files that I want to be compiled only when targeting .NET 4.8 and others that should only be compiled when targeting .NET 6.0. I know there's a couple ways to achieve this, but I am trying to structure my files such that the directory /AspNetCore contains all of my .NET 6 files and /NetFramework contains all my .NET 4.8 files.
With that, in my csproj, I can do:
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<Compile Remove="NetFramework\**" />
<EmbeddedResource Remove="NetFramework\**" />
<None Remove="NetFramework\**" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<Compile Remove="AspNetCore\**" />
<EmbeddedResource Remove="AspNetCore\**" />
<None Remove="AspNetCore\**" />
</ItemGroup>
But then the /AspNetCore folder is hidden because the IDE is choosing NET48 as the target framework. This is fine when I'm working on the NET48 code, but not ideal when I'm working on the NET6.0 code.
If I were using #if NETFRAMEWORK #elif NET6_0 #endif syntax in each file, then I could select the target framework from the project dropdown, but I want to avoid having compile-time logic in my files. I could also keep going back to my csproj and updating the tag, but I don't want to do that either in case I accidentally forget to change it back.
Is there a good to change the target framework in the IDE when targeting multiple frameworks like this?
Create a Directory.Build.targets file in your solution folder and copy the contents below:
<Project>
<PropertyGroup>
<BuildDependsOn>
RemoveNet48Files;
RemoveNet60Files;
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<Target Name="RemoveNet48Files" Condition="'$(TargetFramework)' != 'net48'">
<ItemGroup>
<Compile Remove="NetFramework\**" />
<EmbeddedResource Remove="NetFramework\**" />
<None Remove="NetFramework\**" />
</ItemGroup>
</Target>
<Target Name="RemoveNet60Files" Condition="'$(TargetFramework)' != 'net6.0'">
<ItemGroup>
<Compile Remove="AspNetCore\**" />
<EmbeddedResource Remove="AspNetCore\**" />
<None Remove="AspNetCore\**" />
</ItemGroup>
</Target>
</Project>
This way the files will be removed from the item group only before build.

Filter CopyLocalLockFileAssemblies output

I set CopyLocalLockFileAssemblies to true and want to filter the output. So I used the following code:
<Target Name="FilterCopyLocalItems" AfterTargets="ResolveLockFileCopyLocalProjectDeps">
<ItemGroup>
<ReferenceCopyLocalPaths Remove="#(ReferenceCopyLocalPaths)" Condition="'%(Filename)' == 'Microsoft.Extensions.DependencyInjection.Abstractions'" />
</ItemGroup>
</Target>
But this code did not work, how can I put a filter on the output?
Your target FilterCopyLocalItems is to remove the reference dll from the output folder.
I wonder if you means that the target cannot be executed.
For me, I used the below xml code in my net core project which installed the nuget package Microsoft.Extensions.DependencyInjection.Abstractions.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<Target Name="FilterCopyLocalItems" AfterTargets="ResolveLockFileCopyLocalProjectDeps">
<ItemGroup>
<ReferenceCopyLocalPaths Remove="#(ReferenceCopyLocalPaths)" Condition="'%(Filename)' == 'Microsoft.Extensions.DependencyInjection.Abstractions'" />
</ItemGroup>
</Target>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" />
</ItemGroup>
</Project>
You can find the target under the Detailed output build log.
Enter Tools-->Options-->Projects and Solutions-->Build and Run-->set MSBuild project build output verbosity to Detailed.
And you can see the target by searching its name under the detailed output log while you build it.
It will prevent the Microsoft.Extensions.DependencyInjection.Abstractions.dll being generated in the output folder.
Update 1
Actually, you may do some extra operation which causes the target ResolveLockFileCopyLocalProjectDeps not to be triggered. Due to the lack of your detailed project structure and CSPROJ file, I did not notice that.
For your situation, the target ResolvePackageDependenciesForBuild works well.
So in your side, you should use this:
<Target Name="FilterCopyLocalItems" AfterTargets="ResolvePackageDependenciesForBuild">
<ItemGroup>
<ReferenceCopyLocalPaths Remove="#(ReferenceCopyLocalPaths)" Condition="'%(Filename)' == 'Microsoft.Extensions.DependencyInjection.Abstractions'" />
</ItemGroup>
</Target>
Besides, when you execute the target, please do not add <ExcludeAssets>Runtime</ExcludeAssets> under the PackageReference of your nuget package, its effect is actually the role of your target FilterCopyLocalItems. At runtime, remove the related package.dll in the output folder. See this document.
So you should delete it to avoid reuse.

Asp.net core 2 - Files are not published

EDIT
For info, I'm developping on macOS using VS Code
I'm trying to include files in my publish process ( Currently cshtmlthat represents my email templates ).
I follow this thread on github but seems that their solutions don't work for me.
Here my csproj to add an unique cshtml file :
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
<ItemGroup>
<EmailFile Include="$(ProjectDir)/EmailTemplates/OrderCompleteEmail.cshtml" />
</ItemGroup>
<Copy SourceFiles="#(EmailFile)" DestinationFolder="$(PublishDir)" SkipUnchangedFiles="false" />
</Target>
Your solution was almost correct, you have to use AfterTargets="Publish":
<Target Name="CopyCustomContentOnPublish" AfterTargets="Publish">
<ItemGroup>
<EmailFile Include="EmailTemplates/OrderCompleteEmail.cshtml" />
</ItemGroup>
<Copy SourceFiles="#(EmailFile)" DestinationFolder="$(PublishDir)" />
</Target>
You can also copy all your email templates in a single Target to the same folder like:
<Target Name="CopyCustomContentOnPublish" AfterTargets="Publish">
<ItemGroup>
<EmailTemplates Include="EmailTemplates\*.cshtml" />
</ItemGroup>
<Copy SourceFiles="#(EmailTemplates)" DestinationFolder="$(PublishDir)%(EmailTemplates.RelativeDir)" />
</Target>

How do i have a .csproj build a .proj file before building itself?

I have a .csproj file and a .proj file. As part of my .proj file I am generating a file to include in the .csproj, so the .proj needs to run first.
How can this be done. I originally tried to add a project reference as follows:
<ProjectReference Include="..\FileGenerator\FileGenerator.proj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
This however gives me the error:
error MSB4057: The target "GetNativeManifest" does not exist in the project
I then noticed there is a BeforeBuild target in my csproj file.
Can I use this to have the other file be built?
Use MSBuild task to invoke other projects. Example:
<Target Name="BeforeBuild">
<MSBuild Projects="..\FileGenerator\FileGenerator.proj" Targets="Build" />
</Target>
If you need any cleanup done as part of the common Clean target, you can plug in custom cleanup target like this:
<Target Name="FileGeneratorClean" BeforeTargets="Clean">
<MSBuild Projects="..\FileGenerator\FileGenerator.proj" Targets="Clean" />
</Target>

XSLTC.EXE MSBuild Task

I have several XSLTs used in my ASP.NET web application.
I want these files to be compiled to dll whenever I build the project.
Currently, I'm compiling the xslts manually by invoking xsltc.exe from vs2010 tools command prompt.
How can I add msbuild task for xsltc.exe so that it will generate assembly whenevr i build my project?
I'm using .NET 4.0.
That works but doesn't really wrap the tool in a MSBuild friendly way.
I came up with this (which was good enough to get by).
<!-- The Transform File Names... -->
<ItemGroup>
<XsltcTransform Include="Transform1.xslt">
<!-- And the generated .Net Class name. -->
<Class>Transform1Class</Class>
</XsltcTransform>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- Sadly using $(OutDir) MUST come after the Import of CSharp.targets -->
<PropertyGroup>
<XSLTCOutputDll>$(OutDir)xslts.dll</XSLTCOutputDll>
</PropertyGroup>
<Target Name="FindXSLTC">
<PropertyGroup>
<XSLTC>"$(TargetFrameworkSDKToolsDirectory)xsltc.exe"</XSLTC>
</PropertyGroup>
</Target>
<Target Name="XSLTC" Inputs="#(XsltcTransform)" Outputs="$(XSLTCOutputDll)" DependsOnTargets="FindXSLTC">
<Exec Command="$(XSLTC) /out:"$(XSLTCOutputDll)" #(XsltcTransform -> ' /class:%(Class) %(FullPath) ')" />
</Target>
<Target Name="BeforeResolveReferences" DependsOnTargets="XSLTC">
</Target>
These targets will let you compile multiple transforms into one DLL.
Running XSLTC before "BeforeResolveRefereneces" is necessary so that you can have an assembly reference to the generated DLL.
<PropertyGroup>
<WinSDK>C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin</WinSDK>
</PropertyGroup>
<Target Name="Build">
<Exec Command="%22$(WinSDK)\xsltc.exe%22 /out:$(OutputPath)\_PublishedWebsites\xyzapp\bin\Xslts.dll /class:ABC %22$(MSBuildProjectDirectory)\xyzapp\a.xslt%22 /class:DEF %22$(MSBuildProjectDirectory)\xyzapp\b.xslt%22 /class:GHI %22$(MSBuildProjectDirectory)\xyzapp\c.xslt%22"/>
</Target>

Categories