.NET AssemblyName per configuration -> always rebuilding - c#

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

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>

Visual studio How to set output path dependant of assembly version

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.

Publish Configuration disabled for csproj

Why can't I change these settings shown in the image below?
This is a clickOnce application, and my problem is that I want to change by publish path, assembly name, product name, install URL, and preform some app.config translations based on the build configuration. I am able to achieve this by manually editing the csproj like so
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<AssemblyName>someApplicationTest</AssemblyName>
<ProductName>Some Application Test</ProductName>
<PublishUrl>c:\publish\someApplicationTest\</PublishUrl>
<InstallUrl>http://sub.example.com/someApplicationTest/</InstallUrl>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<AssemblyName>someApplication</AssemblyName>
<ProductName>Some Application</ProductName>
<PublishUrl>c:\publish\someApplication\</PublishUrl>
<InstallUrl>http://sub.example.com/someApplication/</InstallUrl>
</PropertyGroup>
I'm just confused why these options are disabled in visual studio and if I'm missing something. Perhaps I'm confused and these controls weren't even intended for this purpose.
Also, I'm going to be investigating Squirrel.Windows as an alternative later, but for now I wanted to learn more about this.
This is just a visual representation of the structure of a project file. Some settings can have different values for different configurations. Others have only one value that doesn't depend on the configuration. The best example of the first set are the settings in the Build tab. Of course you want to build your program differently in the Release build. So the configuration combos are enabled.
Also have a look-see in the .csproj file with a text editor, Notepad will do just fine. Note the <PropertyGroup> elements, some have a Condition attribute that enables them for a specific configuration. The publish properties are located in the PropertyGroup without a Condition.
So for the settings in the Publish tab Microsoft decided that it was not necessary to make the settings specific to a configuration. Which makes sense if you think about it, you'd only publish your Release build. Well, normally. So the combos are disabled. Feature, not a bug.

Can publish options for ClickOnce be made configuration specific in Visual Studio 2013

I have a C# app with a few different build configurations for different 'flavors' of the app. I would like to have ClickOnce publish options for each configuration, rather than have to set them each time I do a build-and-publish. It appears that the publish options apply to all the configurations. Is there a way to make publish options specific to each configuration?
You cannot make them different for each configuration directly inside Visual Studio. You need to edit the .csproj file and make the following changes to the things you need to be different.
E.g. the first <PropertyGroup> element contains the <PublishUrl> element. You can apply conditions and add the element multiple times:
<PublishUrl Condition=" '$(Configuration)' == 'Debug' ">debug\</PublishUrl>
<PublishUrl Condition=" '$(Configuration)' == 'Release' ">release\</PublishUrl>
When the project is published using the Debug configuration, it will be published to the debug directory.
When the project is published using the Release configuration, it will be published to the release directory.
Apply this conditions to the elements you need to modify. Sadly, you can apply the condition to a whole PropertyGroup, but this is much more complex, as you need to duplicate and keep track of both groups, if you make a change in any of them.
BUT:
This will be overwritten every time you save the project from inside Visual Studio.
So you can either make a xslt file which transforms the file before publishing, or directly create your own publishing application, that fills all required publishing parameter only when publishing it e.g. from a build server.
Edit
This answer will not help. Since I wrote it, I found out that even when using the Choose element, the Visual Studio IDE (at least VS2015) can't seem te keep its hands off our configuration. Although the code below works, when switching between configurations and publishing, at some point the IDE will overwrite this section in the project file in such a way that it no longer works as intended.
Because it technically works as is, and as such could perhaps be useful when using another tool for publishing (or when the issue in VS gets fixed), I'll leave the original answer for reference.
Original answer
I too first tried the solution mentioned by Herdo, only to run into the big "But" he mentions; Visual Studio overwriting your conditionals every time you save the project file.
However, I since found that with the Choose element you can achieve the same behavior without Visual Studio messing with it. You still need to manually edit the .csproj file though.
In short, do not apply the Conditional directly to the PublishUrl tag in the first property group. It will get overwritten by Visual Studio. Instead, define another one later in the project file. When building or publishing, the second one's value will supersede the first one's value, but the IDE will leave it alone. It is that second one you can make conditional using a Choose group.
Below that first property group, add section like this:
<Choose>
<When Condition=" '$(Configuration)' == 'Debug' ">
<PropertyGroup>
<PublishUrl>your\debug\publish\location</PublishUrl>
</PropertyGroup>
</When>
<When Condition=" '$(Configuration)' == 'Release' ">
<PropertyGroup>
<PublishUrl>your\release\publish\location</PublishUrl>
</PropertyGroup>
</When>
</Choose>
Of course, you can add as many configurations and flavors as you like.
At the menu where you set Debug, Release. You can create a new type of configuration. Once you created your own configuration. You can use the code above:
#if ReleaseConfiguration1
//specifics configuration 1 goes here
#endif
#if ReleaseConfiguration2
//specifics configuration 2 goes here
#endif

Do Web Deployment Projects work with x86 build configuration

I have a need to build a website and several DLLs that it references in an x86 configuration. To date we have been using Web Deployment Projects to create Zip files of the resultant site and all it's required files. We need to continue to use WDPs however, they seem to have problems with the x86 build configuration.
In my project, when I build the solution in Release/x86 I get the following error.
Description File Line Column Project
Error 80 Could not load type 'WwwRooot.Default'. /WwwRooot.csproj/Default.aspx 1 1 WwwRooot.csproj_deploy
There are no build errors or warnings from the web application or any of the referenced class libraries.
I thought this might be something specific to the project I'm working on so to prove myself wrong I created a solution containing a Web Application (c#). I then used the Configuration Manager to create the x86 configuration by copying the Any CPU config. I checked the properties page an made sure the new config was set to build to x86, and it was. I built the solution without error.
Next I right clicked the Web App and added a WDP from the context menu.
Right clicked on the WDP and edited the project file. At this point I changed any references for AnyCPU to x86 so that the WDP has conditions of x86 build.
I rebuilt the solution in Release/x86 and everything builds fine.
Next I add a Class Library, use Configuration Manager to create an x86 config for this library, add a reference to the web app for the library and then rebuild all in Release/x86 and I receive the same error as detailed above.
Are WDPs compatible with x86 build?
If I remove the Class Library (and the reference) the solution (including the WDP) builds fine.
I am using Visual Studio 2008 SP1, with the appropriate WDPs installed, on 64Bit Windows 7 Pro.
Out of the box, Web Deployment Projects (WDP) don't work with x86 or x64 build configurations. This is because a Web Application built under one of these configurations outputs the resultant assemblies in a different place and the WDP doesn't know to look there for the DLLs.
There are a few things you'll need to do to get the WDP working with your x86 configuration.
Firstly, your WDP probably doesn't have an x86 configuration, you'll need to create one. Edit the deployment project using the XML editor in Visual Studio (or any text editor), near the top of the file will see a <propertyGroup> tag (usually the second one) with a condition Debug|AnyCPU like so:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>.\Debug</OutputPath>
<EnableUpdateable>true</EnableUpdateable>
<UseMerge>true</UseMerge>
</PropertyGroup>
Duplicate this whole tag and change the configuration to be Debug|x84:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>.\Debug</OutputPath>
<EnableUpdateable>true</EnableUpdateable>
<UseMerge>true</UseMerge>
</PropertyGroup>
Now save the file and open the configuration manager (Build menu > Configuration manager) and check your deployment project now has an x86 configuration.
Now edit the Web Application Project file using your text editor and locate the outputPath element within the Debug|x86 configuration. It should have a value of Bin\x86\Debug. This needs changing to Bin:
<!-- Before -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>Bin\x86\Debug\</OutputPath>
<!-- After -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>Bin\</OutputPath>
Save, close and reload you Web Application Project. We've now instructed the Web Application to put it's DLLs where the WDP expects to find them.
Set your build configuration to x86 and build the project.
Rinse and repeat for Release and any other build configurations you might have.
Try this
Put this command in post compilation events of your web project
xcopy "$(TargetDir)*.*" "$(TargetDir)..\..\" /Y
This command wil copy files from bin\x86\Debug to bin
It will work with Release configuration

Categories