Use nuspec version information from custom target - msbuild

I'm trying to use the dotnet CLI to pack and publish packages for one of our solutions. I've created a target to do this so that each project in the solution can use it. The target looks like this:
<Target Name="PackAndPublishPackages" AfterTargets="AfterBuild" Condition=" '$(Configuration)' == 'Release' And '$(IsDesignTimeBuild)' != 'false' ">
<PropertyGroup>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<NuspecProperties>Configuration=$(Configuration);Platform=$(Platform)</NuspecProperties>
</PropertyGroup>
<Exec Command="del $(MSBuildProjectDirectory)\$(PackageOutputPath)*.nupkg" />
<Exec Command="dotnet pack $(MSBuildProjectDirectory)\$(MSBuildProjectName).csproj --no-build" />
<Exec Command="dotnet nuget push $(MSBuildProjectDirectory)\$(PackageOutputPath)*.nupkg -k key -s http://url/" />
</Target>
The target runs as I would expect each time, but it doesn't appear to use the version information from the provided nuspec file or the listed nuspec properties defined in the property group when running dotnet pack. I've confirmed that the file exists in the directory for each project.

You defined the PropertyGroup at the scope of your target, which only set the property when the target runs.
Did you try to move it outside of the target?
<PropertyGroup>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
<NuspecProperties>Configuration=$(Configuration);Platform=$(Platform</NuspecProperties>
</PropertyGroup>
<!-- later in your csproj -->
<Target Name="PackAndPublishPackages" AfterTargets="AfterBuild" Condition=" '$(Configuration)' == 'Release' And '$(IsDesignTimeBuild)' != 'false' ">
<Exec Command="del $(MSBuildProjectDirectory)\$(PackageOutputPath)*.nupkg" />
<Exec Command="dotnet pack $(MSBuildProjectDirectory)\$(MSBuildProjectName).csproj --no-build" />
<Exec Command="dotnet nuget push $(MSBuildProjectDirectory)\$(PackageOutputPath)*.nupkg -k key -s http://url/" />
</Target>

Related

How to create custom project file that works with fast-up-to-date (and avoids other problems)?

I am trying to create a project file that performs few custom steps (specifically, it "wraps" existing Angular CLI project).
Here is my best attempt (myproject.csproj):
<Project ToolsVersion="Current" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{...some-guid...}</ProjectGuid>
<!-- do not include files by default -->
<EnableDefaultItems>false</EnableDefaultItems>
<!-- this removes 'Publish...' menu in VS -->
<OutputType>Library</OutputType>
<!-- output directory name -->
<AngularProject>MyWebFiles</AngularProject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<ItemGroup>
<AngularFile Include="**" Exclude="node_modules\**" />
</ItemGroup>
<Target Name="Build" Inputs="#(AngularFile)" Outputs="$(OutputPath)$(AngularProject)\index.html">
<Exec Command="ng build --no-progress --output-path $(OutputPath)$(AngularProject)\" Condition="'$(Configuration)'=='Debug'" />
<Exec Command="ng build --no-progress --output-path $(OutputPath)$(AngularProject)\ --prod" Condition="'$(Configuration)'=='Release'" />
</Target>
<Target Name="Clean">
<RemoveDir Directories="$(OutputPath)$(AngularProject)\" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
</Project>
Everything works fine, I can add this project to VS2019 solution, compile, etc. But it has problems:
Fast up-to-date check doesn't work. Related logging produces this:
Build started...
1>Project 'myproject' is not up to date. Error (0x8000FFFF).
I've tried specifying fast up-to-date files manually (via UpToDateCheckInput, etc), but it didn't work (presumably because it relies on additional definitions pulled in when you specify Sdk attribute of Project tag).
VS configuration manager has empty 'Platform' combo box. I'd like to be able to have x64 in it:
it is rather obvious that PlatformTarget is getting ignored by VS.
Opening project in VS results in creation of obj\x64\Debug\TempPE\ directory (if current Configuration is Debug). Nothing ever gets generated in it -- would be nice to avoid it being created.
Is it possible to fix these 3 problems? I suspect relates subsystems expect certain values/properties to be generated, I've tried digging in .props/.targets that come with VS in attempt to locate them, but quickly got lost.
Here is how to do it:
<Project Sdk="Microsoft.Build.NoTargets/3.2.14">
<ItemGroup>
<PackageReference Include="Microsoft.Build.NoTargets" Version="3.2.14" />
</ItemGroup>
<PropertyGroup>
<!-- Any target framework you want as long as its compatible with your referenced NuGet packages -->
<TargetFramework>net462</TargetFramework>
<Platforms>x64</Platforms>
<!-- Do not add TargetFramework to OutputPath -->
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<!-- Do not expect pdb files to be generated (this is for fast up-to-date check) -->
<DebugType>None</DebugType>
<!-- Do not include files by default -->
<EnableDefaultItems>false</EnableDefaultItems>
<!-- Output subdir name -->
<AngularProject>MyWebFiles</AngularProject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutputPath>..\..\Bin\Debug\</OutputPath>
<BuildCommand>ng build --no-progress --output-path $(OutputPath)$(AngularProject)\</BuildCommand>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutputPath>..\..\Bin\Release\</OutputPath>
<BuildCommand>ng build --no-progress --output-path $(OutputPath)$(AngularProject)\ --prod</BuildCommand>
</PropertyGroup>
<ItemGroup>
<None Include="**" Exclude="node_modules\**;$(BaseIntermediateOutputPath)\**;$(MSBuildProjectFile)" />
<!-- This deals with fast up-to-date checks -->
<UpToDateCheckBuilt Original="package-lock.json" Include="node_modules/.build" />
<UpToDateCheckInput Include="#(None);$(MSBuildProjectFile)" Set="AngularFiles" />
<UpToDateCheckOutput Include="$(OutputPath)$(AngularProject)\index.html" Set="AngularFiles" />
</ItemGroup>
<Target Name="InitModules" Inputs="package-lock.json" Outputs="node_modules/.build">
<Exec Command="npm ci --no-progress --no-color" YieldDuringToolExecution="true" />
<Exec Command="cd . > node_modules/.build" />
</Target>
<Target Name="BuildAngular" BeforeTargets="AfterBuild" Inputs="#(None);$(MSBuildProjectFile)" Outputs="$(OutputPath)$(AngularProject)\index.html" DependsOnTargets="InitModules">
<Exec Command="$(BuildCommand)" YieldDuringToolExecution="true" />
</Target>
<Target Name="CleanAngular" BeforeTargets="AfterClean">
<RemoveDir Directories="$(OutputPath)$(AngularProject)\" />
</Target>
</Project>
Notes:
it will still generate additional local directory (obj), but it can be moved away by overriding IntermediateOutputPath

Target with AfterTargets="Publish" executes in unpublishable project

I have a project in my solution that I wanna publish separately from the rest of the solution. So the way to skip it is by setting the IsPublishable property to false, which works like a charm. It seems though that no matter the publishable status of the project, targets set to run after the publish target (AfterTargets="Publish") are still executed when I try to publish the entire solution.
Is this intended? Is there any way to prevent this? I am using VS 2022 preview.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<LangVersion>9.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<BaseOutputPath>..\Build</BaseOutputPath>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<SomeFiles Include="$(SolutionDir)SomeFiles\**\*.txt" />
</ItemGroup>
<Target Name="CopyCustomContentBuild" AfterTargets="AfterBuild">
<Copy SourceFiles="#(SomeFiles)" DestinationFolder="$(TargetDir)SomeFiles" />
<Message Text="Files copied successfully." Importance="high" />
</Target>
<Target Name="CopyCustomContentPublish" AfterTargets="Publish">
<Copy SourceFiles="#(SomeFiles)" DestinationFolder="$(PublishDir)SomeFiles" />
<Message Text="Files copied successfully to publish dir." Importance="high" />
</Target>
</Project>
That is the intended behaviour. When you set IsPublishable to false MsBuild still logs when a Publish target is supposed to run and continues onto your AfterTargets="Publish" target.
You'll have to set a condition on your actions inside the target to make sure they do not get executed when IsPublishable is false.
<Target Name="CopyCustomContentPublish" AfterTargets="Publish">
<Copy SourceFiles="#(SomeFiles)" DestinationFolder="$(PublishDir)SomeFiles" Condition=" '$(IsPublishable)' == 'true' " />
<Message Text="Files copied successfully to publish dir." Importance="high" Condition=" '$(IsPublishable)' == 'true' " />
</Target>

Wix bootstrapper - Set version number in Bundle

I've got a Wix installer that uses a bootstrapper to launch my msi file. I've done this by calling a batch file as a post build event in my wix project. This then calls candle and light manually and passes various variables into the Bundle.wxs file. This all works and generates the exe which calls my msi file..
However, I now want to pass the msi BuildVersion into the bundle file. In the wxs file that creates the msi I am using the BuildVersion that I have setup in the BeforeBuild section, using the BuildVersion=%(AssemblyVersion.Version).
I cannot access this variable no matter what I try, in order to pass it to my build_bootstrapper.bat file. I can however pass in hardcoded values. I am currently setting up my own AssemblyVersionNumber enviornment variable as you can see below in the AfterBuild section:
<AssemblyVersionNumber Condition="'$(AssemblyVersionNumber)' == ''">$(BuildVersion)</AssemblyVersionNumber>
but it is empty by the time it gets to my script file (even though it's populated if hardcoded). I've tried everything.
Does anybody have any ideas of how I can get the %(AssemblyVersion.Version); to my command file from the post build step?
Thanks in advance
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="..\..\App\AppThing\bin\Release\AppThing.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version);</DefineConstants>
</PropertyGroup>
</Target>
<Target Name="AfterBuild">
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version);</DefineConstants>
<AssemblyVersionNumber Condition="'$(AssemblyVersionNumber)' == ''">$(BuildVersion)</AssemblyVersionNumber>
</PropertyGroup>
</Target>
<PropertyGroup>
<PreBuildEvent>$(ProjectDir)scripts\copy_services.bat $(SolutionDir) $(ProjectDir)</PreBuildEvent>
</PropertyGroup>
<Target Name="AfterClean">
<Message Text="Cleaning wix files, TargetDir is: $(TargetDir)" Importance="High" ContinueOnError="true" />
<CreateItem Include="$(TargetDir)\**\*.*">
<Output TaskParameter="Include" ItemName="BinFilesDir" />
</CreateItem>
<Delete Files="#(BinFilesDir)" />
</Target>
<PropertyGroup>
<PostBuildEvent>$(ProjectDir)scripts\build_bootstrapper.bat $(ProjectDir) $(ConfigurationName) $(AssemblyVersionNumber)</PostBuildEvent>
</PropertyGroup>
$(BuildVersion) isn't set to anything.
You're setting define constants to "BuildVersion=%(AssemblyVersion.Version)" but never actually defining a MSBuild property called "BuildVersion" so the value of $(BuildVersion) is "".
Use %(AssemblyVersion.Version).
<AssemblyVersionNumber Condition="'$(AssemblyVersionNumber)' == ''">%(AssemblyVersion.Version)</AssemblyVersionNumber>

Current build number not being considered during tfsbuild

Something is not right. i am trying to build and deploy thru the below code snippet, but it so happens that the current build doesnt get deployed, whereas if i give a build number older than a current build, that gets deployed. I am puzzled what is wrong ... Can you please help me ...
I am not sure why the current buildnumber is not being considered ...
<PropertyGroup>
<deployappsvr>\\vdev\$(HostedFolder);\\vdev2\$(HostedFolder)</deployappsvr>
<prjbin>Release\_PublishedWebsites\RE.Service</prjbin>
</PropertyGroup>
<Target Name ="AfterEndToEndIteration" Condition=" '$(IsDesktopBuild)'!='true' ">
<!-- Starting deployment to servers -->
<Message Text="Starting deployment to servers" />
<CallTarget Targets="DeployBatching" />
<Message Text="finished deploying to servers" />
<!-- Unmap TFS mapping -->
<Exec Command="tf workfold /unmap $(tfsmap) /workspace:$(WorkspaceName) /collection:http://tfsapp:8080/tfs"/>
</Target>
<ItemGroup>
<SrcToCopy Include="$(DropLocation)\$(BuildNumber)\$(prjbin)\**\*"/>
<DestToCopy Include="$(deployappsvr)"/>
</ItemGroup>
<Target Name="DeployBatching" Outputs="%(DestToCopy.FullPath)">
<PropertyGroup>
<DestToCopy>%(DestToCopy.FullPath)</DestToCopy>
</PropertyGroup>
<RemoveDir Directories="#(DestToCopy)"/>
<MakeDir Directories="#(DestToCopy)"/>
<Message Text="111 #(SrcToCopy) 222 $(prjbin) 333 "/>
<Message Text="444 Copying source files #(SrcToCopy->'$(DestToCopy)\%(RecursiveDir)\%(Filename)%(Extension)') "/>
<Copy
SourceFiles="#(SrcToCopy)"
DestinationFiles="#(SrcToCopy->'$(DestToCopy)\%(RecursiveDir)\%(Filename)%(Extension)')"/>
<Message Text="Finished Copying source files"/>
<Exec Command="powershell Invoke-Command -computername vdev -scriptblock {md c:\buildtestfolder} > c:\power\pwrcmd.log 2>&1"/>
</Target>
I tired deploying from the build server i.e deploying build artifacts from the server copy instead of dropzone, that seem to be working but deploying from dropzone doesn't seem to be working still.

MSBUILD Unable to force PackageLocation for Package target

I'm trying to build a deployment package for my web service from msbuild just like you can do in Visual Studio by right-clicking on the project file.
The package gets created fine and is put in the /obj/Release/Packages folder under my source directory for the project file.
You should be able to specify where that package is created by setting the PackageLocation property in a PropertyGroup inside the project file. However, that is not working for me. It still puts the package in /obj/Release/Packages under the source directory.
Here is the snippet from my project file:
<Import Project="$(SrcTreeRoot)\Build\TaskInit.Tasks" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
<Import Project="$(SrcTreeRoot)\Build\TaskOverrides.Tasks" />
<PropertyGroup>
<Platform>Any Cpu</Platform>
<Configuration>Dev</Configuration>
<PackageLocation>$(PackageOutputDir)\MatrixOnCdsService\MatrixOnCdsService.zip</PackageLocation>
<PackageAsSingleFile>True</PackageAsSingleFile>
<EnablePackageProcessLoggingAndAssert>True</EnablePackageProcessLoggingAndAssert>
<!--OutDir>$(PackageOutputDir)\MatrixOnCdsService\</OutDir-->
</PropertyGroup>
We are also using a MasterBuild.proj that has the following sections:
<PackageProject Include="..\Source\AnalysisSuite\MatrixOnCdsService\MatrixOnCdsService.csproj"/>
...
<Target Name="Package">
<MSBuild Projects="#(PackageProject)" Targets="Package" Properties="Platform=$(Platform);
Configuration=$(Configuration);
DeployOnBuild=true;
DeployTarget=Package;
PackageLocation=$(PackageLocation);"/>
</Target>
TaskInit.tasks is our own custom import file that contains the PackageOutputDir property that we use to tell the project where to put the package.
My question is why is the package still being placed in the /obj/Release/Packages folder even after specifying the PackageLocation?
Stick the property group you have in a target:
<Target Name="SetValues">
<PropertyGroup>
<Platform>Any Cpu</Platform>
<Configuration>Dev</Configuration>
<PackageLocation>$(PackageOutputDir)\MatrixOnCdsService\MatrixOnCdsService.zip</PackageLocation>
<PackageAsSingleFile>True</PackageAsSingleFile>
<EnablePackageProcessLoggingAndAssert>True</EnablePackageProcessLoggingAndAssert>
<!--OutDir>$(PackageOutputDir)\MatrixOnCdsService\</OutDir-->
</PropertyGroup>
</Target>
then add this as a DependsOnTarget for your Package Target and i think you will have your values passed.
e.g. <Target Name="Package" DependsOnTargets="SetValues">
You could do the following in your MasterBuild.proj.
<Target Name="Package">
<ConvertToAbsolutePath Paths="$(PackageOutputDir)">
<Output TaskParameter="AbsolutePaths" PropertyName="Source_Dir_Abs"/>
</ConvertToAbsolutePath>
<MSBuild Projects="#(PackageProject)" Targets="Package"
properties="Platform=$(Platform);
Configuration=$(Configuration);
DeployOnBuild=false;
DeployTarget=Package;
PackageLocation=$(Source_Dir_Abs)\$(PackageProjectName).zip;
PackageAsSingleFile=true;
ExcludeFilesFromDeployment=Web.config;
_PackageTempDir=$(PackageOutputDir)\temp;">
</MSBuild>
</Target>
Where you are calling msbuild you will need to add a property that will be used in $(PackageProjectName) by doing the following:
msbuild.exe /property:PackageProjectName=$project