I am trying to configure an MSBuild target, in my Project.csproj file. The target, called "Test" is called with the following command:
msbuild.exe /target:Test
However, I cannot figure out how to change the AssemblyName (original assembly name is MyProject), for this target. My code looks like this:
<Target Name="Test">
<PropertyGroup>
<PublishUrl>D:\PublishLocation\ClickOnce Setup\</PublishUrl>
</PropertyGroup>
<MSBuild Projects="$(ProjLocation)\$(ProjectName).csproj" Properties="ProductName=$(ProductName) (Test); AssemblyName=MyProjectTest; PublishUrl=$(PublishUrl)" Targets="Publish" />
<ItemGroup>
<SetupFiles Include="$(ProjPublishLocation)\*.*" />
<UpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(SetupFiles)" DestinationFolder="D:\PublishLocation\ClickOnce Setup\" />
<Copy SourceFiles="#(UpdateFiles)" DestinationFolder="D:\PublishLocation\ClickOnce Setup\Application Files\%(RecursiveDir)" />
</Target>
If I remove the AssemblyName=MyProjectTest; from the code, it works fine. If I run it as above, I get the following error:
"C:\Users\...\MyProject\MyProject.csproj"
(Test target) (1) ->
"C:\Users\...\MyProject\MyProject.csproj"
(Publish target) (1:2 ) ->
(CoreCompile target) ->
Models\SessionModel.cs(207,51): error CS1528: Expected ; or = (cannot
specify constructor arguments in declaration) [
C:\Users\...\MyProject\MyProject.csproj]
(...and a lot more, which is more or less the same)
UPDATE
I found some more, perhaps more interesting, warnings in the build output:
"C:\Users\...\MyProject\MyProject.csproj"
(Fast target) (1) ->
"C:\Users\...\MyProject.csproj"
(Publish target) (1:2 ) -> (ResolveAssemblyReferences target) ->
C:\Program Files
(x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(1820,5):
warning MSB3277:
Found conflicts between different versions of the
same dependent assembly that could not be resolved. These reference
conflicts a re listed in the build log when log verbosity is set to
detailed. [C:\Users\...\MyProject\MyProject.csproj]
It might be worth mentioning, that I have older versions published in the publish location, in which the assembly name was not changed. Can this cause the conflict I see...?
Change AssemblyName via MSBuild Target
According to your Test target and another post, I have created a sample app to test this issue with following scripts and MSBuild command line msbuild.exe /target:Test:
<PropertyGroup>
<ProjLocation>$(ProjectDir)</ProjLocation>
<ProjLocationReleaseDir>$(ProjLocation)\bin\Debug</ProjLocationReleaseDir>
<ProjPublishLocation>$(ProjLocationReleaseDir)\app.publish</ProjPublishLocation>
<DeploymentFolder>D:\PublishFolder\Test\</DeploymentFolder>
</PropertyGroup>
<Target Name="Test" DependsOnTargets="Clean">
<MSBuild Projects="$(ProjLocation)\$(ProjectName).csproj" Properties="ProductName=$(ProductName) (Test); AssemblyName=MyProjectTest; PublishUrl=$(PublishUrl)" Targets="Publish" />
<ItemGroup>
<SetupFiles Include="$(ProjPublishLocation)\*.*" />
<UpdateFiles Include="$(ProjPublishLocation)\Application Files\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(SetupFiles)" DestinationFolder="$(DeploymentFolder)\Public Test\" />
<Copy SourceFiles="#(UpdateFiles)" DestinationFolder="$(DeploymentFolder)\Public Test\Application Files\%(RecursiveDir)" />
</Target>
It works fine without any issue, and assembly name was also changed:
So this issue should be more related to the error CS1528 in the SessionModel.cs file and warning MSB3277. To resolve the CS1528 error, you can check the document Compiler Error CS1528 for some more details.
And for the warning MSB3277, you can change the "MSBuild project build output verbosity" to "Detailed" to check the reference conflicts and resolve this conflicts. you can see this thread for some details.
Hope this helps.
It turns out the problem was, that I had included System.net.http.dll as a file in my project. This was set to Content as build actions. When I changed it to Embedded resource, it started working.
Related
I currently have a solution with a web.api project that I want to deploy to different virtual directories in my local IIS. Currently I am doing the following in the .csproj of the api:
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)' == 'CustomerOne.Debug'">
<CustomerName>CustomerOne</CustomerName>
....
</PropertyGroup>
...
These variables are used extenisvely further on for web.config transforms, copying to different locations, etc., by referencing them like $(CustomerName).
The only place where it does not work is in the definition of the virtual directory, i.e., I'd like to connect the build configuration to the IISUrl below, which you can hardcode:
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
...
<IISUrl>http://localhost/api/something</IISUrl>
...
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
Replacing this by <IISUrl>http://localhost/api/$(CustomerName)</IISUrl> does not work. Ideas?
Replacing this by http://localhost/api/$(CustomerName) does not work. Ideas?
That because Anything inside of a ProjectExtensions element will be ignored by MSBuild.
You can get the detailed info from this document ProjectExtensions Element (MSBuild):
Allows MSBuild project files to contain non-MSBuild information.
Anything inside of a ProjectExtensions element will be ignored by
MSBuild.
That is the reason why the Msbuild variables not work in Project Extensions.
Hope this helps.
You could update the underlying project file. A Target like this in your project file would do it.
<Target Name="AfterBuild">
<PropertyGroup>
<NewUrl>http://localhost/api/$(CustomerName)</NewUrl>
</PropertyGroup>
<Message Text="Updating IISUrl: $(NewUrl) in $(MSBuildProjectFile)" />
<XmlPeek Namespaces="<Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>" XmlInputPath="$(MSBuildProjectFile)" Query="/msb:Project/msb:ProjectExtensions/msb:VisualStudio/msb:FlavorProperties/msb:WebProjectProperties/msb:IISUrl/text()">
<Output TaskParameter="Result" ItemName="Peeked" />
</XmlPeek>
<Message Text="Current Url: #(Peeked)" />
<!-- Only update the IISUrl if its changed -->
<XmlPoke Condition=" '#(Peeked)'!='$(NewUrl)' " XmlInputPath="$(MSBuildProjectFile)" Namespaces="<Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>" Query="/msb:Project/msb:ProjectExtensions/msb:VisualStudio/msb:FlavorProperties/msb:WebProjectProperties/msb:IISUrl" Value="$(NewUrl)" />
</Target>
However it does have side affects. Changing the underlying project file means Visual Studio decides it should reload the project.
To use it you cannot go directly into Debug. Instead build, reload the project and then go into debug. If you go directly into Debug (with a compile) it will use the old url.
I am not pretty familiar with msbuild technique.
Currently I have a build.proj in my solution to assign a build number to the exe, by using a following param:
<PropertyGroup Condition=" '$(BUILD_NUMBER)' != '' ">
<!-- Build Server Number -->
<Version>$(BUILD_NUMBER)Version>
<FileVersion>$(BUILD_NUMBER)FileVersion>
<InformationalVersion>$(BUILD_NUMBER)InformationalVersion></PropertyGroup><Target Name="Version">
<Attrib Files="$(MSBuildProjectDirectory)\AssemblyInfo.cs" ReadOnly="False" />
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(MSBuildProjectDirectory)\GlobalAssemblyInfo.cs"
GenerateClass="true"
AssemblyCopyright="Copyright © $(Year). All rights reserved."
AssemblyConfiguration="$(BuildConfiguration)"
AssemblyVersion="$(Version)"
AssemblyFileVersion="$(FileVersion)"
AssemblyInformationalVersion="$(InformationalVersion)" /> </Target>
Now I can't use build.proj. Is it any alternative way to assign a build number to csproj?
We generate a AssemblyInfo.cs file to put in all assembly attributes we need to define. You could do this on pre-build time if that suits you. There we define the AssemblyVersion, AssemblyFileVersion attribute and other related attributes.
Those properties will be used compiling the assembly.
I have a VS 2010 solution that uses config transforms like so:
<Target Name="AfterBuild">
<TransformXml Condition="'$(IsDesktopBuild)' == 'false'" Source="Web.config" Transform="$(ProjectConfigTransformFileName)" Destination="$(PublishDir)\_PublishedWebsites\$(ProjectName)\Web.config" />
<Delete Files="$(PublishDir)\_PublishedWebsites\$(ProjectName)\Web.Debug.config" Condition="'$(IsDesktopBuild)' == 'false'" />
<Delete Files="$(PublishDir)\_PublishedWebsites\$(ProjectName)\Web.Release.config" Condition="'$(IsDesktopBuild)' == 'false'" />
<Delete Files="$(PublishDir)\_PublishedWebsites\$(ProjectName)\Web.Something.config" Condition="'$(IsDesktopBuild)' == 'false'" />
<Delete Files="$(PublishDir)\_PublishedWebsites\$(ProjectName)\Web.Else.config" Condition="'$(IsDesktopBuild)' == 'false'" />
<AspNetCompiler Condition="('$(IsDesktopBuild)' != 'false') AND ('$(MvcBuildViews)'=='true')" VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
<AspNetCompiler Condition="('$(IsDesktopBuild)' == 'false') AND ('$(MvcBuildViews)'=='true')" VirtualPath="temp" PhysicalPath="$(PublishDir)\_PublishedWebsites\$(ProjectName)" />
</Target>
This is in each website project (.csproj) in .sln.
When I run the v 4.0 MSBuild with a /p:PublishDir specified everything is fine. But when I run this as a Build Definition using Default template (GitTemplate.xaml) the folder for some reason resolves to $(ProjectDir), not the publish location. so the transform target is trying to copy files to the folder that doesn't exist ($(ProjectDir)\_PublishedWebsites\$(ProjectName)\Web.config).
If I manually specify the /p:PublishDir to a STATIC location on disk, the build succeeds. How can I tell the MSBuild where the PublishDir actually is?
I am happy to edit the workflow to assign a variable or something, I just don't see how its possible.
I also tried specifying relative paths (e.g. /p:PublishDir="..\..\bin\\") and the team build agent variables (same as we have configured for build paths, e.g. /p:PublishDir="D:\$(BuildAgentId)\$(BuildDefinitionId)\bin\\") to no avail - it seems that the variables are not unwrapped and relative path doesn't work.
Add something like this before the AfterBuild target:
<PropertyGroup>
<PublishDir>PATH_TO_YOUR_PUBLISH_DIR</PublishDir>
</PropertyGroup>
You should be able to use relative paths without problem:
<PropertyGroup>
<PublishDir>..\..\bin\</PublishDir>
</PropertyGroup>
Remember that the path will be relative to the current project file.
If you want to override it from the command line you can add a condition like this:
<PropertyGroup>
<PublishDir Condition="'$(PublishDir)'==''">PATH_TO_YOUR_PUBLISH_DIR</PublishDir> <!-- This will be the default value -->
</PropertyGroup>
Then call MsBuild with /p:PublishDir="Your_New_Path" to override the default value.
If you have a VS solution you could use something like this:
<PropertyGroup>
<PublishDir Condition="'$(PublishDir)'==''">$(SolutionDir)PublishDir</PublishDir> <!-- This will be the default value -->
</PropertyGroup>
This will use the directory where the solution is placed as root and will create a "PublishDir" folder there.
This will work only if you have a solution, off course you could define a default value in case there's no solution:
<PropertyGroup>
<SolutionDir Condition="'$(SolutionDir)'=='' Or '$(SolutionDir)'=='*Undefined*'">Your_Default_SolutionDir</SolutionDir>
<PublishDir Condition="'$(PublishDir)'==''">$(SolutionDir)PublishDir</PublishDir> <!-- This will be the default value -->
</PropertyGroup>
I have a situation where i want the versioning to be dynamic at build time.
Version Pattern: <year>.<month>.<day>.<hhmm>
But i have read where the String value used in the Attribute is reparsed at compile time.
Any advise on how to get this dynamic versioning completed?
Ideal situation:
<Assembly: AssemblyVersion("4.0.0.0")>
<Assembly: AssemblyFileVersion(Year(Now) & "." & Month(Now()) & "." & Day(Now()) & "." & String.format("hhmm", now()))>
I know it wont work but should get the point acrossed.
You can use the MsbuildCommunityTasks to generate the build number and to customize the assembly file version on pre-build time.
Download the zip at MsbuildCommunityTasks
Unzip to the folder [SolutionFolder]\MsBuildExtensions\MSBuildCommunityTasks
Add the sample below on your project (csproj), just after the Microsoft.CSharp.Targets import.
<PropertyGroup>
<MSBuildCommunityTasksPath>$(MSBuildThisFileDirectory)..\MsBuildExtensions\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
<My-PropertiesDir>Properties</My-PropertiesDir>
</PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets"/>
<Target Name="BeforeBuild">
<Time Format="yyyy.MM.dd.HHmm">
<Output TaskParameter="FormattedTime" PropertyName="My-VersionNumber" />
</Time>
<Message Text="Building $(My-VersionNumber) ...">
</Message>
<ItemGroup>
<My-AssemblyInfo Include="$(My-PropertiesDir)\AssemblyVersionInfo.cs" />
<Compile Include="#(My-AssemblyInfo)" />
</ItemGroup>
<MakeDir Directories="$(My-PropertiesDir)" />
<AssemblyInfo OutputFile="#(My-AssemblyInfo)"
CodeLanguage="CS"
AssemblyFileVersion="$(My-VersionNumber)"
AssemblyInformationalVersion="$(My-VersionNumber)"
Condition="$(My-VersionNumber) != '' "
/>
</Target>
<Target Name="AfterBuild">
<Delete Files="#(My-AssemblyInfo)" />
</Target>
Wipe the AssemblyFileVersion attribute from your AssemblyInfo.cs. It will be generated at build time.
You'll see the version number being printed on the console when you build. The generated file is deleted on the AfterBuild target, to keep your source control clean.
BeforeBuild:
Building 2013.01.14.1016 ...
Created AssemblyInfo file "Properties\AssemblyVersionInfo.cs".
(...)
AfterBuild:
Deleting file "Properties\AssemblyVersionInfo.cs".
If you want do this to many projects with less msbuild code, it will be necessary to create a customized build script to wrap up your solution.
In my C# ClickOnce application, there is an auto-incremented publish version in the Project -> Properties -> Publish tab. I'd like to display that version in my menu Help -> About box, but the code I'm using apparently accesses the assembly Version, which is different.
The Assembly Version can be changed manually in the Project -> Properties -> Application -> Assembly Information dialog. So for now, every time before I publish I've been copying the publish version to the assembly version, so my dialog shows the current version of
the application. There must be a better way to do this.
All I really want to do is have an accurate, auto-updated, code-accessible version number.
Here's the code I'm using to access the assembly version number:
public string AssemblyVersion
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
An alternative would be to find code that accesses the publish version.
sylvanaar's last line looks like the way to go, in my experience; but with the caveat that it is only available to deployed versions of the application. For debugging purposes, you might want something like:
static internal string GetVersion()
{
if (ApplicationDeployment.IsNetworkDeployed)
{
return ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();
}
return "Debug";
}
I modified my .csproj file to update the assembly version. I created a configuration called "Public Release" for this, but it's not required to do that.
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- 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>
-->
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<MSBuildCommunityTasksPath>$(SolutionDir)Tools\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
</PropertyGroup>
<!-- Required Import to use MSBuild Community Tasks -->
<Import Project="$(SolutionDir)Tools\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" Condition="'$(BuildingInsideVisualStudio)' == 'true'" />
<Target Name="BeforeCompile" Condition="'$(BuildingInsideVisualStudio)|$(Configuration)' == 'true|PublicRelease'">
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output TaskParameter="OutputVersion" PropertyName="AssemblyVersionToUse" />
</FormatVersion>
<AssemblyInfo CodeLanguage="CS" OutputFile="$(ProjectDir)Properties\VersionInfo.cs" AssemblyVersion="$(AssemblyVersionToUse)" AssemblyFileVersion="$(AssemblyVersionToUse)" />
</Target>
The published version may be:
ApplicationDeployment.CurrentDeployment.CurrentVersion
I would like to expand on Sylvanaar's answer, as some of implementation details weren't obvious to me. So:
Manually install community build tasks found at: https://github.com/loresoft/msbuildtasks/releases Note: Don't install by nuget if you clean your packages, as the build will fail before getting a chance to restore the packages, since msbuildtasks are referenced as a task in the build file. I put these in folder next to solution file called .build
Add a completely empty file to your projects properties folder called VersionInfo.cs
3 Remove these lines if they exist in AssemblyInfo.cs
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.*")]
4 Modify your csproj file
<!-- Include the build rules for a C# project. -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!--INSERT STARTS HERE-->
<!--note the use of .build directory-->
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<MSBuildCommunityTasksPath>$(SolutionDir)\.build\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
</PropertyGroup>
<!-- Required Import to use MSBuild Community Tasks -->
<Import Project="$(SolutionDir)\.build\MSBuild.Community.Tasks.targets" Condition="'$(BuildingInsideVisualStudio)' == 'true'" />
<Target Name="BeforeCompile" Condition="'$(BuildingInsideVisualStudio)|$(Configuration)' == 'true|Release'">
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output TaskParameter="OutputVersion" PropertyName="AssemblyVersionToUse" />
</FormatVersion>
<AssemblyInfo CodeLanguage="CS" OutputFile="$(ProjectDir)Properties\VersionInfo.cs" AssemblyVersion="$(AssemblyVersionToUse)" AssemblyFileVersion="$(AssemblyVersionToUse)" />
</Target>
5 Use a method like the following to access the version text:
public string Version()
{
Version version = null;
if (ApplicationDeployment.IsNetworkDeployed)
{
version = ApplicationDeployment.CurrentDeployment.CurrentVersion;
}
else
{
version = typeof(ThisAddIn).Assembly.GetName().Version;
}
return version.ToString();
}
I modified sylvanaar's solution for use with VB:
- Microsoft.VisualBasic.targets instead of Microsoft.CSharp.targets
- CodeLanguage="VB" instead of CodeLanguage="CS"
- AssemblyInfo.vb instead of VersionInfo.cs
, differences in paths:
- $(SolutionDir).build instead of $(SolutionDir)Tools\MSBuildCommunityTasks
- $(ProjectDir)AssemblyInfo.vb instead of $(ProjectDir)Properties\VersionInfo.cs
, and to remove conditions:
- Condition="'$(BuildingInsideVisualStudio)' == 'true'"
- Condition="'$(BuildingInsideVisualStudio)|$(Configuration)' == 'true|PublicRelease'"
I also synchronized Company and Product with ClickOnce PublisherName and ProductName respectively and generated a Copyright based on the current date:
- AssemblyCompany="$(PublisherName)"
- AssemblyProduct="$(ProductName)"
- AssemblyCopyright="© $([System.DateTime]::Now.ToString(`yyyy`)) $(PublisherName)"
I ended up adding this to my vbproj file. It requires the MSBuildTasks NuGet package to be installed first:
<Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" />
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<MSBuildCommunityTasksPath>$(SolutionDir).build</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets" Condition="'$(BuildingInsideVisualStudio)' == 'true'" />
<Target Name="BeforeCompile">
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output TaskParameter="OutputVersion" PropertyName="AssemblyVersionToUse" />
</FormatVersion>
<AssemblyInfo CodeLanguage="VB" OutputFile="$(ProjectDir)AssemblyInfo.vb" AssemblyVersion="$(AssemblyVersionToUse)" AssemblyFileVersion="$(AssemblyVersionToUse)" AssemblyCompany="$(PublisherName)" AssemblyProduct="$(ProductName)" AssemblyCopyright="© $([System.DateTime]::Now.ToString(`yyyy`)) $(PublisherName)"/>
</Target>
I'm not sure how much the location within the project file matters, but I added this to the end of my project file, just before:
</Project>
I did it the other way around, used a wildcard for my assembly version - 1.0.* - so Visual Studio/MSBuild generated a version number automatically:
// AssemblyInfo.cs
[assembly: AssemblyVersion("1.0.*")]
And then I added the following AfterCompile target to the ClickOnce project to assign synchronize PublishVersion with the assembly version:
<Target Name="AfterCompile">
<GetAssemblyIdentity AssemblyFiles="$(IntermediateOutputPath)$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="TargetAssemblyIdentity" />
</GetAssemblyIdentity>
<PropertyGroup>
<PublishVersion>%(TargetAssemblyIdentity.Version)</PublishVersion>
</PropertyGroup>
</Target>