Using WriteCodeFragment MSBuild Task - visual-studio

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>

Related

MSBuild or NAnt script that generates C# files, adds them to a project and compiles the project

I've just written a code generator app for FluentMigrator API that emits an unknown number of C# class files. I wish to compile the code generator, run it to emit the C# classes then add the new C# files to an existing C# project and then compile final solution.
What's the best approach for adding the code generated C# files to a project?
Given what we know from your post, there could be a couple approaches.
You have an API that will generate some class files and want to integrate this into your build process so you would make an API call to generate new class files, then incorporate those class files into your build.
If your executable emits output files into it's current working directory, you could use an Exec task to run your command in the $(IntermeidateOutputPath) so as to not clutter up your project's source tree:
<Exec Command="MyExe.exe " WorkingDirectory="$(IntermediateOutputPath)\AutoGenClasses\" />
Following this command, you could append those output classes into the default compile group:
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)\AutoGenClasses\**\*.cs" />
</ItemGroup>
Now you'd probably want to control when this occurs, so you'd embed this code into a separate <Target /> and schedule it to occur before the build occurs.
<Target Name="AutoGenClasses" BeforeTargets="Compile">
<Message Text="Starting the AutoGenClasses task..." Importance="high" />
<Exec Command="MyExe.exe " WorkingDirectory="$(IntermediateOutputPath)\AutoGenClasses\" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)\AutoGenClasses\**\*.cs" />
</ItemGroup>
<Message Text="... completed the AutoGenClasses." Importance="high" />
</Target>

Is it possible to get Product Version in post build event?

I wish to copy MyDir (in my $ProjectDir) to $OutDir{ProductVersion} upon completion of a build.
Using the following configuration in csproj file I am able to get the File Version.
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<Exec Command="robocopy $(ProjectDir)MyDir $(OutDir)/%(AssemblyVersion.Version) /E" IgnoreExitCode="true" />
</Target>
This however, retrieves the FileVersion and not the ProductVersion. Is there any way I can obtain the ProductVersion in post build event?
You're looking for Read AssemblyFileVersion from AssemblyInfo post-compile. You're going to need a custom task for this, since GetAssemblyIdentity doesn't return the productversion.
The linked question has the answer for AssemblyFileVersion, it shouldn't be too hard to adapt it to make it return the ProductVersion.

Msbuild task to find specific filetype

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

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>

msbuild custom task

I have a custom MSBuild task that takes in a set of JavaScript files, minifies them, and outputs them, with the extension .min.js. When I do a normal build through Visual Studio, it works perfectly and the .min.js files are output to the same directory as the original files. When I try to deploy using the Publish feature in Visual Studio, only the original .js files make it to the publish directory.... How can I get the output of my task to be counted as "content" so that it will end up in the published folder?
EDIT:
I was able to figure it out by adding the Output tag inside my task and then creating an ItemGroup around that:
<Target Name="AfterBuild">
<ItemGroup>
<JavaScriptFiles Include="Scripts\*.js" Exclude="Scripts\*.min.js" />
</ItemGroup>
<JsCompress Files="#(JavaScriptFiles)" OutputPath="Scripts">
<Output TaskParameter="CompressedFiles" ItemName="CompressedFiles" />
</JsCompress>
<ItemGroup>
<Content Include="#(CompressedFiles)" />
</ItemGroup>
</Target>
Build and Publish are separate targets. Add a target to your project, abstract your minification to its own target, then make the AfterBuild and Publish target depend on the minification target. Something like this:
<Target Name="AfterBuild" DependsOnTargets="Build;Minify">
</Target>
<Target Name="Publish" DependsOnTargets="Build;Minify">
</Target>
<Target Name="Minify" DependsOnTargets="Build">
<ItemGroup>
<JavaScriptFiles Include="Scripts\*.js" Exclude="Scripts\*.min.js" />
</ItemGroup>
<JsCompress Files="#(JavaScriptFiles)" OutputPath="Scripts">
<Output TaskParameter="CompressedFiles" ItemName="CompressedFiles" />
</JsCompress>
<ItemGroup>
<Content Include="#(CompressedFiles)" />
</ItemGroup>
</Target>
This snippet, of course, means you have to have a build target, which may or not be the case. For that reason you may need to modify this. Hope this helps!
Change the file properties. Check the Build Action and Copy to Output Directory properties for those files.

Resources