How to read values from a config file while building a project? - c#

I have programmed a bootstrapper-project with WiX 3.8 and VS 2013 (C#).
In the project file You can create properties like that one for getting the build version from the EXE-file which shall be installed:
<Target Name="BeforeBuild">
<!-- Set the Variable BuildVersion -->
<GetAssemblyIdentity AssemblyFiles="../FilesForSetup/MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AsmInfo" />
</GetAssemblyIdentity>
<CreateProperty Value="%(AsmInfo.Version)">
<Output TaskParameter="Value" PropertyName="BuildVersion" />
</CreateProperty>
<CreateProperty Value="$(DefineConstants)">
<Output TaskParameter="Value" PropertyName="DefineConstantsOriginal" />
</CreateProperty>
<CreateProperty Value="$(DefineConstants);BuildVersion=$(BuildVersion)">
<Output TaskParameter="Value" PropertyName="DefineConstants" />
</CreateProperty>
</Target>
Now i want to put some properties for a webserver login (url, user, pw) into a config file, read the properties while build and put them into project properties.
Can this be done and which type of config file would be the best choice (p.e. XML)?
Thanks in advance!

Yes, it can be done and the best choice for the config file type is XML.
Per example you can write a config file for web server access like that:
<?xml version="1.0" encoding="utf-8" ?>
<parameters>
<parameter name="webserverurl" value="http://deviis.myCompany.net/release/" />
<parameter name="webserveruser" value="administrator" />
<parameter name="webserverpassword" value="myPassword" />
</parameters>
Then we define some varaibles in the bundle file:
<Variable Name="WebServerUrl"
Value="$(var.WebServerUrl)"></Variable>
<Variable Name="WebServerUser"
Value="$(var.WebServerUser)"></Variable>
<Variable Name="WebServerPassword"
Value="$(var.WebServerPassword)"></Variable>
At least we have to insert a few lines in the bootstrapper project file to read the properties from the config and set the bundle variables.
Here just one example: the web server url
<XmlPeek Namespaces="<Namespace Prefix='myNamespace' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>" XmlInputPath="..\AppConfig\appconfig.xml" Query="/parameters/parameter[#name='webserverurl']/#value">
<Output TaskParameter="Result" PropertyName="webserverurlResult" />
</XmlPeek>
<CreateProperty Value="$(webserverurlResult)">
<Output TaskParameter="Value" PropertyName="WebServerUrlProp" />
</CreateProperty>
<CreateProperty Value="$(DefineConstants);WebServerUrl=$(WebServerUrlProp)">
<Output TaskParameter="Value" PropertyName="DefineConstants" />
</CreateProperty>
At first we read the property with the XmlPeek-tag and put it into the property webserverurlResult.
Then we create the property WebServerUrlProp with the value webserverurlResult and put it into the bundle variable WebServerUrl.
Alternatively we don't have to create the property WebServerUrlProp.
A few lines less in our project file.
This would look like that:
<XmlPeek Namespaces="<Namespace Prefix='myNamespace' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>" XmlInputPath="..\AppConfig\appconfig.xml" Query="/parameters/parameter[#name='webserverurl']/#value">
<Output TaskParameter="Result" PropertyName="webserverurlResult" />
</XmlPeek>
<CreateProperty Value="$(DefineConstants);WebServerUrl=$(webserverurlResult)">
<Output TaskParameter="Value" PropertyName="DefineConstants" />
</CreateProperty>
Each time the bootstrapper project builds, it reads the properties from the config file and puts them into the bundle variables.
If you have a self-made UI, you can access the variable values like that:
UrlWebapi = Bootstrapper.Engine.StringVariables["WebServerUrl"];
UrlWebapi is in that case a property of type string.
I hope this helps.

Related

.Net Core msbuild build.proj equivilent of AssemblyInfo

I'm upgrading a project from AspNet Mvc 4 to AspNet Core Mvc 2.2; I am attempting to migrate the msbuild build.proj file to set the version and other attributes for the projects that create dll's; everything is working except the GenerateAssemblyInfo task. Is there a new way to do this in netcoreapp2.2?
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetRevisionInfo" BeforeTargets="Build">
<GitPendingChanges ContinueOnError="WarnAndContinue">
<Output TaskParameter="HasPendingChanges" PropertyName="HasPendingChanges" />
</GitPendingChanges>
<!--This will throw git Error 128 if there are no Tags -->
<GitDescribe SoftErrorMode="true" Lightweight="true" ContinueOnError="WarnAndContinue">
<Output TaskParameter="Tag" PropertyName="Tag" />
<Output TaskParameter="CommitCount" PropertyName="CommitCount" />
<Output TaskParameter="CommitHash" PropertyName="CommitHash" />
</GitDescribe>
<GitBranch ContinueOnError="WarnAndContinue">
<Output TaskParameter="Branch" PropertyName="Branch" />
</GitBranch>
<PropertyGroup>
<ShortCommitHash Condition="'$(CommitHash)' != ''">$(CommitHash.Substring(0,6))</ShortCommitHash>
<ReleaseType Condition="'$(CommitCount)' != '' AND '$(CommitCount)' != '0'">Beta</ReleaseType>
<ReleaseType Condition="'$(Branch)' != '' AND '$(Branch)' != 'master'">Alpha</ReleaseType>
</PropertyGroup>
</Target>
<!-- Error out if this is a release and our working copy has uncommitted changes -->
<Target Name="CheckRelease" AfterTargets="GetRevisionInfo" Condition="'$(Configuration)' == 'Release'">
<Error Text="Cannot build a Release Version when there are uncommitted changes, commit or revert all changes." Condition="'$(CommitHash)' != '' AND '$(HasPendingChanges)' == 'True'" />
</Target>
<!-- Generates AssemblyInfo file using Git Describe -->
<Target Name="GenerateAssemblyInfo" AfterTargets="CheckRelease" Condition="'$(CommitHash)' != ''">
<Time>
<Output TaskParameter="Year" PropertyName="Year" />
</Time>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs"
AssemblyProduct="$(MSBuildProjectName) $(ReleaseType)"
AssemblyCompany="xxx xxx xxx, LLC"
AssemblyCopyright="Copyright © $(Year) xxx xxx xxx, LLC. All rights reserved."
AssemblyConfiguration="$(Configuration)-$(Platform)"
AssemblyVersion="$(Tag).$(CommitCount)"
AssemblyFileVersion="$(Tag).$(CommitCount)"
AssemblyInformationalVersion ="$(Tag)-$(CommitCount)-$(ShortCommitHash) $(ReleaseType)"
AssemblyTitle="$(Tag)-$(CommitCount)-$(CommitHash)"/>
</Target>
<!-- copy framework files to libraries -->
<Target Name="CopyLibraries" Condition="'$(MSBuildProjectName)' == 'BaseApplication'" AfterTargets="Build">
<CreateItem Include="$(TargetDir)xxx.*">
<Output TaskParameter="Include" PropertyName="CopyFiles" />
</CreateItem>
<Copy SourceFiles="$(CopyFiles)" DestinationFolder="$(MSBuildProjectDirectory)\..\libraries\xxx\$(Configuration)"/>
</Target>
<Target Name="CopyKendoUI" AfterTargets="AfterBuild">
<ItemGroup>
<KendoFiles Include="
$(TargetDir)kendo.mvc.*;
$(TargetDir)\**\Kendo.Mvc.resources.*;" />
</ItemGroup>
<Copy SourceFiles="#(KendoFiles)" DestinationFolder="$(MSBuildProjectDirectory)\..\libraries\kendoui\$(Configuration)\%(RecursiveDir)"/>
</Target>
</Project>
There were a few things preventing this from working in Visual Studio 2019 using .Net Core;
The name of the target cannot be "GenerateAssemblyInfo", if this is the name of the target, it just gets ignored, no error, warning, or message, the target just doesn't run...
In the csproj file for the project that the msbuild project is imported in, you need to add false, I added this right below the
You have to manually create the Properties folder if you set the OutputFile to be in that folder.
Below is the working build.proj file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetRevisionInfo" BeforeTargets="Build">
<GitPendingChanges ContinueOnError="WarnAndContinue">
<Output TaskParameter="HasPendingChanges" PropertyName="HasPendingChanges" />
</GitPendingChanges>
<!--This will throw git Error 128 if there are no Tags -->
<GitDescribe SoftErrorMode="true" Lightweight="true" ContinueOnError="WarnAndContinue">
<Output TaskParameter="Tag" PropertyName="Tag" />
<Output TaskParameter="CommitCount" PropertyName="CommitCount" />
<Output TaskParameter="CommitHash" PropertyName="CommitHash" />
</GitDescribe>
<GitBranch ContinueOnError="WarnAndContinue">
<Output TaskParameter="Branch" PropertyName="Branch" />
</GitBranch>
<PropertyGroup>
<ShortCommitHash Condition="'$(CommitHash)' != ''">$(CommitHash.Substring(0,6))</ShortCommitHash>
<ReleaseType Condition="'$(CommitCount)' != '' AND '$(CommitCount)' != '0'">Beta</ReleaseType>
<ReleaseType Condition="'$(Branch)' != '' AND '$(Branch)' != 'master'">Alpha</ReleaseType>
</PropertyGroup>
</Target>
<!-- Error out if this is a release and our working copy has uncommitted changes -->
<Target Name="CheckRelease" AfterTargets="GetRevisionInfo" Condition="'$(Configuration)' == 'Release'">
<Error Text="Cannot build a Release Version when there are uncommitted changes, commit or revert all changes." Condition="'$(CommitHash)' != '' AND '$(HasPendingChanges)' == 'True'" />
</Target>
<!-- Generates AssemblyInfo file using Git Describe -->
<Target Name="GenerateAssemblyInfo" AfterTargets="CheckRelease" Condition="'$(CommitHash)' != ''">
<Time>
<Output TaskParameter="Year" PropertyName="Year" />
</Time>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs"
AssemblyProduct="$(MSBuildProjectName) $(ReleaseType)"
AssemblyCompany="xxx xxx xxx, LLC"
AssemblyCopyright="Copyright © $(Year) xxx xxx xxx, LLC. All rights reserved."
AssemblyConfiguration="$(Configuration)-$(Platform)"
AssemblyVersion="$(Tag).$(CommitCount)"
AssemblyFileVersion="$(Tag).$(CommitCount)"
AssemblyInformationalVersion ="$(Tag)-$(CommitCount)-$(ShortCommitHash) $(ReleaseType)"
AssemblyTitle="$(Tag)-$(CommitCount)-$(CommitHash)"/>
</Target>
<!-- copy framework files to libraries -->
<Target Name="CopyLibraries" Condition="'$(MSBuildProjectName)' == 'BaseApplication'" AfterTargets="Build">
<CreateItem Include="$(TargetDir)xxx.*">
<Output TaskParameter="Include" PropertyName="CopyFiles" />
</CreateItem>
<Copy SourceFiles="$(CopyFiles)" DestinationFolder="$(MSBuildProjectDirectory)\..\libraries\xxx\$(Configuration)"/>
</Target>
<Target Name="CopyKendoUI" AfterTargets="AfterBuild">
<ItemGroup>
<KendoFiles Include="
$(TargetDir)kendo.mvc.*;
$(TargetDir)\**\Kendo.Mvc.resources.*;" />
</ItemGroup>
<Copy SourceFiles="#(KendoFiles)" DestinationFolder="$(MSBuildProjectDirectory)\..\libraries\kendoui\$(Configuration)\%(RecursiveDir)"/>
</Target>
</Project>
And the MyProject.csproj file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSBuildTasks" Version="1.5.0.235">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http.Abstractions">
<HintPath>C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.http.abstractions\2.2.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
<Import Project="../.build/build.proj" />
</Project

Change assembly name for build settings in .csproj

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!

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>

Condition using File::Exists not working

I currently create my first MSBuild-script.
I've a tag "Folders" that findes all Directories in a given root path:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<PropertyGroup>
<RootFolder>tmp</RootFolder>
</PropertyGroup>
<ItemGroup>
<Folders Include="$([System.IO.Directory]::GetDirectories("$(RootFolder)"))"/>
</ItemGroup>
<Message Text="#(Folders -> '%(FullPath)\Bin\Debug\%(Filename)%(Extension).dll', ';')"/>
</Target>
</Project>
That works perfect.
My problem is that I only need directories where the specified file exists.
I tried a condition like that
Condition="$([System.IO.File]::Exists("%(FullPath)\\Bin\\Debug\\%(Filename)%(Extension).dll"))"
for the folder tag.
This script runs without any error but my list is empty.
Why?
Are there any other solutions to check for a file?
I used this solution because it uses C# and I'm a C#-developer.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<PropertyGroup>
<RootFolders>tmp</RootFolders>
</PropertyGroup>
<GetFiles rootFolders="$(RootFolders)">
<Output PropertyName="Files" TaskParameter="Files" />
</GetFiles>
<Message Text="$(Files)" />
</Target>
<UsingTask
TaskName="GetFiles"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<rootFolders ParameterType="System.String" Required="true" />
<files ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Linq" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Func<string, string> BuildFilePath = path => path + #"\Bin\Debug\" + Path.GetFileName(path) + ".dll";
var dirs = Directory.GetDirectories(rootFolders).Where(x => File.Exists(BuildFilePath(x)));
files = string.Join("\n", dirs.Select(BuildFilePath));
]]>
</Code>
</Task>
</UsingTask>
</Project>
AFAIK, the thing is Condition is executed and checked for the whole declaration of Items (i.e. <Folders ..> tag).
I think, you need to loop through the collection (e.g. using target/task batching) and check the file to exist in every single folder folder in the collection. Then if the file exists - include it in the new <FoldersFiletered> items collection.
NB: I don't have time to test the code now, but this is the idea roughly:
<Target Name="FilterFolders"
Inputs="#(Folders)"
Outputs="%(FullPath)">
<ItemGroup>
<FoldersFiltered Include="#(Folders->'%(FullPath)')"
Condition="$([System.IO.File]::Exists("#(Folders->'%(FullPath)'\\Bin\\Debug\\YourFile.dll"))" />
</ItemGroup>
</Target>

Fetching asmv1:assemblyIdentity from manifest

After compilation the following is present in the app manifest, if I publish it by hand in VS the version picked is the first one, i.e. 3.9.0.3
<asmv1:assemblyIdentity name="MyApp.exe" version="3.9.0.3" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="x86" type="win32" />
<description asmv2:iconFile="Logo.ico" xmlns="urn:schemas-microsoft-com:asm.v1" />
<application />
<entryPoint>
<assemblyIdentity name="MyApp" version="3.8.0.25806" language="neutral" processorArchitecture="x86" />
If instead I try running it in a MSBuild script and use
<Target Name="GetVersion">
<Message Text="Getting version info..."/>
<GetAssemblyIdentity AssemblyFiles="#(GetVersionAssembly)">
<Output TaskParameter="Assemblies"
ItemName="GetVersionAssemblyInfo"/>
</GetAssemblyIdentity>
<Message Text="%(GetVersionAssemblyInfo.Version)..."/>
</Target>
Where #(GetVersionAssembly) is the path to the executable, the 3.8.0.25806 version is picked, how do I get the published version rather than assembly version extracted in MSBuild, to make it match the manual publish?
What I ended up doing was use the MSBuild.Community.Tasks library and in the GetVersion target add below line, to allow the published version to be stored in the version.txt file. Not sure if there is a way of accessing the publish number within Visual Studio and increment that one when publishing with msbuild, which would prevent having to manually update these versions if you switch between the two ways of publishing. I would characterize this as a half solution since it still involve a manual step.
<Version VersionFile="version.txt" RevisionType="Increment"> <Output TaskParameter="Major" PropertyName="Major" /> <Output TaskParameter="Minor" PropertyName="Minor" /> <Output TaskParameter="Build" PropertyName="Build" /> <Output TaskParameter="Revision" PropertyName="Revision" /> </Version>
Within the script I then changed all %(GetVersionAssemblyInfo.Version) references to $(Major).$(Minor).$(Build).$(Revision)

Categories