Msbuild task to find specific filetype - visual-studio

How can we find a specific file type under a path? I have checked MSBuild Task Reference but couldn't figure out.
Looking for something as:
<FindUnderPath Path="$(OutDir)" Files="*.txt">
<Output TaskParameter="InPath" ItemName="AllTxtFiles"/>
</FindUnderPath>
But it fails sayings "MSB3541: Files has invalid value "*.txt""
P.S. I am a rookie at msbuild tasks!

If you just need list of all txt files in certain folder you can get it as simple as
<ItemGroup>
<AllTxtFiles Include="$(PathToFolder)\**\*.txt" />
</ItemGroup>
Double stars (**) means that folder should be searched recursively for file pattern

You could use an ItemGroup to specify such files and reference the ItemGroup in the Files parameter. Something like:
<ItemGroup>
<MyFiles Include="*.txt" />
</ItemGroup>
<FindUnderPath Path="$(OutDir)" Files="#(MyFiles)">
<Output TaskParameter="InPath" ItemName="AllTxtFiles" />
</FindUnderPath>
Source: http://msdn.microsoft.com/en-us/library/vstudio/ms164293(v=vs.120).aspx

Related

How to include an <ItemGroup> across multiple .csproj files

Within Visual Studio (2019 in this case) solution, is there a way to specify an <ItemGroup> in a location that multiple .csproj files can use it within the solution?
As an example, this ItemGroup is used in multiple .csproj files within Test.sln. Instead of having to add this ItemGroup to each .csproj file, I'd like to somehow place it in a common file and reference it from the .csproj file. Is that possible?
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdk)" />
</ItemGroup>
You can use a Directory.Build.props file in the directory hierarchy (e.g. next to the .sln file) containing only these common things:
<Project>
<ItemGroup>
...
</ItemGroup>
</Project>

What is the value of MSBuildThisFileDirectory?

If I have a project structure like this:
\MySolution
\MyProject
ReadMe.md
\build
MyProject.targets
What would the value of $(MSBuildThisFileDirectory) be when used in the MyProject.targets file?
Assuming my solution folder is in the root of C: drive, would it be?..
c:\MySolution\MyProject\build\
In the MyProject.targets file, how would I reference the ReadMe.md file using the $(MSBuildThisFileDirectory)?
Additional information:
MyProject.targets looks like:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\xxx\ReadMe.md">
<Link>FrameworkTests.feature</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CustomToolNamespace></CustomToolNamespace>
</None>
</ItemGroup>
</Project>
What is the value of MSBuildThisFileDirectory?
It depends on your MyProject.targets. According to the literal meaning of this variable, you could to know ThisFileDirectory means "This File Directory".
Since you have use this argument in the file MyProject.targets, the path should be related to the location of the "this file" MyProject.targets. So the value of this argument should be the directory of this file MyProject.targets.
After install the nuget, the file MyProject.targets should be added to the path:
c:\MySolution\packages\MyProject.1.0.0<YouPackagefolder>\build
You can use a target to output that value in your project file, to accomplish this, unload your project. Then at the very end of the project, just before the end-tag </project>, place below scripts:
<Target Name="TestValue" AfterTargets="build">
<Message Text="#(none)">
</Message>
</Target>

Make visual studio build when output won't change?

In my project I have a json file I use for configuration that I have git set to ignore. When the repository is first cloned, the configuration file that is part of the project and that is copied to the output directory doesn't exist. I've gotten this to work using tasks in the 'BeforeBuild' target in the project that will copy the sample file to the actual config file if it doesn't exist.
<Target Name="BeforeBuild">
<ItemGroup>
<MySourceFiles Include="Configuration.sample.json" />
</ItemGroup>
<ItemGroup>
<MyDestinationFiles Include="Configuration.json" />
</ItemGroup>
<Message Importance="high" Condition="!Exists('#(MyDestinationFiles)')"
Text="Copying #(MySourceFiles) to #(MyDestinationFiles)" />
<Copy Condition="!Exists('#(MyDestinationFiles)')"
SourceFiles="#(MySourceFiles)"
DestinationFiles="#(MyDestinationFiles)" />
</Target>
So if I build the project, then delete the configuration file and do a build, nothing happens because no changes have been made that would change the outputs I think. Is there a way to change the project file so that a build will be flagged as necessary? It shouldn't come up very often and I can always do a 'Clean' or 'Rebuild' manually, but it's nagging at me since I'm just starting to learn MSBuild files.
From the documentation on a Target's Outputs attribute:
The files that form outputs into this target. Multiple files are
separated by semicolons. The timestamps of the files will be compared
with the timestamps of files in Inputs to determine whether the Target
is up to date
So if you add the paths to the outputfiles created by your Beforebuild target to it's Outputs attribute, at the start of every build msbuild will check if those files exist and if not it will start a build because now the project is considered to not be up-to-date anymore. In practice use:
<Target Name="BeforeBuild" Outputs="#(MyDestinationFiles)">

How to add a command line code generator to Visual Studio?

I'm working on a project that uses code generation to generate C# classes using a command line tool from a text-based description. We are going to start using these descriptions for javascript too.
Currently these classes are generated and then checked in, however, I would like to be able to make the code generate automatically so that any changes are propagated to both builds.
The step that is run manually is:
servicegen.exe -i:MyService.txt -o:MyService.cs
When I build I want MSBuild/VS to first generate the CS file then compile it. It is possible to do this using, by modifying the csproj, perhaps using a MSBuild Task with Exec, DependentUpon & AutoGen?
Normally I would recommend a pre-build command be placed in a pre-build event, but since your command line tool will be creating C# classes needed for compiling, this should be done in the BeforeBuild target in the .csproj file. The reason for this is because MSBuild looks for the files it needs to compile between the time BeforeBuild is called and the time when PreBuildEvent is called in the overall process (you can see this flow in the Microsoft.Common.targets file used by MSBuild).
Call the Exec task from within the BeforeBuild target to generate the files:
<Target Name="BeforeBuild">
<Exec Command="servicegen.exe -i:MyService.txt -o:MyService.cs" />
</Target>
See the Exec task MSDN documentation for more details about specifying different options for the Exec task.
Antlr has an example of a process that can be used to add generated code to a project. This has the advantage of showing the files that are generated nested under the source file, although it is more complex to add.
You need add an item group with the file to be generated from, for example:
<ItemGroup>
<ServiceDescription Include="MyService.txt"/>
</ItemGroup>
Then add the cs file to be generated to the ItemGroup containing the rest of the source code.
<ItemGroup>
...
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
...etc..
<Compile Include="MyService.txt.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>MyService.txt</DependentUpon> <!--note: this should be the file name of the source file, not the path-->
</Compile>
</ItemGroup>
And then finally add the build target to execute the code generation (using % to execute the command for each item in the ItemGroup). This could be put into a separate file, so that it can be included from many projects.
<Target Name="GenerateService">
<Exec Command="servicegen.exe -i:%(ServiceDescription.Identity) -o:%(ServiceDescription.Identity).cs" />
</Target>
<PropertyGroup>
<BuildDependsOn>GenerateService;$(BuildDependsOn)</BuildDependsOn>
</PropertyGroup>

Using WriteCodeFragment MSBuild Task

I am trying to use the WriteCodeFragment MSBuild task to create an AssemblyVersion attribute. I'm having a problem creating a property group to correctly pass the ITaskItem array required for processing. Can someone help with an example?
This creates a file called BuildVersion.cs with an AssemblyVersion attribute of 123.123.123.123. If OutputFile is removed then a randomly generated file name will be used instead. The Compile item name automatically adds the item to the Compile items (includes BuildVersion.cs in the build). The FileWrites item name allows the file to be removed during Clean.
<Target Name="BeforeBuild">
<ItemGroup>
<AssemblyAttributes Include="AssemblyVersion">
<_Parameter1>123.123.123.123</_Parameter1>
</AssemblyAttributes>
</ItemGroup>
<WriteCodeFragment AssemblyAttributes="#(AssemblyAttributes)"
Language="C#"
OutputDirectory="$(IntermediateOutputPath)"
OutputFile="BuildVersion.cs">
<Output TaskParameter="OutputFile" ItemName="Compile" />
<Output TaskParameter="OutputFile" ItemName="FileWrites" />
</WriteCodeFragment>
</Target>

Resources