ItemGroup Item scope, alternatively "Why does MSBuild hate me?" - c#

I have a solution I'm trying to get to build on TFS. I want to update the versions of all appropriate files, and I've been stuck trying to get this done. There are plenty of links on how to do it, but none of them work for me, due to one little issue... Scope.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<Target Name="DesktopBuild">
<CallTarget Targets="GetFiles" />
<Message Text="CSFiles: '#(CSFiles)'" />
</Target>
<Target Name="GetFiles">
<ItemGroup>
<CSFiles Include="**\AssemblyInfo.cs" />
</ItemGroup>
<Message Text="CSFiles: '#(CSFiles)'" />
</Target>
</Project>
My tree looks like this:
test.proj
application.sln
application (Folder)
main.cs
Properties (Folder)
AssemblyInfo.cs
When I run "c:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe test.proj" from the solution folder... I get the following output:
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3074]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 7/6/2009 3:54:10 PM.
Project "D:\src\test.proj" on node 0 (default targets).
CSFiles: 'application\Properties\AssemblyInfo.cs'
DesktopBuild:
CSFiles: ''
Done Building Project "D:\src\test.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.04
So, how can I make my ItemGroup have global scope? All the Targets files used by the compiler and TeamBuild do this same thing, and theirs all seem to be global... I don't understand why this isn't working for me.
Any help?

Have you tried using DependsOnTarget rather than CallTarget? It could be that CallTarget is causing the scope issue.

The previous commenter was correct, you should change this to use DependsOnTargets instead of using the CallTarget task. What you are seeing is a bug not a scoping inssue. The way to avoid this bug is to use DependsOnTargets (which is a much better approach anywayz).
Sayed Ibrahim Hashimi
My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

As said, you should use DependsOnTargets. I've done some research on MSBuild scope, you can find my results on my blog : http://blog.qetza.net/2009/10/23/scope-of-properties-and-item-in-an-msbuild-script/
The thing is there seems to be a global scope for the project and a local scope for the target . When entering the target, the global scope is copied and when exiting the target, the local scope is merged back. So a CallTarget will not get the modified local scope values but the DependsOnTargets will since the first target is exited before the entering the second target.

We do something similar in our build. We pass the version as a command line parameter.
In our TFSBuild.proj we set the version to 0.0.0.0 if no version was supplied:
<!--Our assembly version. Pass it in from the command prompt like this: /property:Version=1.0.0.0-->
<PropertyGroup>
<Version>0.0.0.0</Version>
</PropertyGroup>
<PropertyGroup>
<!--Used to ensure there is a newline before our text to not break the .cs files if there is no newline at the end of the file.-->
<newLine>%0D%0A</newLine>
Then we do this:
<Target Name="BeforeCompile">
<!--Update our assembly version. Pass it in from the command prompt like this: /property:Version=1.0.0.0-->
<!--attrib needs to be run first to remove the read only attribute since files from tfs are read only by default.-->
<Exec Command='attrib -R $(SolutionRoot)\Source\Project\GlobalAssemblyInfo.cs' />
<WriteLinesToFile File="$(SolutionRoot)\Source\Project\GlobalAssemblyInfo.cs"
Lines='$(newLine)[assembly: AssemblyVersion("$(Version)")]'/>
</Target>

Related

error CS8773: "Feature 'global using directive' is not available in C# 9.0" after downgrade from net6.0 to net5.0

I have a project that was initially created for .NET 6 but then I needed to downgrade it to .NET 5.
I changed Target framework in Project Properties and tried to compile. As a result I received a bunch of the errors:
GlobalUsings.g.cs(2,1,2,29): error CS8773: Feature 'global using directive' is not available in C# 9.0. Please use language version 10.0 or greater.
File GlobalUsings.g.cs is created automatically and it reappears every time after compilation.
Finally I found that the reason is an extra property ImplicitUsings in the project file that is not supported by .net 5.0.
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
I needed to remove ImplicitUsings from the file.
remove <ImplicitUsings>enable</ImplicitUsings> in the csproj project file, then can build success
find this solution from here
Remove the tag indeed work.
But just change the value of it did the trick as well!
<ImplicitUsings>disable</ImplicitUsings>
To get rid of this error which is caused by downgrading below net6.0.
Remove the following items from the .csproj file:
<ImplicitUsings>
<Using Include="..." />
If you don't want to remove the ImplicitUsings or made changes to the project file. You can tell the build cli to disable it during build process by
dotnet build --configuration "Release" --framework "net5.0" /p:ImplicitUsings=false /p:PublishReadyToRun=false

dotnet publish profile CLI vs Visual Studio behave different

I have a dotnet core 2.1 web API project with a publish profile to a folder that lately I plan to copy to an IIS as in dotnet publish documentation.
I created the publish profile with VS and the resulting file is
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<PublishProvider>FileSystem</PublishProvider>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>b3155c62-817f-4eba-8856-e5941137f4ed</ProjectGuid>
<SelfContained>true</SelfContained>
<_IsPortable>false</_IsPortable>
<publishUrl>bin\publish\</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
</Project>
If I run the profile from VS it creates a bin\publish folder that I can copy to IIS.
If I do the same with dotnet publish /p:PublishProfile=FolderProfile (as in the CI server) after updating to latest VS I get the following error
C:\Program Files\dotnet\sdk\2.1.400\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(122,5): error NETSDK1067: Self-contained applications are required to use the application host. Either set SelfContained to false or set UseAppHost to true. [C:\Users\Guillem.Sola\source\repos\ASG.DHW.HealthCheck\ASG.DHW.HealthCheck.API\ASG.DHW.HealthCheck.API.csproj]
I can achieve something similar excuting in cmd
dotnet publish .\HealthCheck.API\HealthCheck.API.csproj -o .\bin\publish -r win-x64 -c Release
What is going on, why the profile is not behaving the same when calling from CLI?
I think that the key to your (updated) issue might be in this part of the error message:
Self-contained applications are required to use the application host. Either set SelfContained to false or set UseAppHost to true.
The difference in behaviors could easily be explained by a number of things, like MSBuild target files brought in by various VS conditions, for example.
Because you don't want to use the AppHost, you'll need to change<SelfContained>true</SelfContained> to <SelfContained>false</SelfContained>. You could also consider adding an explicit <UseAppHost>false</UseAppHost> - that might help mitigate differences between a VS and a CI build.
Turning on/up the MSBuild verbosity is a good way to get more data to help you understand why the operation has the observed results.

How to publish for all target frameworks

I am building a set of build template in TeamCity for .Net Core projects. Everything is working great, except for console projects. The problem is that when I go to publish the solution, I need to specify the framework version. At no other point in the build do I need to know the framework. At least this is true when publishing .sln files, with a console project that only has a single framework targeted.
So now I am in a situation where I need to figure out what framework the console project should target. I could read various XML files, but I'm hoping I don't need to. Is there some builtin way that I can query for the frameworks in use for a given solution?
For example, something like (PowerShell)
$frameworks = & dotnet.exe --<what I want> .\MySolution.sln
for ($framework in $frameworks) {
& dotnet.exe publish -f $framework .\MySolution.sln
}
That way I don't need to modify the build system every time a new framework is in use. I've poked around in the CLI repo, but I can't find a command that does what I need. Is opening .csproj files my only hope?
If you want to publish projects that target multiple frameworks, the default Publish target fails, but you can create a custom target that performs the multi-targeting itself. To do this, create a file named Directory.Build.props in the solution folder (with MSBuild > 15.1 this can and should be named Directory.Build.targets because there was a bug with multi-targeting projects):
<Project>
<Target Name="PublishProjectIfFrameworkSet"
DependsOnTargets="Publish"
Condition=" '$(TargetFramework)' != '' " />
<Target Name="PublishProjectForAllFrameworksIfFrameworkUnset" Condition=" '$(TargetFramework)' == '' ">
<ItemGroup>
<_PublishFramework Include="$(TargetFrameworks)" />
</ItemGroup>
<MSBuild Projects="$(MSBuildProjectFile)" Targets="Publish" Properties="TargetFramework=%(_PublishFramework.Identity)" />
</Target>
<Target Name="PublishAll"
DependsOnTargets="PublishProjectIfFrameworkSet;PublishProjectForAllFrameworksIfFrameworkUnset" />
</Project>
Then you can publish the projects for all defined frameworks by executing this in the solution directory:
$ dotnet msbuild /t:PublishAll /p:Configuration=Release
Microsoft (R) Build Engine version 15.1.1012.6693
Copyright (C) Microsoft Corporation. All rights reserved.
app2 -> /Users/martin/testproj/app2/bin/Release/netcoreapp1.1/app2.dll
app1 -> /Users/martin/testproj/app1/bin/Release/netcoreapp1.1/app1.dll
app1 -> /Users/martin/testproj/app1/bin/Release/netcoreapp1.0/app1.dll

How to run MSBuild pack target in Visual Studio 2017

My question is similar to this and this.
I want to package a .Net Framework library in Visual Studio 2017 RC. In VS 2015 with project.json build system, I was able to accomplish this by importing a custom targets file into the .xproj file. This custom targets file took care of creating the nuspec file if it didn't exist, then running nuget pack, copying the resulting packages to local feed location, etc.
Do I need to do something similar in 2017 (haven't made a serious effort yet) or can I somehow enable the pack target in my .csproj file?
The docs here only show to run the pack target from the command line.
EDIT:
I'm trying the below custom target referencing a Nuget 4.0 exe from the nightly builds...
<Target Name="PackNugets" AfterTargets="Build">
<PropertyGroup>
<NugetV4Path>$([System.IO.Path]::GetFullPath('path to nuget.exe'))</NugetV4Path>
</PropertyGroup>
<Exec Command=""$(NugetV4Path)\nuget.exe" pack "$(MSBuildProjectDirectory)\$(PackageId).csproj" -Symbols -OutputDirectory bin -Properties Configuration=Release"/>
</Target>
But I get the following error
System.InvalidCastException: Unable to cast object of type 'System.String' to type 'NuGet.Frameworks.NuGetFramework'.
at NuGet.ProjectManagement.NuGetProject.GetMetadata[T](String key)
at NuGet.ProjectManagement.PackagesConfigNuGetProject..ctor(String folderPath, Dictionary`2 metadata)
at CallSite.Target(Closure , CallSite , Type , Object , Dictionary`2 )
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
at NuGet.CommandLine.ProjectFactory.AddDependencies(Dictionary`2 packagesAndDependencies)
at NuGet.CommandLine.ProjectFactory.ProcessDependencies(PackageBuilder builder)
at NuGet.CommandLine.ProjectFactory.CreateBuilder(String basePath, NuGetVersion version, String suffix, Boolean buildIfNeeded, PackageBuilder builder)
at NuGet.Commands.PackCommandRunner.BuildFromProjectFile(String path)
at NuGet.CommandLine.PackCommand.ExecuteCommand()
at NuGet.CommandLine.Command.ExecuteCommandAsync()
at NuGet.CommandLine.Command.Execute()
at NuGet.CommandLine.Program.MainCore(String workingDirectory, String[] args)
Something to do with one of the properties in the csproj? Does NugetFramework refer to the TargetFramework?
I am targeting net452 in my csproj, in case that helps.
EDIT:
That exception is indeed about nuget attempting to parse the TargetFramework, but it is not clear whether it is failing at my csproj or at a dependency...
EDIT: The latest updates to VS2017 have added the Pack action to the project context menu, so the below action can be changed as follows:
AfterTargets=Pack
Remove the Exec element
If you are targeting multiple frameworks, you may have to insert a \$(Configuration) after the \bin in the NugetPackages Include.
Ok, this issue solved it for me. For now, we have to use dotnet instead of msbuild or nuget.
So, my custom target in the imported .targets file becomes
<Project ToolsVersion="15.0">
<Target Name="PackNugets" AfterTargets="AfterBuild">
<!-- Swap out nuget for dotnet and update the cli args -->
<!-- Might actually be able to leave out the csproj path, since
this target should run in the project directory. Test it first. -->
<Exec Command="dotnet pack "$(MSBuildProjectDirectory)\$(PackageId).csproj" --no-build --include-symbols -o bin -c Release"/>
<!-- If you want to copy to a local feed -->
<ItemGroup>
<NugetPackages Include="$(MSBuildProjectDirectory)\bin\$(PackageId).$(PackageVersion)*.nupkg"/>
</ItemGroup>
<Copy SourceFiles="#(NugetPackages)" DestinationFolder="path to local feed" />
</Target>
</Project>
I was wanting to know the same thing, so I went spelunking in the MSBuild files, namely: C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Sdks\NuGet.Build.Tasks.Pack\build and look for the "Pack" target.
Beyond that, I would refer to general MSBuild command line syntax here, but a few examples:
msbuild.exe -t:Pack -p:IncludeSymbols=true
msbuild.exe -t:Pack -p:Configuration=Release -p:VersionSuffix="alpha" # or "-alpha" - not sure
EDIT: A little more research and work on my scenario and I found this documentation of the important properties.
Please note that dotnet pack or msbuild /t:Pack currently doesn't support .NET Framework projects. They only work NETCore projects.
You can even improve Ken G answer if you want to push the nuget using this:
<Target Name="PushNugetPackage" AfterTargets="Pack" Condition="'$(Configuration)' == 'Release'">
<Exec Command="nuget.exe push -Source "mysource" -ApiKey VSTS $(OutputPath)..\$(PackageId).$(PackageVersion).nupkg" />
</Target>
It will only run after you choose pack from context menu and configuration is Release, so you don't push debug packages, remove that condition if you really don't need it
Source

How to set LARGEADRESSAWARE in no signing assembly projects for a ClickOnce app?

I have an application with the next two Post-Compilation commands:
call editbin /LARGEADDRESSAWARE $(TargetPath)
call editbin /LARGEADDRESSAWARE $(ProjectDir)obj\$(PlatformName)\$(ConfigurationName)\$(TargetFileName)
and works fine.
But when I publish into a server as the ClickOne Application works with no errors but when I try install in a client the hash of file is different than the value calculated in the manifest.
I tryed to use the next command:
sn -Ra $(ProjectDir)obj\$(PlatformName)\$(ConfigurationName)\$(TargetFileName) PublicPrivateKeyFile.snk
but does not work and it shows the next message:
app.exe does not represent any strong-named assembly.
I suppose it's because all my projects has the "signing the assembly" option with false value. Before using LARGEADDRESSAWARE the ClickOnce Application worked fine.
It is necesary to set the "signing the assembly" option with true value for all projects or are there any way to use LARGEADDRESSAWARE with false value for this option?
EDIT:
Solution of Mark Sowul works fine:
Also I added in AfterBuild the next lines in order to check if the AfterCompile works fine
call "$(VS110COMNTOOLS)vsvars32.bat"
dumpbin /headers "$(TargetPath)" > "$(TargetPath).info"
findstr "(>2GB)" "$(TargetPath).info"
set BUID_ERRORLEVEL=%ERRORLEVEL%
del "$(TargetPath).info"
if [%BUID_ERRORLEVEL%]==[0] echo EXE program updated to use more than 2GB
if [%BUID_ERRORLEVEL%]==[1] echo ERROR: EXE PROGRAM WAS NOT UPDATED TO USE MORE THAN 2GB
set ERRORLEVEL=%BUID_ERRORLEVEL%
Post Build events are too late in the game. You have found this out to an extent, as I did, by realizing you need to alter the file in obj.
However, Post Build occurs after the manifest generation. So editing the obj file there is too late. The better place to do it is in the AfterCompile build target.
You'll have to edit the csproj; add after the lines you'll already see for this:
<!-- 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>
-->
**<Target Name="AfterCompile">
<Exec Command="
call "$(VS110COMNTOOLS)\vsvars32.bat"
editbin /largeaddressaware "$(IntermediateOutputPath)$(TargetFileName)"
" />
</Target>**

Categories