Visual studio How to set output path dependant of assembly version - c#

I want to configure my main output path to something like
C:\Company\UpdaterLauncher\Worker\Version
Where version is my AssemblyInfo.Version in string.
So a new folder each time I decide to change the assembly version.
I know I can change output all time.. But it's annoying.
Is this possible to use something like "C:\Company\UpdaterLauncher\Worker\{AssemblyVersion}" for visual output path to interprete it and build where I want?
I looked a bit in documentation and didn't found anything like this...

Which way do you build the project? By msbuild command-line or within VS IDE?
First direction: Let's read the assembly version number before the build starts, then
pass it to outputpath property.
I've written a script trying to read the version before the build starts. But not completely work:(
E.g: Using a class library project as the example.
Right-click the project and choose edit the xx.csproj, add the script (From In property to FourthNum property) into the PropertyGroup:
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DAB28A16-73AD-4EC5-9F8D-E58CE3EC84BE}</ProjectGuid>
......
<In>$([System.IO.File]::ReadAllText('$(MSBuildProjectDirectory)\properties\AssemblyInfo.cs'))</In>
<Pattern>\[assembly: AssemblyVersion\(.(\d+)\.(\d+)\.(\d+).(\d+)</Pattern>
<FirstNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[1].Value)</FirstNum>
<SecondNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[2].Value)</SecondNum>
<ThirdNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[3].Value)</ThirdNum>
<FourthNum>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern),System.Text.RegularExpressions.RegexOptions.Multiline).Groups[4].Value)</FourthNum>
</PropertyGroup>
It will read the assembly version number from AssemblyInfo.cs. If I have an assembly whose assembly version is 3.13.8.5. Then the FirstNum=3, SecondNum=13 ...
And set the outputpath as: <OutputPath>C:\Company\UpdaterLauncher\Worker\$(FirstNum).$(SecondNum).$(ThirdNum).$(FourthNum)</OutputPath>
Reload the project and build it. You can find the build output there C:\Company\UpdaterLauncher\Worker\3.13.8.5.
Note:
1.In this way, since we will build it in both debug and release mode. We need to set the outputpath value in both propertygroup for debug and release.(2 places)
2.Since we only define the output depending on version, the debug output and release will all locates in same folder. So I think the <OutputPath> would be better like:
<OutputPath>C:\Company\UpdaterLauncher\Worker\$(FirstNum).$(SecondNum).$(ThirdNum).$(FourthNum)\$(Configuration)</OutputPath>
3.This script won't work immediately after you change the version in VS IDE.
Via Command-line: It works well, every time we change the version number and build it, the output is correct.
Within VS IDE: Every time after we change the version, it needs us to unload and reload the project file by right-clicking the project, then it will work. So I say it isn't that perfect.(I would think this issue has something to do with when and how the VS loads the project file)
Second Direction: The build output actually is copy the related
assemblies to output folder. So we can copy or move the output content
to the directory after the build we want by copy or move task.
We can check this issue, using GetAssemblyIdentity to get the info after the build.
Using the way above to get version number, name it $(MyVersion). Then use a after-build target to copy the output to the specified folder.
<Target Name="CopyToSpecificFolder" AfterTargets="build">
<GetAssemblyIdentity
AssemblyFiles="$(OutputPath)$(AssemblyName).dll">
<Output
TaskParameter="Assemblies"
ItemName="MyAssemblyIdentities"/>
</GetAssemblyIdentity>
<PropertyGroup>
<MyVersion>%(MyAssemblyIdentities.Version)</MyVersion>
</PropertyGroup>
<ItemGroup>
<Out Include="$(OutputPath)*.*" />
</ItemGroup>
<Copy DestinationFolder="C:\Company\UpdaterLauncher\Worker\$(MyVersion)" SourceFiles="#(Out)"/>
</Target>
Add this script into the xx.csproj file. In the bottom of it like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
......
<Target Name="CopyToSpecificFolder" AfterTargets="build">
......
</Target>
</Project>
It works well in whether VS IDE or by command-line. And it's for class project, if you're developing a .exe project, change the $(AssemblyName).dll to $(AssemblyName).exe.

Related

Adding reference to another executable with ReferenceOutputAssembly=false doesn't copy dependencies

I have a solution with several executables in it (say, MainApp.exe and Tool.exe).
The main goal is to ensure that the tool (Tool.exe) with its dependencies is copied to the main executable directory during build.
I used the advice from here, and it seemed to work with the older Visual Studio version (at least with some version prior to 16.8).
My project structure (simplified) looks like this:
Solution.sln
├ MainApp.csproj
├ Tool.csproj
| └ App.config
└ ToolLib.csproj
Tool project contains App.config file, and references ToolLib project.
My MainApp.csproj looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Tool/Tool.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Content</OutputItemType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Targets>Build;DebugSymbolsProjectOutputGroup</Targets>
</ProjectReference>
</ItemGroup>
</Project>
After upgrading to 16.8 after the compilation the file Tool.exe was indeed copied to the output directory, but neither its dependency ToolLib.dll nor Tool.config was copied to the output directory any more.
Is this a bug or intended behaviour? What is the proper way to ensure that the whole Tool with all the needed dependencies is copied to the MainApp's output dir?
Added test project reproducing the problem here: https://github.com/vladd/ReferenceOutputAssembly
What you gave is too old and it is not suitable for VS2019. And all your projects target to net core 3.1. I have tested your project both in VS2019 16.8 , VS2019 16.7, even 16.6 which all act the same behavior as you described. Only contain the Tool.dll and Tool.exe.
So I wonder why you said before that the result of the build of ToolLib will be printed in the main project.
Actually, <ReferenceOutputAssembly>false</ReferenceOutputAssembly> will prevent the most main output files of the referenced project and its dependency project being copied into the main project.
Suggestion
You have to set it as true:
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
If you want to not copy ToolLib.pdb and Tool.pdb files into the main project, you could add these node on MainApp.csproj file:
<PropertyGroup>
<AllowedReferenceRelatedFileExtensions>*.pdb;.dll.config</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
If you also want to copy pdb files, you should add .pdb under AllowedReferenceRelatedFileExtensions.
<AllowedReferenceRelatedFileExtensions>.pdb;.dll.config</AllowedReferenceRelatedFileExtensions>
Update 1
I tried your suggestion but with it the files Tools.deps,json and
Tool.runtimeconfig.json are not copied, so running the tool fails.
Add this on MainApp.csproj file:
<PropertyGroup>
<AllowedReferenceRelatedFileExtensions>.pdb;.dll.config;.runtimeconfig.dev.json;.runtimeconfig.json</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>

Project that Imports a project of common files. How to do in Visual Studio

I have a project that contains common files used by several projects and would like to use the project by using the MSBuild Import.
The following is a snippet from a *.csproj that contains what I'm after.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.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')" />
***<Import Project="My.commonFiles.csproj" />
<PropertyGroup>***
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
I cannot find any reference how this can be accomplished from within VS 2015. Logically, I would think I would right click the Project | click Add | click Existing Item | and select the project but that does not work.
I really don't wish to have to manually edit the *.csproj. Any help?
You do have to manually edit the .csproj. Visual Studio 2017 features the ability to edit project files without unloading, but for now that only applies to certain project types.
The other bummer is that Visual Studio 2015 will generally not even pick up changes to imported files until you close and reopen the solution. (This drove me to add a feature to my team's custom Visual Studio extension that detects such changes and prompts you to reload the solution with one click. Unfortunately, I don't have that functionality isolated and available to share on the Visual Studio Gallery.) In some cases I work around that by using the MSBuild task in lieu of an Import element, along these lines:
<MSBuild Projects="DESIRED_FILE.targets"
Targets="DESIRED_TARGET"
Properties="Configuration=$(Configuration);
Platform=$(Configuration);
Verbosity=$(Verbosity);
SomeOtherProperty=$(SomeOtherProperty)" />
However, since the MSBuild task spawns a separate process it is only useful for executing Targets-- not for populating common properties or items like you seem to be trying to do. Sorry that the answer is so disappointing!
By the way, to avoid confusion you would want to name any MSBuild files you create with one of the following extensions instead if .csproj:
.props if the file contains PropertyGroup and perhaps ItemGroup elements and needs to be placed near the top of the importing file
.targets (alternatively, .proj is sometimes used) if it contains Target elements and should be placed near the bottom of the consuming file

.NET AssemblyName per configuration -> always rebuilding

I figured how to change the filename of the output assembly depending on the current configuration:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<OutputPath>path-to-debug-output</OutputPath>
<AssemblyName>myAssembly.Debug</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<OutputPath>path-to-release-output</OutputPath>
<AssemblyName>myAssembly</AssemblyName>
</PropertyGroup>
Unfortunately, now everytime I hit "build all" in Visual Studio in debug configuration, it actually rebuilds everything even if no changes were done at all:
1> myAssembly -> path-to-debug-output\myAssembly.Debug.dll
In release mode I get this message exactly one time, after that "myAssembly" is not build until I do any changes to the code.
I suppose there is some check for existence of the output assembly done (and maybe timestamp checking etc.). Does someone know how I can configure this in my .csproj file (or somewhere else)?
[EDIT]
Here is an example of a full .csproj file with configuration dependent output files: http://pastebin.com/pFj4nxtj

Ensure subdirectory builds before rest of project

Working on a very large C# project with multiple subportions. One of these portions creates and places dlls in a specific location for consumption. As per a recent change we're now trying to place these dlls with every root build call made. (Use to be manually placed every milestone or so)
So I'm referencing the dirs.proj file that is compiling the subdirectory which creates and places the dlls. That all works fine. The problem is that for one reason or another other portions of the project start to look for these dlls before that part has finished compiling.
How can I ensure that this part of the project gets compiled and places the dlls before beginning to compile the rest of it? I have a very brief understanding of and but really don't know how to use them to do what I want.
Thanks for any and all help!
Read up on Build Targets and the MakeDir task.
You'll want to include the logic creating the new directory in a task that is scheduled to occur prior to the default Build task.
<Target BeforeTargets="Build" Name="CreateRequiredFolder" Condition="!EXIST('$(MyDirectory)')" >
<MakeDir Directories="$(MyDirectory)" />
</Target>
Note how we use BeforeTargets to schedule this before Build, and the condition on the target causes it to be skipped if the desired folder already exists.
Here is what I ended up doing to get it to work.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="......." InitialTargets="BuildWebshared">
<ItemGroup>
<WebsharedProjects Include="..\location\dirs.proj" />
</ItemGroup>
<Target Name="BuildWebshared">
<MSBuild
Projects="#(WebsharedProjects)"
Targets="Build">
</MSBuild>
</Target>
....

In TFSBuild.Proj file how to <ConfigureToBuild> for Both AnyCPU and x86 Platform?

I have one MyProject.sln project.
In that there is two Class Library Project and one Window Service Project.
Compiler build Class Library Projects on Any CPU Platform and Window Service project Build on X86 platform.
How i will set the so that My Build execute according to selected Platform which i set in project "MyProjectClass.csproj" file
This is Code of TFSBuild.Proj file
<SolutionToBuild Condition="'$(BuildMyProject)'=='True'"
Include="$(SolutionRoot)/MyProject/MyProject.sln">
<Targets>Build</Targets>
<Properties></Properties>
</SolutionToBuild>
Code for
$BuildFlavor = 'Release'
<ConfigurationToBuild Include="$(BuildFlavor)|Any CPU">
<FlavorToBuild>$(BuildFlavor)</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
<ConfigurationToBuild Include="$(BuildFlavor)|x86">
<FlavorToBuild>$(BuildFlavor)</FlavorToBuild>
<PlatformToBuild>x86</PlatformToBuild>
</ConfigurationToBuild>
MSBuild try to build my MyProject.sln for both the platform. it's take more time for build.
It's show that Build Run successfully but not generate the Binaries of Windows Service in specified $DropLocation.
But,
When I was changed my Windows Service project Build Platform from x86 to Any CPU.Now it's working properly.All three project Binaries generated and i got it in my $DropLocation But
[1] Is there any another way to solve this query?
[2] Is TFSBuild execute only one platform for MSBuild compilation?
[3] Is it required that all the platform of subprojects are same for one .sln project?
Modify your solution this way and change your ConfigurationToBuild to:
<ConfigurationToBuild Include="$(BuildFlavor)|Mixed Platforms">
<FlavorToBuild>$(BuildFlavor)</FlavorToBuild>
<PlatformToBuild>Mixed Platforms</PlatformToBuild>
</ConfigurationToBuild>
If you don't have rights to change solution file try following ideas:
Building specific projects from solution with specific target
Try AdditionalProperties Metadata
Build *.csproj files instead of solution file
Build your projects using MSBuild tasks (override Build target)
I had a similar issue - TFSBuild was set up in a way that it picked always the Debug configuration and I didn't have the permissions to change the build definition. I wanted to have the Release configuration to be created in the TFS build.
My solution was to edit the *.csproj files.
The prerequisite for this to work is that the Configuration field is empty in the related TFS build definition's build process parameters (Process / Items to build / Configurations to build). If you have the rights to modify it, you can directly select the desired configuration, save the build definition and skip the steps of the workaround below.
Steps for workaround (right-click on project):
Unload project
Edit project
Now you can edit the project file. Locate
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
and change it to
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
Finally, right click on the project again and select:
Load project
Now queue a new build and it should create it from Release. Likewise, you can enter any other configuration you like, for example you can create Release_TFS in Visual Studio and then insert it instead of Release. This will ensure you have created a separate definition for the TFSBuild before you do that.

Categories