MSBuild Task from .csproj File - c#

I Am trying to add a Post Build MSBuild event to my csproj to do this i am trying to call an MSBuild task from inside the Afterbuild target in the.csproj file
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.-->
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
<Message Text="Copying Files" />
<MSBuild Projects="post.build"
Targets="Copy"
ContinueOnError="false" />
</Target>
This is then the post.build file.
<Project DefaultTargets="Copy"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Copy">
<Message Text="Copying Files inside COPY" />
<CallTarget Targets="CopyToProfile"/> </Target>
</project>
It seems that the csproj cannot call the MSbuild task, can anyone suggest what might be going wrong here. I get the error
error MSB4057: The target "Copy" does
not exist in the project.

So What I eventually got working was.
I did as Martin Suggested and
<Import Project="post.build"/>
However the MSBuild Task still did not function as planned. So I ended up using the
<CallTarget Targets="copy"/>
To Call across the files. This sounds like it is a limitation in VS2008 and is fixed in VS2010.

Are you sure you haven't made the typo in your actual post.build file as well? That is, it should be not . XML is case-sensitive.
Also, I would double-check that the post.build file is placed in the same folder as the .csproj file.

Related

Distributing .editorConfig custom settings through nuget package

I am having an issue trying to implement compiler-based code analysis in my C# .NetFramework solution. I decided to use Microsoft.CodeAnalysis.NetAnalyzers nuget package with some custom .editorconfig ruleset that I do not want to keep directly in the consuming project. I created another nuget with the .editorconfig and the idea is to copy the file to each consuming project before it is built in order to trigger the analysis (during build).
I tried an approach (described here) where the .editorconfig is copied to csproj location in a beforeBuild task defined in .props file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="('$(Configuration)' == 'Debug')">
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
</PropertyGroup>
<ItemGroup>
<EditorConfigFilesToCopy Include="$(MSBuildThisFileDirectory)..\content\.editorconfig" />
</ItemGroup>
<Target Name="CopyEditorConfig" BeforeTargets="BeforeBuild">
<Message Text="Copying the .editorconfig file from '#(EditorConfigFilesToCopy)' to '$(MSBuildProjectDirectory)'"></Message>
<Copy
SourceFiles="#(EditorConfigFilesToCopy)"
DestinationFolder="$(MSBuildProjectDirectory)"
SkipUnchangedFiles="true"
UseHardlinksIfPossible="false" />
</Target>
</Project>
Unfortunately, the file seems to be copied too late as the msbuild ignores it during the build itself and does not fail due to CA violations as I would expect. If the file had been copied manually before running msbuild, it works.
Do You have any idea why it is like that and how to handle this issue properly?

Getting assembly version of WPF

I have a program that i just built an installer for with wix and im now trying to get the assemblyversion of my program where the tutorial i look at us the GetAssemblyIdentity but where i do not see how im supposed to do this for my app quz its not a console app! the tutorial im looking at is this https://www.youtube.com/watch?v=6Yf-eDsRrnM and at time stamp 1h 28 min he starts looking at assembly versions and how to get them for your installer but i do not really understand how to do this with a wpf app. what i have tried this far is
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="..\WPFAPP\bin\$(C)">
</GetAssemblyIdentity>
</Target>
<Target Name="AfterBuild">
WPFAPP is a placeholder for my app.
FIRST SOLUTION:
You can use $(MSBuildProjectName)
So, specify
<TargetName>$(ProjectName)</TargetName>
In your *.csproj
And then you can use $(MSBuildProjectName) as Assembly name.
To get access to this variable you should write in wixproj
<Target Name="BeforeBuild">
<PropertyGroup>
<DefineConstants>ProjectName=%(MSBuildProjectName)</DefineConstants>
</PropertyGroup>
</Target>
List of availables variables you can see here
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2019
SECOND SOLUTION:
So, if you want to use versions. See example here.
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="..\MyApp\bin\$(Configuration)\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version)</DefineConstants>
</PropertyGroup>
</Target>
You should specify Assembly file (dll and exe files is both files of .Net and .exe file can be specified here too).
Then you can use BuildVersionVariable like here:
<Product Id="*"
Name="WixProject1"
Language="1033"
Version="$(var.BuildVersion)"
Manufacturer="WixProject1"
UpgradeCode="c93e09b9-9e8f-444c-a35b-84beb2c3788f">
...
</Product>
p.s. You can combine this approaches and get some info from one path and some info from another. The main IDEA to define constants in project file and then get acces s to it from WIX

Make VisualStudio run a command to generate C# code

I'm trying to do something extremely simple, yet despite hours of scouring the Internet and desperately typing in every tiny example fragment I can find, I literally cannot make VS do what I want. Seriously frustrated here!
This is what I want:
When I do a build, I want VS to find every *.fubar file in the project.
For each *.fubar file, I want VS to execute fubar.exe with the input file as argument.
I want VS to compile the resulting *.cs files as usual.
I am damn-well convinced it must be possible to achieve this trivially simple task. I just need to figure out how.
So that's the problem. Let me explain what I've researched so far.
It seems that you can write a VS extension by implementing some COM interface. But that requires you to edit the Registry to tell VS where the plugin is. Obviously that's completely unacceptable; I should not have to reconfigure the OS just to run a command-line tool!
It appears that MSBuild is supposed to be this ultra-configurable build management tool where you can define an arbitrary set of build steps and their interdependencies. It seems like it should be trivial to add an extra task before the build C# compilation step that runs fubar.exe to generate the source code. And yet, after hours of trying, I cannot get a single C# file to appear.
Some documents talk about special pre-build and post-build steps. But it seems silly to need a special hook when the number and order of build steps is supposed to be arbitrary to begin with. Regardless, I tried it, and it still didn't work.
To be clear: Playing around with the project file makes VS display the item properties slightly differently, and doesn't make the build fail. But it also doesn't ever make any C# files appear. (At least, I can't find them anywhere on the disk. I can't tell if my tool is actually being run or not — I suspect "not".)
Can anybody tell me how to make this trivial, trivial thing work? There must be a way!
I agree that T4 may be a prettier solution, but the below MsBuild code works.
You can create a custom targets file that does the calling, I created one that simply copies files, but the process should be similar for invoking your executable.
Make sure you set your FuBar files to BuildAction "Fubars". The Build action has been defined in the targets file using the "AvailableItemName" property.
You may need to extend the below script with a couple of Conditions and Exists checks to make sure it runs when needed. I called it convert.targets and places it in the project directory.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Ensure Visual Studio puts the FuBars item in the Build Action dropdown -->
<ItemGroup>
<AvailableItemName Include="FuBars" />
</ItemGroup>
<!-- Execute action on all items matching FuBar (only if output is missing or input has changed) -->
<Target Name="GenerateCodeFubar" Inputs="#(Fubars)" Outputs="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(Fubars.Identity).g.cs" >
<MakeDir Directories="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(Fubars.RelativeDir)" />
<Exec Command="cmd /c copy "%(FuBars.FullPath)" "$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(FuBars.Identity).g.cs""/>
</Target>
<!-- Pick up generated code in the Compile group. Separated from the execute action so that the previous action only runs when files have changes -->
<Target Name="IncludeCodeFubarInCompile">
<ItemGroup>
<GeneratedFubars Include="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/%(FuBars.Identity).g.cs"/>
</ItemGroup>
<CreateItem Include="%(GeneratedFubars.FullPath)">
<Output TaskParameter="Include" ItemName="Compile" />
</CreateItem>
</Target>
<!-- Clean up for Rebuild or Clean -->
<Target Name="DeleteCodeFubar">
<RemoveDir Directories="$(MSBuildProjectDirectory)/obj/$(Configuration)/Fubars/" />
<Delete Files="$(MSBuildProjectDirectory)/obj/$(Configuration)/fubars/**/*.g.cs" />
</Target>
<!-- Instruct MsBuild when to call target on Build-->
<PropertyGroup>
<BuildDependsOn>
GenerateCodeFubar;
IncludeCodeFubarInCompile;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<!-- Instruct MsBuild when to call target on Clean-->
<PropertyGroup>
<CleanDependsOn>
DeleteCodeFubar;
$(CleanDependsOn);
</CleanDependsOn>
</PropertyGroup>
</Project>
Include it in your .csproj after the import the last targets file:
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- IMPORT HERE -->
<Import Project="convert.targets" />
<!-- END: IMPORT HERE -->
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
You may have to tell Visual Studio to turn off the Host Compiler, as Visual Studio caches the contents of the <Compile> group for performance. Though a quick test shows this may not be needed.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!-- The first property group should not have any condition on it -->
<PropertyGroup>
<!-- Include below item in the first PropertyGroup item:
<UseHostCompilerIfAvailable>FALSE</UseHostCompilerIfAvailable>
</PropertyGroup>
This is extremely easy.
In your project properties select the Build Events tab and enter a correct Pre-build event command line.
Without knowing what footer.exe is nor its command line args, I can't give you any specific help for that app. But there is a "Macros" button on the pre-build event dialog that can help you with various substitution variables.
Additionally, VS comes with the T4 templating system which might be a better fit than whatever footer.exe is.

Reference AssemblyFileVersion in csproj file

Once my nant build has completed I'd like to rename the generated .exe file using a post-build command which is appended to the end of the projects .csproj file (within the Project element):
<Target Name="AfterBuild">
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(TargetDir)\MyApplication-$(AssemblyFileVersion).exe" SkipUnchangedFiles="true" />
</Target>
As you can see above, I am trying to rename the executable to: MyApplication-$(AssemblyFileVersion).exe however, this is obviously wrong, as the resulting executable is simply named: MyApplication-.exe (so the version I am trying to add at the end of the file name is missing).
I have defined the AssemblyFileInfoVersion in the AssemblyInfo.cs file as follows:
[assembly: AssemblyFileVersion("1.5.1")]
So the question is: How can I access the AssemblyFileVersion in the csproj file of that same project?
GetAssemblyIdentity can get information about complied assemblies. The task output contain metadata entries about Version, PublicKeyToken, and Culture.
I used $(TargetDir)\$(TargetName).exe as the assembly file.
<ItemGroup>
<AssembliesPath Include="$(TargetDir)\$(TargetName).exe" />
</ItemGroup>
<Target Name="GetAssemblyInfo">
<GetAssemblyIdentity AssemblyFiles="#(AssembliesPath)">
<Output TaskParameter="Assemblies" ItemName="AssemblyInfo"/>
</GetAssemblyIdentity>
</Target>
And then:
<Target Name="AfterBuild">
<GetAssemblyInfo />
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(TargetDir)\MyApplication-%(AssemblyInfo.Version).exe" SkipUnchangedFiles="true" />
</Target>
The following code is from ISun's original answer and this is actually how I ended up doing it in the end, as I had problems defining a custom task (ms build references were constantly auto-kicked and the build kept failing over and again).
As you can see from the comments under ISun's answer I always got the version 0.0.0.0 - despite having changed the version for AssemblyFileVersion and AssemblyVersion to 1.0.0.0 by manually opening the AssemblyInfo.cs in a texteditor. I later read how to edit the AssemblyInfo.cs from my Visual Studio, here is how:
Right-click on your project (that generates the exe file)
Click on Properties
Open the Application tab (first tab on the left) on the window that'll open
You'll see fields for setting the Assembly name, Default namespace etc however to edit the
AssemblyInfo.cs, simply click on the Button called Assembly Information to the right
And for some reason - I have no clue why it suddenly worked, after I had put in all the information via the above method (using Visual Studio) ! When I first opened the AssemblyInfo.cs using the above way, all my fields were actually empty, despite them being filled in the actual file.
And now that I got the AssemblyInfo.cs to finally function correctly, I used ISun's original code to achieve my goal. By adding the following snippet just before the closing tag in my project's .csproj file:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(TargetDir)\$(TargetName).exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyInfo"/>
</GetAssemblyIdentity>
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(TargetDir)\MyApplication-%(AssemblyInfo.Version).exe" SkipUnchangedFiles="true" />
</Target>

MSBuild task does not cause generated c# files to compile

I've been trying to write an MSBuild task to "compile" CoCo/R .ATG files into C# files which will then get compiled into the executable, this is to replace the pre-build event.
I've managed to get the .ATG -> .cs process working, however something is not right since the generates .cs files do not get compiled.
If I then modify the .ATG file again, the "old" .cs files seems to get compiled then new ones generated.
I'm quite sure I'm missing something that will inform the rest of the build process that these files have changed.
Here is the target definition that I have included in my Visual Studio 2010 project.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AvailableItemName Include="BuildATG" />
</ItemGroup>
<PropertyGroup>
<CoreBuildDependsOn>
BuildATGTarget;
$(CoreBuildDependsOn)
</CoreBuildDependsOn>
</PropertyGroup>
<Target Name="BuildATGTarget" Inputs="#(BuildATG)" Outputs="#(BuildATG -> '%(RelativeDir)Parser.cs')">
<Exec Command="Coco.exe %(BuildATG.Identity)" Outputs="%(BuildATG.RelativeDir)Parser.cs" />
</Target>
</Project>
I am completly new to MSBuild, so any advice / pointers would be appreciated.
One possible solution I have found is to do the following changes and add an ItemGroup inside the target.
<Target Name="BuildATGTarget" Inputs="#(BuildATG)" Outputs="#(BuildATG -> '%(RelativeDir)Parser.cs')">
<Exec Command="Coco.exe %(BuildATG.Identity)" Outputs="%(BuildATG.RelativeDir)Parser.cs" />
<ItemGroup>
<Compile Include="%(BuildATG.RelativeDir)Parser.cs" />
<Compile Include="%(BuildATG.RelativeDir)Scanner.cs" />
</ItemGroup>
</Target>
To avoid duplicate files in your build, you also need to mark the files (Parser.cs and Scanner.cs) in the Visual Studio 2010 project with Build Action: None

Categories

Resources