How to exclude path from precompilation for AspNetCompiler in csproj - c#

example on command line that i use to exclude Admin path with -x
C:\Users\Test>C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v /Application.Web -p D:\Application.Web -x /Application.Web/Admin
after run this result is fine. But when i translate to csproj scripts.
from lib https://msdn.microsoft.com/en-us/library/ms164291.aspx
It does not parameter to exclude path from precompilation.
Currently command on csproj file
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="Application.Web" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<Target Name="AfterBuild">
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
</Target>

You have to call the aspnet_compiler.exe directly as per this blogpost https://matthewrwilton.wordpress.com/2017/03/12/excluding-node_modules-folder-from-asp-net-compilation/
because the AspNetCompiler msbuild task doesn't have a property for exclusions.
Good thing is that you can call it with multiple exclusions, like so:
<PropertyGroup>
<exclusion1>some/path</exclusion1>
<exclusion2>another/path</exclusion2>
</PropertyGroup>
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<Exec Command="$(MSBuildFrameworkToolsPath)aspnet_compiler.exe -v temp -p $(WebProjectOutputDir) -x $(exclusion1) -x $(exclusion2)"/>
</Target>
Just remember to use the -x parameter once per exclusion.

Related

MSBuild target before build

I have project A that need to insert a TextId.cs before build. And, there is project B. TextId.cs would generated after project B is compiled and executed.
Now I'd like to integrate the compile and execute in Directory.Build.targets in project A. It is not worked as I expect. TextId.cs will generate but the build would still failed as no TextId.cs if I set BeforeTargets="BeforeBuild" as below.
Anyone knows that which target is OK? or, any other solution?
<Project>
<ItemGroup>
<ProjectReferences Include="c:\code\textidfilegenerator\*.*proj" />
</ItemGroup>
<Target Name="BuildOtherProjects">
<Message Importance="High" Text="-----------------------" />
<MSBuild
Projects="#(ProjectReferences)"
Targets="Build">
</MSBuild>
</Target>
<Target Name="CopyText" DependsOnTargets="BuildOtherProjects" BeforeTargets="BeforeBuild">
<Message Importance="High" Text="**********************" />
<Exec Command="C:\Code\TextIdFileGenerator\bin\Debug\net6.0\TextIdFileGenerator.exe C:\Code\Sys1500TestDriver\TextProvider\TextIds.cs" IgnoreExitCode="true"/>
</Target>
</Project>
Before you will dive into the comments below, go through these docs:
MSBuild reserved and well-known properties
Common MSBuild project properties
<Project>
<ItemGroup>
<ProjectReferences Include="c:\code\textidfilegenerator\*.*proj" /> <!--you should use relative path like <ProjectReference Include="../**/*.csproj or absolute with MSBuild well-known properties" />-->
</ItemGroup>
<Target Name="BuildOtherProjects"> <!--you don't need this target if you have project reference-->
<Message Importance="High" Text="-----------------------" />
<MSBuild
Projects="#(ProjectReferences)"
Targets="Build">
</MSBuild>
</Target>
<Target Name="CopyText" DependsOnTargets="BuildOtherProjects" BeforeTargets="BeforeBuild"><!-- 'BeforeBuild' will not work because TextIdFileGenerator.exe needs to be created before you will do anything with it so you should use 'AfterTargets="Build"'-->
<Message Importance="High" Text="**********************" />
<Exec Command="C:\Code\TextIdFileGenerator\bin\Debug\net6.0\TextIdFileGenerator.exe C:\Code\Sys1500TestDriver\TextProvider\TextIds.cs" IgnoreExitCode="true"/><!-- again, you should use relative paths or absolute ones with combination of MSBuildProjectDirectory and OutDir -->
</Target>
</Project>

How to add custom compiled file (in Target/Exec) as resource in .csproj's?

I'm doing a few shadereffects in a wpf_c# project and i don't know and i didn't find how to add the bytecode pixelshader (.ps) as Resource after be compiled by a target/exec. This is my csproj code fragment:
<ItemGroup>
<AvailableItemName Include="PixelShader"/>
</ItemGroup>
<ItemGroup>
<PixelShader Include="Shaders\BlueToneShader.fx" />
bla bla bla other shaders bla bla
</ItemGroup>
<Target Name="PixelShaderCompile" Condition="#(PixelShader)!=''" BeforeTargets="Build">
<Exec Command=""C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64\fxc.exe" %(PixelShader.Identity) /T ps_3_0 /E main /Fo%(PixelShader.RelativeDir)%(PixelShader.Filename).ps" />
</Target>
Everything goes fine and the .ps files are correctly generated, as example:
PixelShaderCompile:
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64\fxc.exe" Shaders\BlueToneShader.fx /T ps_3_0 /E main /FoShaders\BlueToneShader.ps
Microsoft (R) Direct3D Shader Compiler 10.1 Copyright (C) 2013 Microsoft. All rights reserved.
compilation object save succeeded; see "folder"...
But now i dont know how to add that .ps file as 'Resource' during the compilation. Any one knows how? I didn't find any clear documentation.
After 3-4 hours of trial-error i found a (i think dirty) way to do it: the msbuild (.csproj) looks like:
<PropertyGroup>
<BuildDependsOn>
PixelShaderCompile
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<ItemGroup>
<AvailableItemName Include="PixelShader">
<Visible>true</Visible>
</AvailableItemName>
</ItemGroup>
<ItemGroup>
<PixelShader ... />
...
</ItemGroup>
<Target Name="PixelShaderCompile" Condition="#(PixelShader)!=''" BeforeTargets="BeforeBuild;BeforeRebuild">
<MakeDir Directories="$(IntermediateOutputPath)%(PixelShader.RelativeDir)" Condition="!Exists('$(IntermediateOutputPath)%(PixelShader.RelativeDir)')" />
//You put your fxc.exe command here
<Exec Command=""C:\bla bla bla\fxc.exe" %(PixelShader.Identity) /T ps_3_0 /E PSmain /O3 /Fo$(IntermediateOutputPath)%(PixelShader.RelativeDir)%(PixelShader.Filename).ps" Outputs="$(IntermediateOutputPath)%(PixelShader.RelativeDir)%(PixelShader.Filename).ps">
<Output ItemName="CompiledPixelShader" TaskParameter="Outputs" />
</Exec>
<ItemGroup>
<Resource Include="#(CompiledPixelShader)" />
</ItemGroup>
</Target>
//If you want to clear the .ps generated file
<Target Name="PixelShaderClean" Condition="#(PixelShader)!=''" AfterTargets="AfterBuild;AfterRebuild">
<Delete Files="$(IntermediateOutputPath)%(PixelShader.RelativeDir)%(PixelShader.Filename).ps" />
</Target>
That's it... So hard because there aren't so many made examples of msbuild files.

Why doesn't AutogenerateBindingRedirects work for a Web.config in Visual Studio 2017

I have a reference to a .Net Standard 2.0 library that requires Microsoft.AspNet.WebApi.Client 5.2.4. This has a lot of dependencies that need to be redirected to use newer versions.
To avoid package/dependency explosion I've updated the first PropertyGroup in the csproj file:
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
I'm expecting AutoGenerateBindingRedirects to prevent me from needing to change the Web.config to match the versions added.
Why do I still need to add binding redirects to my Web.config to resolve assembly conflicts?
It appears that AutoGenerateBindingRedirects will not work for web projects per https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection.
Inspecting the output from the build shows that binding redirects are generated just not in the Web.config. Instead, they are in $(AssemblyName).dll.config. This file has the original configuration from Web.config as well as the binding redirects.
To put it all together you can have MSBuild copy the resulting config back to the Web.config. To do this you would add the following to the csproj:
<Target Name="AfterBuild">
<Copy SourceFiles="$(TargetDir)\$(AssemblyName).dll.config" DestinationFiles="Web.config" />
</Target>
For iis express: In Web.config replace section assemblyBinding with
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<linkedConfiguration href="file:{AssemblyName}.dll.config"/>
</assemblyBinding>
For iis and iis express:
add to project Scripts\CopyRuntimeSection.ps1
param ($from, $to)
$projectPath = Resolve-Path "$($PSScriptRoot)\..\"
$fromFilePath = "$projectPath\$from";
$toFilePath = "$projectPath\$to";
$fromFileXml = [xml](Get-Content -Path $fromFilePath -Raw)
$toFileXml = [xml](Get-Content -Path $toFilePath -Raw)
$toFileXml.configuration.runtime.InnerXml = $fromFileXml.configuration.runtime.InnerXml
$toFileXml.Save($toFilePath)
add to csproj
<Target Name="CopyRuntimeSection" AfterTargets="Build">
<Exec Command="PowerShell -File Scripts\CopyRuntimeSection.ps1 -from $(OutDir)\$(AssemblyName).dll.config -to Web.config" />
</Target>
Expanding on the other answer from this question, here's a solution that supports incremental builds and uses absolute paths for greater flexibility:
Add this somewhere in your solution (I named it UpdateBindingRedirect.ps1):
param ($from, $to)
$fromFileXml = [xml](Get-Content -Path $from -Raw)
$toFileXml = [xml](Get-Content -Path $to -Raw)
if ( $toFileXml.configuration.runtime.InnerXml -Ne $fromFileXml.configuration.runtime.InnerXml ) {
$toFileXml.configuration.runtime.InnerXml = $fromFileXml.configuration.runtime.InnerXml
$toFileXml.Save($to)
}
Add this to your csproj:
<Target Name="UpdateBindingRedirects" AfterTargets="Build" Inputs="$(OutDir)$(AssemblyName).dll.config" Outputs="$(ProjectDir)Web.config">
<Message Text="Update binding redirects from $(ProjectDir)$(OutDir)$(AssemblyName).dll.config" />
<Exec Command="PowerShell -NoLogo -NoProfile -File ..\UpdateBindingRedirects.ps1 -from $(ProjectDir)$(OutDir)$(AssemblyName).dll.config -to $(ProjectDir)Web.config" />
</Target>
There is a recipe from
https://github.com/CZEMacLeod/MSBuild.SDK.SystemWeb
that you can repurpose to update binding redirects on Web.config automatically.
Warning: not tested
Add a property
<PropertyGroup>
<OverwriteAppConfigWithBindingRedirects>true</OverwriteAppConfigWithBindingRedirects>
</PropertyGroup>
Use the build target UpdateConfigWithBindingRedirects as defined here https://github.com/CZEMacLeod/MSBuild.SDK.SystemWeb/blob/main/src/MSBuild.SDK.SystemWeb/Sdk/Sdk.targets
<Target Name="UpdateConfigWithBindingRedirects" AfterTargets="AfterBuild" Condition="'$(OverwriteAppConfigWithBindingRedirects)'=='true'">
<ItemGroup>
<_DllConfig Remove="#(_DllConfig)" />
<_AppConfig Remove="#(_AppConfig)" />
<_ConfigFile Remove="#(_ConfigFileHash)" />
<_DllConfig Include="$(OutDir)$(AssemblyName).dll.config" />
<_AppConfig Include="web.config" />
</ItemGroup>
<GetFileHash Files="#(_DllConfig)">
<Output TaskParameter="Hash" PropertyName="_DllConfigHash" />
<Output TaskParameter="Items" ItemName="_DllConfigFileHash" />
</GetFileHash>
<GetFileHash Files="#(_AppConfig)">
<Output TaskParameter="Hash" PropertyName="_AppConfigHash" />
<Output TaskParameter="Items" ItemName="_AppConfigFileHash" />
</GetFileHash>
<ItemGroup>
<_ConfigFileHash Include="#(_DllConfigFileHash)" />
<_ConfigFileHash Include="#(_AppConfigFileHash)" />
</ItemGroup>
<Message Text="%(_ConfigFileHash.Identity): %(_ConfigFileHash.FileHash)" />
<Warning Text="Replacing web.config due to changes during compile - This should clear warning MSB3276 on next compile" File="web.config" Condition="'$(_DllConfigHash)'!='$(_AppConfigHash)'" />
<Copy SourceFiles="$(OutDir)$(AssemblyName).dll.config" DestinationFiles="web.config" Condition="'$(_DllConfigHash)'!='$(_AppConfigHash)'" />
</Target>

Extract MSBuild Parameter

I have an MSBuild file like this:
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<Param1>Hello world</Param1>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<Param1>Goodbye world</Param1>
</PropertyGroup>
</Project>
I am working on an external application and I need to be able to find out what the configured value of Param1 is. I need a way to correctly evaluate the MSBuild file so that any conditions are applied and then the correct parameter returned to the calling application.
Being able to do something like this would be great:
>MSBuild /p:Configuration=Release MyBuild.proj -extractParam:Param1
>Goodbye World
Any ideas? Is this possible with C# instead?
You can make the project output the value, then parse it using scripting/C#/....
Add this target to your project:
<Target Name="OutputParam1" AfterTargets="Build">
<Message Text="Param1 = $(Param1)"/>
</Target>
it will be invoked automatically after the Build target.
Then on the commandline:
>MSBuild /p:Configuration=Release MyBuild.proj /fl
where /fl cause the file msbuild.log to be generated, which will contain amongst others a line
Param1 = Goodbye world
because of the Message task. Now use e.g. powershell to output the matching part:
>powershell -command "$a = Select-String -Path msbuild.log -Pattern 'Param1 = (.+)'; $a.Matches[0].Groups[1].Value"
>Goodbye world
You can add a target which prints the param value:
<Target Name="ExtractParam1" >
<Message Text="$(Param1)" Importance="high" />
</Target>
The switches /v:m /nologo makes the output print just the value:
msbuild /p:Configuration=Release MyBuild.proj /t:ExtractParam1 /v:m /nologo

How to integrate conditional logic in postbuild events

Hi I have a visual studio project which includes postbuildevents in the following form:
MyTool.exe $(ProjectDir)somesrcfile.txt $(TargetDir)sometargetfile.bin
Now I want to add some logic saying that these steps are taking place only if the files have changed. In peudocode:
if (somesrcfile.txt is newer than sometargetfile.bin)
{
MyTool.exe $(ProjectDir)somesrcfile.txt $(TargetDir)sometargetfile.bin
}
Can I do this with MsBuild?
EDIT:
I just tried it with a simple copy command but it seems not to work. Also the message is not displayed when I build the solution.
<ItemGroup>
<MyTextFile Include="*.txt" />
</ItemGroup>
<Target Name="Build" Inputs="#(MyTextFile)" Outputs="#(MyTextFile->'%(Filename).bin')">
<CustomBuild>
<Message>Encoding files...</Message>
<Command>
copy %(Identity) %(Filename).bin
</Command>
<Outputs>$(OutDir)%(Identity)</Outputs>
</CustomBuild>
</Target>
Yes, it is possible by using the Inputs and Outputs attributes on your target.
See: How to: Build incrementally
In your case, it would look something like this:
<Target Name="AfterBuild" DependsOnTargets="Test">
</Target>
<ItemGroup>
<MyTextFile Include="*.txt" />
</ItemGroup>
<Target Name="Test" Inputs="#(MyTextFile)" Outputs="#(MyTextFile->'%(FileName).bin')">
<Message Text="Copying #(MyTextFile)" Importance="high"/>
<Copy SourceFiles="#(MyTextFile)" DestinationFiles="#(MyTextFile->'%(FileName).bin')" />
</Target>
This target will only run if input files are newer than output files.

Categories