简体   繁体   中英

Resolve .NET 4.7 external project NuGet dependencies via MSBuild targets

We need to get these projects to build and they have some strange practices. They rely on linked code files that are generated as the result of another project's build process. These projects are situated in a sub-folder.

Thus, before building our projects we need to build the sub-project. This is done by adding a custom build target to each project's BeforeBuild target, and seems to work. However, the sub-project won't build without having it's NuGet dependencies restored.

To clarify: We know that this pattern is totally bonkers, but we don't have anything like the manpower to refactor every project that uses this pattern. We're just trying to get them to compile reliably in one step.

Each of these external project dependencies is listed in a custom ItemGroup, like so:

<ItemGroup> <ExternalDependantProjects Include= "..\\<subfolder>\\<project>\\<project>.csproj" /> </ItemGroup>

We've been trying to solve this by adding custom build targets that are called by each project's BeforeBuild target. We first tried using ResolveNuGetPackageAssets as a build target but discovered that this is only supported in netcore, while we are targeting net47.

Now we're trying to write a custom build target that will restore the NuGet dependencies in the external project. We've tried the straight-forward approach, like so: <Exec Command="nuget restore @(ExternalDependantProjects)" /> and a somewhat more complex & hacky PowerShell attempt: <Exec Command="powershell.exe -command &quot;&apos;@(ExternalDependantProjects, '&apos; &apos;')&apos; | foreach { nuget restore $_ }&quot;" /> <Exec Command="powershell.exe -command &quot;&apos;@(ExternalDependantProjects, '&apos; &apos;')&apos; | foreach { nuget restore $_ }&quot;" />

In both cases it simply tries to restore the packages of the main project, not the external projects. It seems that @(ExternalDependantProjects) is equal to blank, and so no new argument is added. Using has shown that @(ExternalDependantProjects) returns nothing. However, it does work as the "Projects" parameter when we call an MSBuild "Build" target. So, I suspect that we're using the Item parameter in the wrong way? Perhaps there is some syntax to access the Include property of the Item?

However, I'm not sure that it will even work if we can resolve that issue. We tested using the nuget restore command on the sub-project and always get the response that all packages in packages.conf are installed. Yet, ..\\packages\\ from that external project is empty and packages.conf specifies half a dozen packages (They're also missing from "References" in VS and the references' HintPath correctly goes to ..\\packages).

So we're confounded on three fronts: How do we reference the the ExternalDependentProjects Item include path from MSBuild targets, why won't the NuGet restore CLI work, and is this even the correct way to go about solving this problem? Have we gone down completely the wrong avenue?

NuGet discovers packages.config projects to restore from the solution file. If all projects are in the solution file and you run nuget.exe restore <solution file> they should get restored, regardless of what is happening in the project files.

As a workaround for any custom scenario you could write a script to discover all packages.config files in the folder, and then call nuget.exe restore <packages.config> -SolutionDirectory <solution root> on the files directly. For packages.config restore the only part needed is to discover the list of id/versions from the packages.config files so that they can be downloaded and extracted to the solution level packages folder.

I also suggest using Project references with ReferenceOutputAssembly=false to ensure that projects build in the order you want, and without referencing each other. See: https://blogs.msdn.microsoft.com/kirillosenkov/2015/04/04/how-to-have-a-project-reference-without-referencing-the-actual-binary/

How do we reference the the ExternalDependentProjects Item include path from MSBuild targets, why won't the NuGet restore CLI work, and is this even the correct way to go about solving this problem? Have we gone down completely the wrong avenue?

You should write your custom build target with the option -OutputDirectory , so the custom build target should be like below:

  <Target Name="BeforeBuild">
      <Exec Command="nuget restore @(ExternalDependantProjects) -OutputDirectory ..\<subfolder>\packages" />
  </Target>

With this target, NuGet package for external projects will be restored successfully.

Besides, the value of @(ExternalDependantProjects) is not equal to blank, you can use a target echo this value.

Below is my test sample, you can refer to some detail info:

ExternalpProject is the sub-folder name, TestExternalProject is the external project name.

<ItemGroup>
  <ExternalDependantProjects Include= "..\ExternalpProject\TestExternalProject\TestExternalProject.csproj" />
</ItemGroup> 

<Target Name="BeforeBuild">
  <Message Text="Restore package for Externalp Project" Importance="high"></Message>
  <Exec Command="nuget restore @(ExternalDependantProjects) -OutputDirectory ..\ExternalpProject\packages" />
</Target>

<Target Name="AfterBuild">
  <Message Text="Display the value of ExternalDependantProjects" Importance="high"></Message>
  <Message Text="@(ExternalDependantProjects)"></Message>
</Target>

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM