Msbuild - nesting a property inside a property - visual-studio-2010

I currently have something like this
I would like to display the output of the property helloworld by joining the value of properties value1 and value2.
I tried doing $(value1$(value2)) but that doesnt work any suggestions ?
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<value1>hello</value1>
<value2>world</value2>
<helloworld>This works</helloworld>
</PropertyGroup>
<Target Name="Build">
<Message Text = "----Message is: $(value1)"></Message>
<Message Text = "----Message is: $(value1$(value2))"></Message> --->Error :Should display `This works`
</Target>
</Project>

Properties don't 'nest'. Actually what you are trying to do, is dynamically generate a property name and then lookup the value for that property name. That is not supported in MSBuild. You can do this in some languages (for example if the language has an eval() function), but not MSBuild.
Property functions can be nested and properties can be used for parameters to property functions.
e.g.
<PropertyGroup>
<File>test.proj</File>
<Archive>$([MSBuild]::BitwiseAnd(32, $([System.IO.File]::GetAttributes($(File)))))</Archive>
</PropertyGroup>
The value of the $(File) property is passed to GetAttributes().
The return value of GetAttributes() is passed to BitwiseAnd().

Related

How do I use a Property defined in a wixproj file inside a component

I have declared a property inside a PropertyGroup in a wixproj file.
<PropertyGroup>
<Permit>false</Permit>
</PropertyGroup>
I use this property as a Condition to run several commands BeforeBuild and AfterBuild
<Exec Condition=" '$(Permit)' == 'true' " Command="--command here--" />
In my Product.wxs file, I have defined some components that I want to include or exclude based on the defined Permit property.
<ComponentGroup Id="ProductComponents">
<ComponentRef Id="SomeComponent"/>
<!--Component to add based on the property-->
<?if $(Permit)=true ?>
<ComponentRef Id="PermissionComponent"/>
<?endif?>
</ComponentGroup>
Anytime I build the project, I get this error.
Ill-formed preprocessor variable '$(Permit)'. Variables must have a
prefix (like 'var.', 'env.', or 'sys.') and a name at least 1
character long. If the literal string '$(Cache)' is desired, use
'$$(Cache)'.
I have already tried all the specified prefix but it still doesn't work.
Is there something I am not doing right?
You can add your variable to <DefineConstants> wix setting in the wixproj file. Like this (there may be already other constants defined, you probably want to keep those. Also watch out for configurations, you may need to add it for release/debug/etc):
<PropertyGroup>
<Permit>false</Permit>
</PropertyGroup>
...
<DefineConstants>Debug;Permit=$(Permit)</DefineConstants>
And then just use:
<?if $(var.Permit)=true ?>

How do you specify common package versions in a common.props file?

We've got a simple common.props file that is basically doing what Directory.Build.props appears to do. It specifies various settings and is then imported to the csproj files that need it.
Here's the basics of the MyProject.common.props file:
<Project>
<PropertyGroup>
// some settings
</PropertyGroup>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
//some more settings...
<MyNewVariable>3.1.22</MyNewVariable>
</PropertyGroup>
</Project>
Here's the basics of the csproj:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\folderA\MyProject.common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MyNewVariable)" />
</ItemGroup>
</Project>
When I use this approach I get the error:
Project dependency Microsoft.Extensions.Hosting.WindowsServices does
not contain an inclusive lower bound. Include a lower bound in the
dependency version to ensure consistent restore results.
However, if I just input 3.1.22 where the variable is it works fine. This suggests to me that the variable is coming through empty or not being picked up. If I remove the variable altogether and leave the Version blank I get the same error. I also double checked the path to the file by misspelling the import and I get a different error, so I know it can at least find the common.props file.

How to execute custom exe in MSBuild to set environment variable during property evaluation phase

I am trying to import some props and targets file from a directory, which is a user-specified location. In order to determine this directory, I have some custom logic and I am trying to find a way to execute this in one of the props. I am unable to use the standard MSBUILD property functions to achieve this.
My end goal is to have a small UI that allows the user to specify this directory and the scope of this directory must be tied to a project i.e, there could be two different projects pointing to two different directory within a solution
What about calling your build script again after you have fiddled out all your requirements? Every call to the MSBuild task where you put $(MSBuildProjectFile) as your Projects value will call your build file again and that call will have a new copy of the environment.
<Project DefaultTargets="EntryPoint" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<Target Name="SetProperties">
<PropertyGroup>
<PropA>Something</PropA>
</PropertyGroup>
</Target>
<Target Name="EntryPoint">
<MSBuild
Projects="$(MSBuildProjectFile)"
Properties="Configuration=$(Configuration);OutputPath=$(OutputPath);"
Targets="SetProperties;EntryPoint"
Condition="$(PropA) == ''"/>
<Message
Text="Everything is set!"
Condition="$(PropA) != ''"/>
</Target>
</Project>
This setup can be easily extended to execute some Exec task logic and forwards that output to a new call to the entry point.

Check if PropertyGroup item is set to a value in .csproj

I am using .targets files to include common functionality in .csproj files.
In the target file, I want to check if a property was already set before, and if yes, do not set it again.
I need this because I am using the specific target file in many solutions, and I want to include the custum Property only if it wasn't set before.
The property I am talking about is
<PropertyGroup>
<CodeAnalysisRuleSet>$(SolutionDir)CustomizedAllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
If this ruleset was specified before Importing the target file, I don't want to include it again in the .targets file.
How do I check in .csproj if the <CodeAnalysisRuleSet>...</CodeAnalysisRuleSet> was set before?
The pattern I've seen most often is to set it conditionally based on comparing it to an empty value:
<PropertyGroup>
<CodeAnalysisRuleSet Condition="'$(CodeAnalysisRuleSet)' == ''">$(SolutionDir)CustomizedAllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
[Edit: responding to comment with code example]
Here's a longer example that works for me:
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PropertyValue Condition="$(PropertyValue) == ''">Default value</PropertyValue>
</PropertyGroup>
<Target Name="Build">
<Message Text="$(PropertyValue)" />
</Target>
</Project>
Gives the console output:
Project "D:\temp\test.proj" on node 1 (default targets).
Build:
Default value
Done Building Project "D:\temp\test.proj" (default targets).

How to calculate MSBuild property before import

Consider these two MSBuild files. First build-script.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- build-script.xml -->
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputName>OutputName</OutputName>
</PropertyGroup>
<Import Project="build-script-to-import.xml"/>
</Project>
and then build-script-to-import.xml:
<?xml version="1.0" encoding="utf-8"?>
<!-- build-script-to-import.xml -->
<Project
ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>Path\$(OutputName)</OutputPath>
</PropertyGroup>
<Target Name="Build">
<Message Text="$(OutputPath)" />
</Target>
</Project>
These two files represents a very simplified C# project file (build-script.xml) referencing a common targets file (build-script-to-import.xml). That means that it is not an option to change the build-script-to-import.xml file. The problem is that the OutputName is calculated, and I need to set it to this calculated value before I import the build-script-to-import.xml file. Do you know how I can accomplish this?
NOTE 1: The calculated value is in fact an assembly version which I am able to fetch from an assembly file with the GetAssemblyIdentity MSBuild task. The problem as I see it is that I may not call a target before the import happens. I would like to set the value for property OutputName with the help of the GetAssemblyIdentity MSBuild task.
NOTE 2: The build will be triggered by Visual Studio, so I may not use a batch file and send a property to MSBuild as a command line argument.
Move the declaration of $(OutputPath) inside your "Build" target. The only way to use "calculated" properties is to have them evaluated inside a target. Globally declared "static" properties will always be evaluated before any target runs, and presumably you need to run a target to calculate the value of $(OutputName). Just be sure your name calculating target runs before the "Build" target.
If Corresponding targets from imported project allows to modify DependsOnTargets
<Target Name="Build" DependsOnTargets="#(BuildDependsOn)" />
you can modify Item and add your target wich will prepare needed properties.
If it is not possible you can write wrapper to call underlying target/targets (Build/Rebuild/Clean) by yourself (without importing build-script-to-import.xml):
build-script.xml
<Target Name="Build" DependsOn="PrepareProperties">
<MSBuild Projects="build-script-to-import.xml"
Targets="Build"
Properties="OutputName=$(OutputName);$(OtherProperties)"/>
</Target>
Suggestion
Depends on your requirement to dynamically change OutputName there may be mach simpler solution. Just not to do it. It is rather simpler to write your own custom build step to copy from fixed output directory to your destination wich is calculated using your rules.
For example, I prefer practice when I have directory called latest with fixed path and a directory for each version of product. Every one can take latest version from that folder.

Resources