MSBuild Not Resolving Parameters inside Path - c#

i have to run text transform file using ms build so i am using Transform as target. In order to run it in my project i have to pass some path to fetch resource dlls. so my command is
D:\WorkSpace\Projects\Client\Dev\Project\Lw.Domain.Project>msbuild /tv:15.0 /t:transform /p:TransformFile="ProjectEntityModelGenerator.tt" /p:SolutionDir=".." /p:FrameworkVersion=4 /p:MetaModelFolder="$(SolutionDir)\packages\LW.Framework$(FrameworkVersion)" /p:BaseResourceFolder="$(SolutionDir)\packages\LW.Framework$(FrameworkVersion)" /p:FrameworkReference="package" /p:IsFromMSBuild="true"
But it is not resolving the path, so i am getting issue like below,
C:\Program Files (x86)\Microsoft Visual
Studio\2017\Professional\MSBuild\Microsoft\VisualStudio\v15.0\TextTemplating\Microsoft.TextTemplating.targets(340,5):
error : System.IO.FileNotFoundException: Could not find a part of the
path
'D:\WorkSpace\Projects\Client\Dev\Project\Lw.Domain.Project\$(SolutionDir)\packages\LW.Framework$(FrameworkVersion)Lw.System.Metamodel.dll'.
[D:\WorkSpace\Projects\Client\Dev\Project\Lw.Domain.Project\Lw.Domain.Project.csproj]
Am I missing something in my csproj file? Below is my csproj file configuration,
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<!-- Get the Visual Studio version – defaults to 10: -->
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<!-- Keep the next element all on one line: -->
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<SolutionDir>$(SolutionDir)</SolutionDir>
<FrameworkVersion>$(FrameworkVersion)</FrameworkVersion>
<MetaModelFolder>$(SolutionDir)\packages\LW.Framework$(FrameworkVersion)\</MetaModelFolder>
<BaseResourceFolder>$(SolutionDir)\packages\LW.Framework$(FrameworkVersion)\</BaseResourceFolder>
<IsFromMSBuild>$(IsFromMSBuild)</IsFromMSBuild>
<ComputedSolutionDir>$([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(ProjectDir),"..\\"))))</ComputedSolutionDir>
<DomainResourceFolder>$(SolutionDir)..\Product\Lw.Domain\bin\$(Configuration)\</DomainResourceFolder>
</PropertyGroup>
<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" Condition="'$(VisualStudioVersion)' == '15.0'" />

I tested on my local project, and found that Properties cannot be passed in command line using the same syntax "$()" in csproj file.
You need pass to the command line the exact physical path for the BaseResourceFolder. And from below error. You missed a "\" at the end of the value for BaseResourceFolder too.
'D:\WorkSpace\Projects\Client\Dev\Project\Lw.Domain.Project\$(SolutionDir)\packages\LW.Framework$(FrameworkVersion)Lw.System.Metamodel.dll'.
/p:BaseResourceFolder="(phsical path to BaseResourceFolder)\"
Hope you find above helpful.

Related

How do I run an MSBuild task before compilation starts, but after intermediary files are generated?

Background: StyleCop is complaining that an auto-generated file has poor formatting, leading to many warnings when I try to build my project. The auto-generated file is in the obj/ directory of my project, and I want to create an MSBuild task that prepends // <auto-generated/> to this file before compilation (but after it is generated) so that StyleCop doesn't complain.
Problem: I have the following MSBuild code
<!-- StyleCop complains about a file that's auto-generated by the designer,
so we need to prepend 'auto-generated' to it beforehand. -->
<Target Name="BeforeCompile" DependsOnTargets="MarkGeneratedFiles" />
<Target Name="MarkGeneratedFiles">
<PropertyGroup>
<GeneratedFilePath>$(MSBuildThisFileDirectory)obj\$(Configuration)\$(TargetFramework)\$(MSBuildProjectName).Program.cs</GeneratedFilePath>
</PropertyGroup>
<InsertIntoFile FilePath="$(GeneratedFilePath)" LineNumber="1" Text="// <auto-generated/>" />
</Target>
<!-- Code taken from http://stackoverflow.com/a/21500030/4077294 -->
<UsingTask
TaskName="InsertIntoFile"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<FilePath ParameterType="System.String" Required="true" />
<LineNumber ParameterType="System.Int32" Required="true" />
<Text ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Code Type="Fragment" Language="cs">
<![CDATA[
// By tradition, text file line numbering is 1-based
var lines = File.Exists(FilePath)
? File.ReadAllLines(FilePath).ToList()
: new List<String>(1);
lines.Insert(Math.Min(LineNumber - 1, lines.Count), Text);
File.WriteAllLines(FilePath, lines);
return true;
]]>
</Code>
</Task>
</UsingTask>
The file that I want to modify has the filename obj/Debug/netcoreapp1.0/BasicCompiler.Tests.Program.cs. In the above snippet, I have a BeforeCompile target that depends on MarkGeneratedFiles, which goes ahead and tries to insert // <auto-generated/> before the first line of that file.
I have tested, and this seems to work fine if the generated file is already present. However, if I remove the obj/ directory or I build from another machine, I get this error:
"C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj" (default target) (1) ->
(MarkGeneratedFiles target) ->
C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj(68,5): error MSB4018: The "InsertIntoFile" task failed unexpectedly.\r
C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\BasicCompiler.Tests.csproj(68,5): error MSB4018: System.IO.DirectoryNotFoundException: Could not find
a part of the path 'C:\cygwin64\home\james\Code\cs\BasicCompiler\src\BasicCompiler.Tests\obj\Debug\netcoreapp1.0\BasicCompiler.Tests.Program.cs'.\r
Basically it seems like the target is getting run before the file is getting generated, so there's nothing to prepend the text to. Is there a way to run it after this file gets generated, but before compilation?
Additional notes: So far, I have looked through all of the special target names here and tried using both BeforeBuild and BeforeCompile.
Also, since I am using the "new" StyleCop, I cannot put <ExcludeFromStyleCop> in my project file. See https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1145
I managed to work around this; see #stijn's super-helpful comment here.
First, I ran msbuild /v:detailed from the command line. This increases the verbosity of MSBuild so that it gives you a more detailed overview of what's going on, e.g. you can see the name of each target that's being run.
I searched through the log for the name of the target that was generating the file. In my case, it turned out to be GenerateProgramFiles.
I marked my custom target with AfterTargets="GenerateProgramFiles" so it ran after the file was generated.

How to set a configuration parameter using service messages in the project file

Description
TeamCity side: I defined a Configuration Parameter named major.minor.patch with an empty value. This can be accessed in TeamCity build steps as %major.minor.patch%
Visual Studio side:
In my .csproj file I added the code below to set major.minor.patch to the current version of my assembly:
<PropertyGroup Condition="'$(TEAMCITY_BUILD_PROPERTIES_FILE)' == ''">
<TeamCityBuild>true</TeamCityBuild>
</PropertyGroup>
<!-- Other stuff... -->
<Target Name="TeamCity" AfterTargets="Build" Condition="'$(TeamCityBuild)' == 'true'">
<GetAssemblyIdentity AssemblyFiles="obj\$(ConfigurationName)\$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="AssemblyItentity"/>
</GetAssemblyIdentity>
<Message Text="##teamcity[setParameter name='major.minor.patch' value='%(AssemblyIdentity.Version)'"/>
Problem
However, this code seems not working since the Parameter is still empty.
I also tried defining my parameter as an environment variable: env.maor.minor.patch, still no chance.
Question
How can I do this?
How to set a Configuration Parameter in TeamCity to my assembly version in my C# project?
Try setting the Importance of the Message to High
Message Text="##teamcity[setParameter name='major.minor.patch' value='%(AssemblyIdentity.Version)'" Importance="high" />

How to manually evaluate msbuild condition?

I'm creating a custom msbuild task that will be processing a configuration from custom XML file. I want to allow to use Condition attribute in that xml file. Syntax of that attribute should be the same as MSBuild Conditions (https://msdn.microsoft.com/en-us/library/7szfhaft.aspx)
How can I evaluate value of that attribute? Is there an existing library that automate that or I'm forced to write my own parser?
So far I was able only to get value of all variables that probably will be necessary to evaluate that conditions (How to access the MSBuild 's properties list when coding a custom task?)
I’m not sure if this will be helpful for you. I was solving the similar problem c++ projects. I was thinking to use Microsoft.Build.BuildEngine.Project class but later changed my mind. Finally I’ve created mine config in msbuild style (including namespace). I’ve enforced importing my config by msbuild (I’ve misused ForceImportAfterCppTargets property). Msbuild evaluated everything for me. Mine injected config (or props/target file) contained target that was injected into build process by overriding some build property (at the project level) in a way my target was called. Mine custom target called mine custom task with passed all necessary properties and items by parameters.
Following content is response on Uriel Jun 12 at 16:26:
Because you've marked question with tag c# I tried to make sample with C# vs 2010.
I made sample really simple. I put task and xml configuration file into one file named my.props. My custom task just prints values of my configuration provided by item. It prints metadata of item.
One think you have to do is to manually modify your .csproj by adding one simple line. After the line where is Microsoft.CSharp.targets imported add import of custom my.props file.
This sample expects your my.props is in the same directory as .csproj.
Diff style change:
+
Content of my.props:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="MyTool" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<Cfg ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
if (Cfg.Length > 0)
{
for (int i = 0; i < Cfg.Length; ++i)
{
ITaskItem item = Cfg[i];
string value1 = item.GetMetadata("Value1");
string value2 = item.GetMetadata("Value2");
Log.LogMessage(MessageImportance.High, "MyTool: {0} - {1}", value1, value2);
}
}
]]>
</Code>
</Task>
</UsingTask>
<ItemDefinitionGroup Condition=" '$(Configuration)' == 'Release' ">
<MyConfig>
<Value1>Hello</Value1>
<Value2>World</Value2>
</MyConfig>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Configuration)' == 'Debug' ">
<MyConfig>
<Value1>Hello</Value1>
<Value2>Debug world</Value2>
</MyConfig>
</ItemDefinitionGroup>
<ItemGroup>
<MyConfig Include="MyCfg" />
</ItemGroup>
<Target Name="AfterBuild">
<MyTool Cfg="#(MyConfig)" />
</Target>
</Project>

Remove stylecop msbuild integration

I have a solution with about 80 projects in it. Each project has StyleCop MSBuild integration enabled:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.4\Microsoft.StyleCop.targets" />
I need to remove this from ALL projects. is there any way short of checking out and hand-editing each and every .csproj file?
The StyleCop target, as defined in Microsoft.StyleCop.targets is conditional on property StyleCopEnabled.
Here is a snippet from Microsoft.StyleCop.targets:
<!-- Define target: StyleCop -->
<Target Name="StyleCop" Condition="'$(StyleCopEnabled)' != 'false'">
<Message Text="Forcing full StyleCop reanalysis." Condition="'$(StyleCopForceFullAnalysis)' == 'true'" Importance="Low" />
... snip ...
The quick way to disable stylecop is to pass global setting for StyleCopEnabled. E.g. if you are building from command line, the command would be: msbuild MyProject.proj /p:StyleCopEnabled=false
I had a similiar problem, still manual editing, but this worked for me. Opened all of the .csproj file in notepad by searching in the directory of the projects, then selecting all and "Edit with Notepad++". Then do a Find-Replace with "Find All in All Open Documents" to remove the import line:
<Import Project="$(ProjectDir)\..\StyleCop.targets" Condition=" '$(OS)' == 'Windows_NT' " />

Detect the user triggering the build in VS2012 using MSBuild

I am using VS2012, I have a target but i only want it to execute it when it equals a particular value. Now the value i would like to use is the "requested by" description in TFS 2010 which indicates the user that raised the build.
But i do not know yet how to detect the user who raised the build in the MSBuild framework.
Does anyone know how i could go about implementing this? I have a looked in the Well Known Metadata of MSBuild and it doesn't contain the metadata i would like.
Below is my target:
<Target Name="AfterBuild" Condition="Release">
<Message Text="..RUNNING TESTS.." />
<PropertyGroup>
<TestSuccessOrNot>1</TestSuccessOrNot>
</PropertyGroup>
<Exec Command='"C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\MSTest.exe" /testcontainer:C:\\Tests\\MyUnitTests.dll /test:T5278 /detail:testname '>
<Output TaskParameter="ExitCode" PropertyName="TestSuccessOrNot" />
</Exec>
<Error Condition="$(TestSuccessOrNot) == 1" Text="Unit tests fail!" />
Ideally i would like to pass that value(user that raised the build) into the Condition attribute of my target.
Any ideas?
Kind Regards,
The properties are RequestedBy and RequestedFor, but not sure when they differ. The condition should look like Condition=" '$(RequestedBy)' == 'anyUserName'" For users this will be the display name, while for triggered builds it will be [CollectionName]\Project Collection Service Accounts

Categories