Get the assembly project file name from Output TaskParameter - c#

I am using msbuild.exe to automate my build from the command line.
I do the following steps.
Compile projects into a folder each outside the project directory
zip every compiled project
here are my targets
first one to compile
<Target Name="BuildProjects" DependsOnTargets="BeforeBuild">
<ItemGroup>
<BuildProjectFiles Include="**\MyCompany.Project1\MyCompany.Project1.csproj" />
<BuildProjectFiles Include="**\MyCompany.Project2\MyCompany.Project2.csproj" />
<BuildProjectFiles Include="**\MyCompany.Project2\MyCompany.Project2-CE.csproj" />
... and some more
</ItemGroup>
<MSBuild Projects="#(BuildProjectFiles)" Properties="AllowUnsafeBlocks=true;Configuration=$(Configuration);OutputPath=$(MSBuildProjectDirectory)\Deploy\bin\%(BuildProjectFiles.FileName)">
<Output TaskParameter="TargetOutputs"
ItemName="BuildProjectsOutputFiles" />
</MSBuild>
</Target>
and now the target to zip every compiled project to its one file.
<Target Name="ZipProjects" DependsOnTargets="BuildProjects">
<CreateItem
Include="#(BuildProjectOutputFiles)"
AdditionalMetadata="AssemblyName=%(Filename);ProjectName=%(RelativeDir)">
<Output TaskParameter="Include" ItemName="BuildProjectsOutputFiles123"/>
</CreateItem>
<CreateItem Include="%(BuildProjectsOutputFiles123.RelativeDir)*" AdditionalMetadata="OutputFileName=$(MSBuildProjectDirectory)\Deploy\dist\$(Configuration)\%(BuildProjectsOutputFiles123.AssemblyName)-$(MajorNumber).$(MinorNumber).$(ReleaseNumber).zip;WorkingDirectory=%(BuildProjectsOutputFiles123.RelativeDir)">
<Output TaskParameter="Include" ItemName="BuildProjectsOutputFiles456"/>
</CreateItem>
<Zip Files="#(BuildProjectsOutputFiles456)"
WorkingDirectory="%(BuildProjectsOutputFiles456.WorkingDirectory)"
ZipFileName="%(BuildProjectOutputFiles456.OutputFileName)" />
<!-- ZipLevel="9"/> -->
</Target>
So what happens is that every project I specified in BuildProjectFiles gets compiled into the folder <rootdir>\deploy\bin\<name of the csproj file without extension\
In the second step I use the MSBuild.Community.Tasks Zip-Task to zip every project and copy it to <rootdir>\deploy\dist\release\<assemblyname>-<version>.zip
So basically the assembly project1.exe and its dependencies are in the file project1-2.4.7.zip after executing msbuild.
That works very well. But now I have a change that I can't figure out how solve. I have two assemblys with the same assembly name (one for Windows and the other for Windows CE) so the first project compiles and creates a folder project2-2.4.7.zip and then the next project compiles and overwrites the zip file.
Now I want the Zip-File to be named after the .csproj file (like the bin folder).
Since my one project file is called mycompany.project2.csproj and the other one mycompany.project2-ce.csproj I should be fine.
In short: How can I pass the project name to the zip target?

Is this suitable?
$(MSBuildProjectName)
MSBuild Reserved Properties

Related

Wix Toolset how to copy to folder with version in path

I'm using Wix toolset v 3.11 in VS2019 to create a setup for our application. In order to ease maintenance, I need to copy the installer files to a folder with the version in its path.
Basically what I thought was to use a post build event where I would copy the files to a \$(BootstrapPackageVersion)\$(Configuration)\ directory, but I am not able to find such variable.
I tried to extract the version from the .exe package (since it gets its version from the MSI package that itself get its version from the original application, as intended) with the following code in the .wixproj file:
</PropertyGroup>
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersions" />
</GetAssemblyIdentity>
<CreateProperty Value="$(TargetDir)/%(AssemblyVersions.Version)/">
<Output TaskParameter="Value" PropertyName="CustomTargetName" />
</CreateProperty>
<Copy SourceFiles="$(TargetDir)/*" DestinationFolder="$(CustomTargetName)"/>
</Target>
But it fails with the following error:
Cannot get assembly name for "[Path]\Setup.exe". Could not load file
or assembly 'Setup.exe' or one of its dependencies. The module was
expected to contain an assembly manifest.
I saw that there is a property, $(WixBundleVersion), but it doesn't seems to be available to use in build events.
Basically what I want to achieve is a fairly simple thing: take the output from the bootstrapper project and copy everything to another path that include the package version. It is possible?
The way I do this in my wix project is to output setup.exe into it's default folder and then run an AfterBuild and copy it to the final artifacts directory
in wixproj:
<Target>
...
<GetAssemblyIdentity AssemblyFiles="..\MyApp.App\bin\$(Configuration)\net5.0\FrameworkDep\win-$(Platform)\Publish\MyApp.App.dll">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version)</DefineConstants>
</PropertyGroup>
...
</Target>
<Target Name="AfterBuild">
<Copy SourceFiles=".\bin\$(Configuration)\en-us\$(OutputName).msi" DestinationFiles=".\bin\$(Configuration)\$(OutputName)_%(AssemblyVersion.Version).msi" />
</Target>
This assigns
BuildVersion which you can then use in your wxs files
and
AssemblyVersion which you can use in the wixproj with %(AssemblyVersion.Version)
the AfterBuild section is copying the msi to
MyApp_1.2.3.msi
in my wxs file I can use:
<Product Id="*" Name="My App" Language="1033"
Version="$(var.BuildVersion)>
This is then using the build version from above so I just version control MyApp.App project.

Single nuget.target file executed from multiple targets file

We have setup an internal nuget server. While setting things up we have several packages with .targets file which copies some dlls to output folder.
When we use these packages only the last nuget.target file (last package added) seems to be executing and copying the files to output folder.
Cant seem to figure this out as to why others wouldnt execute.
Any suggestion ?
EDIT:
Issue only arises during Debug Configuration.
Code below in csproj file...
<Import Project="..\..\..\packages\Package2.1.0.3504\build\Package2.targets" Condition="Exists('..\..\..\packages\Package2.1.0.3504\build\Package2.targets')" />
Nusepc file:
<file src="buildTargets\Pack1.targets" target="build\Pack1.targets" />
<file src="dlls\external\x64\Pack1Proj\Pack1.dll" target="runtimes\x64\lib\net451\Pack1.dll" />
Targets file:
<Target Name="AfterBuild" >
<Exec Command="xcopy /Y "$(MSBuildThisFileDirectory)..\runtimes\x86\lib\net451" "$(TargetDir)"" />
</Target>
Targets using the same name overwrite each other. So if you have multiple
<Target Name="AfterBuild">
only one will be run - the one that is imported last. In fact, AfterBuild is an empty target defined in the common targets that is meant to be overwritten.
To make your targets compatible, use different methods to hook into this build step:
<Target Name="Package1AfterBuild" AfterTargets="AfterBuild">

Generate C# using MSBuild

I have a batch file which generates a cs file. The new cs file is stored inside one of my projects solution.
Here is a snippet of my msbuild task:
<Target Name="MyDummyGenerator" BeforeTargets="Build">
<Message Text="Generating file" />
<Exec Command="./path_to_batch_file/dummy.bat" ContinueOnError="false" />
</Target>
The problem is that my generated file is not listed in the files to compile and is not a part of the assembly. After trying to add this file implicitly to the csproj file using <Compile Include="MyGeneratedFile.cs"> I got this error (MyGeneratedFile.cs is the file written from the batch file:
"Duplicate 'Content' items were included. The .NET SDK includes 'Content' items from your project directory by default".
BTW - When compiling it again the generate file is a part of the assembly but it is because the file is in the project source before start the entire build step...
I even tried to run it in the PreBuild event with no luck.
What is the proper way of generating c# code using msbuild?
P.S I'm using .net Framework 4.7.1
I guess this post answer the question:
https://mhut.ch/journal/2015/06/30/build-time-code-generation-in-msbuild
In a nutshell, you need to hook the CoreCompile target to generate your file before compilation (or intellisense) starts and generate a Compile item inside a target.
See this doc for details on how to create items during build:
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-items?view=vs-2019#create-items-during-execution
Also you should generate the file to the intermediate output (obj folder) rather than in your source to not mess up.
In the example below, the inputs and outputs are mainly here for MSBUILD to know what to refresh when files are changing.
<Target Name="UpdateGeneratedFiles"
DependsOnTargets="_UpdateGeneratedFiles"
Condition=="'#(ResourceFile)' != ''"
>
<ItemGroup>
<Compile Include="$(IntermediateOutputDir)GeneratedFile.g.cs" />
<!-- see https://mhut.ch/journal/2016/04/19/msbuild_code_generation_vs2015
<FileWrites Include="$(IntermediateOutputDir)GeneratedFile.g.cs" />
-->
</ItemGroup>
</Target>
<Target Name="_UpdateGeneratedFiles"
Inputs="$(MSBuildProjectFile);#(ResourceFile)"
Outputs="$(IntermediateOutputDir)GeneratedFile.g.cs"
>
<FileGenerationTask
Inputs="#(ResourceFile)"
Output="$(IntermediateOutputDir)GeneratedFile.g.cs"
>
</Target>

ClickOnce include imported .targets when publishing (FFMPEG)

I have a WinForms application which I am publishing via ClickOnce. This applciation includes the Accord FFMPEG libraries, which are included as references.
The FFMPEG NuGet package folder includes a .targets file, which includes a variety of dlls needed for proper operation of the FFMPEG library (avcodec.dll, avformat.dll, avutil.dll). These are copied to the \bin folder when building the project. This is done by including this line in the .csproj:
Import Project="..\packages\Accord.Video.FFMPEG.3.3.0\build\Accord.Video.FFMPEG.targets" Condition="Exists('..\packages\Accord.Video.FFMPEG.3.3.0\build\Accord.Video.FFMPEG.targets')"
However when publishing the application via ClickOnce, these files are not included in the published folder. Is there a way to run the Import Project task and add the files into the published folder?
Unfortunately I didn't manage to get this working using the .targets file. My solution in the end was to copy the required dlls to the deployment folder and include them in the manifest, as below.
<ItemGroup Label="FFMPEG DLL">
<ClickOnce Include="..\packages\Accord.Video.FFMPEG.3.3.0\build\*.dll">
<InProject>false</InProject>
<Visible>false</Visible>
</ClickOnce>
</ItemGroup>
<Target Name="BeforeBuild">
<CreateItem Include="#(ClickOnce)" AdditionalMetadata="TargetPath=%(FileName)%(Extension);IsDataFile=false">
<Output TaskParameter="Include" ItemName="_DeploymentManifestFiles" />
</CreateItem>
</Target>
<Target Name="BeforePublish">
<Touch Files="#(IntermediateAssembly)" />
</Target>
This is using .NET 4.6.1 and VS2017.

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>

Categories

Resources