3

I am developing a suite of UI tests using Selenium. One of the run-time dependencies of this suite is the chromedriver.exe, which we are expected to consume through the Selenium.WebDriver.ChromeDriver NuGet package.

The old world

When this NuGet package is imported the following lines are injected into the csproj file:

<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\Selenium.WebDriver.ChromeDriver.2.44.0\build\Selenium.WebDriver.ChromeDriver.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Selenium.WebDriver.ChromeDriver.2.44.0\build\Selenium.WebDriver.ChromeDriver.targets'))" />
</Target>
<Import Project="..\packages\Selenium.WebDriver.ChromeDriver.2.44.0\build\Selenium.WebDriver.ChromeDriver.targets" Condition="Exists('..\packages\Selenium.WebDriver.ChromeDriver.2.44.0\build\Selenium.WebDriver.ChromeDriver.targets')" />

And it is automatic by the Visual Studio. This covers our bases, making sure the build targets provided by the Selenium.WebDriver.ChromeDriver package are there at the time of the build and running them as needed. The logic inside the build targets file copies/publishes the chromedriver.exe to the right location.

All is green.

The new world.

I consume the same NuGet package as PackageReference in the csproj file. Cool. However, the build targets of that package are no longer executed. See https://github.com/NuGet/Home/issues/4013. Apparently, this is by design.

I could import the targets manually, but the problem is that I will have to hard code the location where the package is restored. It is no longer restored in the packages directory in the solution, but under my windows profile. But there is no property pointing to this location and hard coding it sucks.

So, here is the version that works for me and I hate it:

<PropertyGroup>
  <MyPackagesPath>$(UserProfile)\.nuget\packages\</MyPackagesPath>
  <SeleniumWebDriverChromeDriverTargets>$(MyPackagesPath)selenium.webdriver.chromedriver\2.44.0\build\Selenium.WebDriver.ChromeDriver.targets</SeleniumWebDriverChromeDriverTargets>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="2.44.0" />
</ItemGroup>

<Target Name="EnsureChromeDriver" AfterTargets="PrepareForRun">
  <Error Text="chrome driver is missing!" Condition="!Exists('$(OutDir)chromedriver.exe')" />
</Target>
<Import Project="$(SeleniumWebDriverChromeDriverTargets)" Condition="Exists('$(SeleniumWebDriverChromeDriverTargets)') And '$(ExcludeRestorePackageImports)' == 'true'" />

Overall, the Sdk style projects are absolutely great, but this whole business of running targets from the packages is totally broken, even if it is by design.

What am I missing?

EDIT 1

So, here is the content of the generated obj\UITests.csproj.nuget.g.targets:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
  </PropertyGroup>
  <ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
    <Import Project="$(NuGetPackageRoot)selenium.webdriver.chromedriver\2.44.0\build\Selenium.WebDriver.ChromeDriver.targets" Condition="Exists('$(NuGetPackageRoot)selenium.webdriver.chromedriver\2.44.0\build\Selenium.WebDriver.ChromeDriver.targets')" />
  </ImportGroup>
</Project>

Notice the ImportGroup condition is '$(ExcludeRestorePackageImports)' != 'true'. Now, this condition is always false, because ExcludeRestorePackageImports seems to be hard coded to be true in

c:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets

Inspecting binary log confirms this. Plus https://github.com/NuGet/Home/issues/4013 was closed as WontFix.

Or am I still missing something?

mark
  • 59,016
  • 79
  • 296
  • 580
  • the generated obj\*.props/.targets should contain the references to nuget packages' msbuild files. If it doesn't work, I rathe suspect an issue with the targets. you. could share a binary log of both the new and old builds. – Martin Ullrich Dec 08 '18 at 06:35
  • @MartinUllrich - I added a clarification. – mark Dec 08 '18 at 21:32
  • `ExcludeRestorePackageImports` should only be set during restore, so that NuGet targets don't interfere with restore (so that 1st and successive restores are equal). Can you create a binary log of a normal build? or have you been running restore and additional targets during the same invocation? – Martin Ullrich Dec 08 '18 at 22:08
  • Stupid me!!! Of course, I did it with the two targets. Yes, it works. Do you mind providing an answer? I would like to credit you. – mark Dec 08 '18 at 22:30
  • When you have time - https://stackoverflow.com/questions/53687694/msbuild-packagereference-privateassets-all-does-not-seem-to-work Thank you so much! – mark Dec 08 '18 at 22:40

1 Answers1

2

If you are running Restore and other targets during the build, you may get unexpected results due to NuGet modifying xml files on disk or because MSBuild files imported by NuGet packages aren't imported correctly.

Martin Ullrich
  • 94,744
  • 25
  • 252
  • 217
  • I found your note about the `/restore` switch the most useful, it did not exist before. If you could add it to your answer it would be very useful for others. – mark Dec 09 '18 at 15:17