In my company we have some home made tools that are used in the build process when building other projects.
I need to use these tools in VS2017 BeforeBuild and AfterBuild scripts and it must work in MS Build as well.
The tools are distributed as NuGet packages and most of our projects are ported to PackageReference instead of Packages.config
I know that the current installation of MyTool (version X.Y.Z) is at C:\Users\Me\.nuget\packages\MyTool\X.Y.Z, but how do I reference it in my project file, so it also works when the next version is released?
I think C:\Users\Me\.nuget\packages can be replaced with $(NuGetPackageRoot), but what to do to always reference the version installed in the project?
Some Nuget packages seem to put contributions into MyProject.csproj.nuget.g.props and MyProject.csproj.nuget.g.targets in the obj folder, but I can find very little useful information about these files.
Inside a target, you can use this to create a property based on an item:
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
</ItemGroup>
<Target Name="PrintStuff" AfterTargets="AfterBuild">
<PropertyGroup>
<NewtonsoftJsonVersion Condition="'%(PackageReference.Identity)' == 'Newtonsoft.Json'">%(PackageReference.Version)</NewtonsoftJsonVersion>
<NewtonsoftJsonPath>$(NuGetPackageRoot)newtonsoft.json\$(NewtonsoftJsonVersion)\</NewtonsoftJsonPath>
</PropertyGroup>
<Message Importance="high" Text="JSON.NET version: $(NewtonsoftJsonVersion)" />
<Message Importance="high" Text="JSON.NET path: $(NewtonsoftJsonPath)" />
<Exec Command="ls" WorkingDirectory="$(NewtonsoftJsonPath)" Condition="'$(OS)' != 'Windows_NT'" />
<Exec Command="dir" WorkingDirectory="$(NewtonsoftJsonPath)" Condition="'$(OS)' == 'Windows_NT'" />
</Target>
Related
Bear with me - this is an unusual scenario.
I have 4 projects in my solution. The top most project references the 3 other projects. None of the 3 other projects reference each other. So the architecture is like this:
Now, when I build project A I want it to produce a nuget package containing projects B, C and D but not project A. As this is in .NET standard I can configure the packages tab of project A to produce a nuget package automatically when it builds by checking the 'Generate NuGet package on build option.' Then, I can get it to include B, C and D by making the following changes to A's csproj file:
<ItemGroup>
<ProjectReference Include="..\B.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
<ProjectReference Include="..\C.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
<ProjectReference Include="..\D.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
<Version>1.0.0-beta</Version>
<PackageId>A</PackageId>
<Company></Company>
<Product>A</Product>
<Description></Description>
<Authors></Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'all'))" />
</ItemGroup>
</Target>
Ideally I would like to add a line to remove A.dll from the nuget package. Is this possible? A is a wrapper project which consuming code will never need to use. It is not possible for B, C and D to reference each other.
UPDATE
This is how I solved it (thanks #tom redfern)
I created a nuspec file manually:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>A</id>
<version>1.0.0-beta</version>
<authors>Foo</authors>
<owners>Bar</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>A package</description>
<dependencies>
<group targetFramework=".NETStandard2.0">
</group>
</dependencies>
</metadata>
<files>
<file src="bin\Release\netstandard2.0\B.dll" target="lib\netstandard2.0\B.dll" />
<file src="bin\Release\netstandard2.0\C.dll" target="lib\netstandard2.0\C.dll" />
<file src="bin\Release\netstandard2.0\D.dll" target="lib\netstandard2.0\D.dll" />
</files>
</package>
Then in my .csproj file for A I put the following to automatically pack it after a build:
<Target Name="__PackNuGetPackage" AfterTargets="Build">
<Exec Command="$(NugetPackage)nuget.exe pack "A.nuspec"" />
</Target>
Using patented(1) elite(2) debugging skills, we can figure out if it's possible without manually creating and maintaining a nuspec file.
First, let's start with NuGet's docs on creating a package with the dotnet CLI. It says "msbuild -t:pack is functionality equivalent to dotnet pack". So, first hint, it's just running MSBuild targets.
So, run dotnet msbuild my.csproj -pp:pp.txt. This "pre-processes" (evaluates all MSBuild import statements and writes the result into a single file) the csproj (just a standard MSBuild file). We then search for the pack target, and scroll up until we find the filename of the file that was imported. We see it's NuGet.Build.Tasks.Pack.targets, and since NuGet is open source on GitHub, I can point you to the source.
Searching NuGet.Build.Tasks.Pack.targets for Condition, to see what extensibility options the NuGet team has provided, I see <IncludeBuildOutput Condition="'$(IncludeBuildOutput)'==''">true</IncludeBuildOutput>. So, settings <IncludeBuildOutput Condition="'$(IncludeBuildOutput)'==''">false</IncludeBuildOutput> in your csproj, might work.
(1) not patented
(2) standard, but since people don't modify MSBuild files anywhere near as often as C#, the skills and tools aren't as well known
You can achieve this by using a nuspec file. Use nuspec when you need absolute control over the nuget pack process. A simple nuspec file:
<package >
<metadata>
<id>MyPackage</id>
<version>1.0</version>
<authors>Something</authors>
<owners>Something</owners>
<description>Somthing</description>
<copyright></copyright>
<dependencies>
<!-- any nuget package dependencies -->
<dependency id="AnotherPackage" version="2019.2.4.1" />
</dependencies>
</metadata>
<files>
<!-- this is where you can have complete control over which assemblies get added to your package. You can add them individually pr using wildcards. -->
<file src="..\obj\**\*.dll" target="lib" />
</files>
</package>
When you have created your .nuspec file, add it into your solution, and then make your "Nuget Pack" build step read the nuspec file rather than the project file.
I have the following project in my solution which I am trying to use to create a NuGet package:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>ExpressionTreeTestObjects</PackageId>
<Authors>Zev Spitz</Authors>
<Company />
<Product>ExpressionTreeTestObjects</Product>
<Description>A set of expression trees, and instances of other types from System.Linq.Expressions, for testing code against a variety of expression trees. The objects are generated by the C# compiler, by the VB.NET compiler, or using the factory methods at System.Linq.Expressions.Expression.</Description>
<Copyright>Copyright (c) 2019 Zev Spitz</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/zspitz/ExpressionTreeToString</PackageProjectUrl>
<RepositoryUrl>https://github.com/zspitz/ExpressionTreeToString</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>expression-tree code-generation visual-basic.net vb.net csharp test-data tostring</PackageTags>
<IncludeBuildOutput>true</IncludeBuildOutput>
<IncludeReferencedProjects>true</IncludeReferencedProjects>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ExpressionTreeTestObjects.VB\TestObjects.VB.vbproj" />
<ProjectReference Include="..\ExpressionTreeTestObjects\TestObjects.csproj" />
</ItemGroup>
</Project>
Build and pack both seem to work. However, the .nupkg file doesn't seem to include the referenced DLLs.
How can I troubleshoot this? How can I resolve it?
I think the solution can be found here:
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
I have a few solutions that started out in VS2008 or VS2010 or something like that. They contain some stuff that I think relate to NuGet package restore and including certain MS NuGet packages in the build process.
But this looks different in different projects, while I see no reason for any differences, and a new VS2017 project contains yet another different version.
So, what could I safely do to get my old projects up to date in this regard - what should it look like now, in VS2017?
Both old solutions contain a solution-level subfolder ".nuget" with three files: NuGet.Config, NuGet.exe and NuGet.targets.
The old .csproj files contain these "versions" of NuGet stuff:
Proj1:
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
Proj2 (additional conditions):
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
Proj3 (doesn't import nuget.targets):
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
A new VS2017 web application project (doesn't import nuget.targets or Microsoft.Bcl.Build.1.0.21, different <Error Condition.../> tags):
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
</Target>
I would like to have the same setup for all these old projects (unless there would be specific reasons to differ), and I want the setup to be up to date with the tools and environment as per VS2017.
I have automatic package restore enabled and want to keep it that way.
Note: All four projects seem to build and run successfully. Proj2 and Proj3 are unit test projects. Proj1 is really two projects that are identical in this respect.
What's the current impl. of NuGet stuff in .csproj?
To resolve this question, you can follow below steps:
Close Visual Studio to avoid file potential file locks and conflicts.
If using TFS: Remove nuget.exe and nuget.targets from the solution's .nuget folder and remove those files from the solution workspace. Retain nuget.config with the disableSourceControlIntegration setting as explained in Omitting packages with Team Foundation Version Control.
If not using TFS: Remove the .nuget folder from the solution and the solution workspace.
Edit each project file in the solution, remove the <RestorePackages> element, and remove any references to the nuget.targets file.
<RestorePackages>true</RestorePackages>
And
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
Note: Retain <Import Project="..\packages\Microsoft.Bcl.Build...
For some details, please refer to:
Nuget: Switching from "Enable Package Restore" to "Automatic Package Restore".
I have created a test project based on .Net Core 2 and wrote some NUnit test cases. After installing necessary NuGet packages i.e. NUnit3TestAdapter, I was able to see all test cases in "Test Explorer" and able to execute those. Now, when I looked into the project directory, I found that it's creating "obj" folder and some json files in it. So I tried to change the path of "obj" folder by modifying ".csproj" file. I provided some different path in the parameter "BaseIntermediateOutputPath" and that way, I was able to get rid of "obj" folder. The reason for providing different path was, I wanted to keep json files separate from source code.
However, after modifying that I am not able to see or execute any test cases from Test Explorer.
Is this a Microsoft bug?
Is any packages having dependency on "obj" folder?
P.S.
I am using "NUnit" and "NSubstitute" packages for my test project.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<TargetFramework>netcoreapp2.0</TargetFramework>
<OutputPath>..\..\build\$(Configuration)\UnitTests\</OutputPath>
<BaseIntermediateOutputPath>..\..\work\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<ItemGroup>
<PackageReference Include="Castle.Core" Version="4.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="NSubstitute" Version="2.0.3" />
<PackageReference Include="NUnit" Version="3.8.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<ItemGroup>
<ProjectReference Include="..\UtilityLibrary\UtilityLibrary.csproj" />
</ItemGroup>
</Project>
When .NET Core projects build, they do not copy all referenced files into the bin folder. When you add Microsoft.NET.Test.Sdk to your test project, one of the things it does is add an AssemblyResolve event handler which loads other dependent assemblies from a list of searchDirectories.
BaseIntermediateOutputPath not working was reported against the VSTest project and is an issue with MSBuild. The workaround is noted in the dotnet sdk repository. From that, you need to use Sdk imports in your csproj instead of the Sdk attribute on the Project element.
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj\XXX\</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- Body of project -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
I installed POSTSHARP as a nuget package and I want Visual Studio Online to automatically restore it.
POSTSHARP must be restored before build though.
I am trying to follow this with no success: link
How can I run scripts / commands in Visual Studio Online BEFORE build?
There are instructions on nuget.org on how to set up a package restore with TFS, including Visual Studio Online: http://docs.nuget.org/docs/reference/package-restore-with-team-build
It mentions that the default Build Process Templates for VSO already implements NuGet Package Restore workflow. So, supposedly, you need to do additional setup only when you customize the templates.
The proposed approach is to create a simple MSBuild project file that will be used to build the solution. You can include all the required targets there (e.g. Build, Rebuild, Clean) that will just invoke MSBuild on your solution file with specifying the corresponding target.
Additionally create a target for package restore - it will invoke NuGet.exe restore MySolution.sln command. The common build targets will depend on this one.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutDir>$(MSBuildThisFileDirectory)bin</OutDir>
<Configuration>Release</Configuration>
<ProjectProperties>
OutDir=$(OutDir);
Configuration=$(Configuration);
</ProjectProperties>
</PropertyGroup>
<ItemGroup>
<Solution Include="$(MSBuildThisFileDirectory)src\*.sln" />
</ItemGroup>
<Target Name="RestorePackages">
<Exec Command=""$(MSBuildThisFileDirectory)tools\NuGet\NuGet.exe" restore "%(Solution.Identity)"" />
</Target>
<Target Name="Build" DependsOnTargets="RestorePackages">
<MSBuild Targets="Build"
Projects="#(Solution)"
Properties="$(ProjectProperties)" />
</Target>
<!-- other targets... -->
</Project>