I need to choose profile from launchSettings.json based on chosen build config. Lets say I have build config Debug, Debug-2 and I need to choose different profile for each of them from launchSettings.json. Is there a way to do that?
I use "Copy" MSBuild task to override launchSettings.json with either Debug or Release versions during the build:
<ItemGroup>
<UpToDateCheckInput Include="Properties/launchSettings.$(Configuration).json" />
</ItemGroup>
<Target Name="CleanLaunchSettings" AfterTargets="Clean">
<Message Importance="high" Text="Deleting launchSettings.json..." />
<Delete Files="Properties/launchSettings.json" />
</Target>
<Target Name="CopyLaunchSettings" AfterTargets="Build" Inputs="Properties/launchSettings.$(Configuration).json" Outputs="Properties/launchSettings.json">
<Message Importance="high" Text="Copying launchSettings.$(Configuration).json..." />
<Copy SourceFiles="Properties/launchSettings.$(Configuration).json" DestinationFiles="Properties/launchSettings.json" />
</Target>
Related
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>
I want to embed local references in the assembly before compiling the main unit. But the written target does not work.
<Target Name="EmbedLocal" BeforeTargets="CoreCompile">
<Message Text="Run EmbedLocal for $(MSBuildProjectFullPath)..." Importance="high"/>
<ItemGroup>
<EmbeddedResource Include="#( ReferencePath->WithMetadataValue( 'CopyLocal', 'true' )->Metadata( 'FullPath' ) )"/>
</ItemGroup>
<Message Text="Embed local references complete for $(OutputPath)$(TargetFileName)." Importance="high" />
</Target>
#(EmbeddedResource) at this moment contains valid list of paths.
Update:
Now my import file contains:
<Project ToolsVersion="$(MSBuildToolsVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<EmbedLocalReferences Condition=" '$(EmbedLocalReferences)' == '' ">True</EmbedLocalReferences>
</PropertyGroup>
<Target Name="EmbedLocal" BeforeTargets="ResolveReferences" Condition=" '$(EmbedLocalReferences)' == 'True' ">
<Message Text="Run EmbedLocal for $(MSBuildProjectFullPath)..." Importance="high"/>
<ItemGroup>
<EmbeddedResource Include="#(ReferenceCopyLocalPaths->WithMetadataValue( 'Extension', '.dll' )->Metadata( 'FullPath' ))">
<LogicalName>%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Message Text="Embed local references complete for $(OutputPath)$(TargetFileName)." Importance="high" />
</Target>
</Project>
It works fine. Output assembly contains all .dll references as EmbeddedResource.
MSBuild. Create EmbeddedResource before build
You can try to use BeforeBuild action to the csproj file to include the embedded resources:
<Target Name="BeforeBuild">
...
<ItemGroup>
<EmbeddedResource Include="..."/>
</ItemGroup>
...
</Target>
Now MSBuild will add this file as embedded resource into your assembly.
Update:
Thanks #Martin Ullrich. He pointed out the correct direction, we could use <Target Name="EmbedLocal" BeforeTargets="PrepareForBuild"> in the Directory.Build.props to resolve this issue. You can check if it works for you.
<Target Name="EmbedLocal" BeforeTargets="PrepareForBuild">
...
<ItemGroup>
<EmbeddedResource Include="..."/>
</ItemGroup>
...
</Target>
I have an application which I want to publish with ClickOnce via command line. I have a test and a live version. It should be allowed to have both installed at the same time, which means that I need to change the assemble name (and preferably also the product name) for one of the builds. I would like to do this in the build settings.
I have managed to make some build settings, which works fine, but I cannot figure out how to change the assembly and product name, for just one of them.
I have added the following code to my .csproj file, which I call with the command msbuild /target:Test or msbuild /target:Live. But where do I implement the assembly and product name change?
<PropertyGroup>
<ProjLocation>$(ProjectDir)</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Debug</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<DeploymentFolder>C:\MyProjects\Software\Publish\</DeploymentFolder>
</PropertyGroup>
<!-- Build settings for live version -->
<Target Name="Live" DependsOnTargets="Clean">
<MSBuild Projects="$(ProjLocation)\$(ProjectName).csproj"
Properties="$(DefaultBuildProperties)"
Targets="Publish"/>
<ItemGroup>
<SetupFiles Include="$(ProjPublishLocation)\*.*"/>
<UpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(SetupFiles)" DestinationFolder="$(DeploymentFolder)\Live\" />
<Copy SourceFiles="#(UpdateFiles)" DestinationFolder="$(DeploymentFolder)\Live\Application Files\%(RecursiveDir)"/>
</Target>
<!-- Build settings for test version -->
<Target Name="Test" DependsOnTargets="Clean">
<MSBuild Projects="$(ProjLocation)\$(ProjectName).csproj"
Properties="$(DefaultBuildProperties)"
Targets="Publish"/>
<ItemGroup>
<SetupFiles Include="$(ProjPublishLocation)\*.*"/>
<UpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(SetupFiles)" DestinationFolder="$(DeploymentFolder)\Public Test\" />
<Copy SourceFiles="#(UpdateFiles)" DestinationFolder="$(DeploymentFolder)\Public Test\Application Files\%(RecursiveDir)"/>
</Target>
You can add "AssemblyName" property to the PropertyGroup. like this:
<PropertyGroup>
<AssemblyName>YourAppName</AssemblyName>
</PropertyGroup>
or you can use the MSBuild command line switch. like this :
msbuild /property:AssemblyName=YourAppName
I had almost the same task (need to distinguish between Staging and Production) and I solved it with the following MSBuild-Target:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- call "nuget restore Deployment\packages.config" before executing this script -->
<Import Project="..\packages\MSBuildTasks.1.5.0.235\build\MSBuildTasks.targets" />
<PropertyGroup>
<Configuration>Release</Configuration>
<ClientProject>..\MyProject\MyProject.vbproj</ClientProject>
<ClientPublishDir Condition="$(Environment) == 'Staging'">\\deployment-staging\MyProject\</ClientPublishDir>
<ClientPublishDir Condition="$(Environment) == 'Production'">\\deployment\MyProject\</ClientPublishDir>
</PropertyGroup>
<ItemGroup>
<ClientModifiedAppConfig Include="$(ClientProject)\..\App.$(Environment).config" />
</ItemGroup>
<Target Name="DeployClient">
<Error Condition="$(Environment) == ''" Text="The Property 'Environment' has not been set." />
<Error Condition="$(Environment) != 'Staging' AND $(Environment) != 'Production'" Text="The Property 'Environment' has not been set properly. Valid values are 'Staging' and 'Production'." />
<!-- Sets different assembly names for INT and PRD applications. Due to this, both INT and PRD applications with the
same version number can be installed on the same system. -->
<XmlUpdate Condition="$(Environment) == 'Staging'" Prefix="n" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XmlFileName="$(ClientProject)" Xpath="/n:Project/n:PropertyGroup/n:AssemblyName" Value="MyProjectStaging" />
<XmlUpdate Condition="$(Environment) == 'Staging'" Prefix="n" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XmlFileName="$(ClientProject)" Xpath="/n:Project/n:PropertyGroup/n:ProductName" Value="MyProject Staging" />
<XmlUpdate Condition="$(Environment) == 'Production'" Prefix="n" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XmlFileName="$(ClientProject)" Xpath="/n:Project/n:PropertyGroup/n:AssemblyName" Value="MyProject" />
<XmlUpdate Condition="$(Environment) == 'Production'" Prefix="n" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XmlFileName="$(ClientProject)" Xpath="/n:Project/n:PropertyGroup/n:ProductName" Value="MyProject" />
<!-- Overwrites the original App.config with the environment-dependent App.config.
Reason: ClickOnce only uses App.config and does not apply transformations, as it is done in Web Projects. -->
<Copy
SourceFiles="#(ClientModifiedAppConfig)"
DestinationFiles="$(ClientProject)\..\App.config"
OverwriteReadOnlyFiles="true"
/>
<!-- Publish -->
<MSBuild
Projects="$(ClientProject)"
Targets="Publish"
Properties="
PublishDir=$(ClientPublishDir);
Configuration=$(Configuration);
Platform=x86" />
</Target>
</Project>
To make this work, you have to restore the NuGet package MSBuildTasks.
Finally, all I have to call is msbuild Deployment.targets /t:DeployClient /p:Environment=Staging
Hope that helps!
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>
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.