NuGet & MSBuild via API - not executing targets file

1.2k Views Asked by At
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="MainFrameworkRegenerateFeatures" BeforeTargets="BeforeBuild">
    <Message Importance="high" Text="Regenerating Feature Files" />
    <Exec Command="$(ProjectDir)..\packages\SpecFlow.3.0.1\tools\SpecFlow.exe generateAll $(ProjectPath) /force /verbose"
          Condition="Exists('$(ProjectDir)..\packages\SpecFlow.3.0.1\tools\SpecFlow.exe')" />
  </Target>
  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);
      MainFrameworkRegenerateFeatures;
    </BuildDependsOn>
  </PropertyGroup>
</Project>

I have the above targets file, which gets bundled into the build directory of a nuget package. When the package is installed, I can see it makes the appropriate entries in the csproj. Note - the above target file has taken many forms as I try to debug this. I originally had it without the property group, but then put the property group in after hearing that this approach may be a work around for other issues etc.

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

So this is the default installation from nuget.exe when it installs the package into a csproj.

When I run a build via Visual Studio - the target fires correctly. I see output saying 'Regenerating Feature Files' and then it tells me what feature files are regenerated.

However, when I build via a custom build tool I developed that uses the MSBuild API - it skips the MainFrameworkRegenerateFeatures altogether.

My build tool accomplishes the build via:

                ProjectCollection pc = new ProjectCollection();
                Dictionary<string, string> globalProperty = new Dictionary<string, string>();
                globalProperty.Add("nodeReuse", "false");
                BuildParameters bp = new BuildParameters(pc);
                bp.Loggers = new List<Microsoft.Build.Framework.ILogger>()
                {
                    new FileLogger() {Parameters = @"logfile=buildresult.txt", Verbosity = LoggerVerbosity.Diagnostic}
                };
                BuildRequestData buildRequest = new BuildRequestData(projectFilePath, globalProperty, "12.0",
                    new string[] { "Clean", "Build" }, null);
                BuildResult buildResult = BuildManager.DefaultBuildManager.Build(bp, buildRequest);
                BuildManager.DefaultBuildManager.Dispose();

                pc = null;
                bp = null;
                buildRequest = null;

The build succeeds, but it never executes the Target file - no feature files get regenerated.

I have a highly verbose log file that tells me the following:

Target "EnsureNuGetPackageBuildImports" in file "C:\ETRBuilder\BuildRunner\ETRun_9302082.1742918\CP.SolutionDir\CP.ProjectDir\IE.Project.csproj": Task "Error" skipped, due to false condition; (!Exists('$(SolutionDir).nuget\NuGet.targets')) was evaluated as (!Exists('C:\ETRBuilder\BuildRunner\ETRun_9302082.1742918\CP.SolutionDir\.nuget\NuGet.targets')). Task "Error" skipped, due to false condition; (!Exists('..\packages\MainFramework.0.15.0\build\MainFramework.targets')) was evaluated as (!Exists('..\packages\MainFramework.0.15.0\build\MainFramework.targets')). Done building target "EnsureNuGetPackageBuildImports" in project "IE.Project.csproj".

Why would this work in visual studio, but fail in MSBuild?

Any ideas on how to solve this?

EDIT 1: When running MSBuild.exe against the solution file manually, It does execute the target and regenerates feature files into test classes. It seems that it fails to do so when it is done via the API and only when it is on this particular machine.

GenerateTargetFrameworkMonikerAttribute: Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files. MainFrameworkRegenerateFeatures: Regenerating Feature Files
C:\ETRBuilder\BuildRunner\ETRun_9372271.4822016\ClientName\ProjectFolder..\packages\SpecFlow.2.0.1\tools\SpecFlow.exe generateAll C:\ETRBuilder\BuildRunner\ETRun_9372271.4822016\ClientName\ProjectFolder\Client.csproj /force /verbose Processing project: ClientProject etc....
Features\Legacy\SmokeTest\Core\Core.feature -> test updated

I am going to try and take out the MSBuild version attribute from the API call, in case it is causing it.

I tried this as a work around - but when its executed via a Service application, it still skips the targets!! However if I execute it myself manually via a command line - it works fine and regenerates the feature files... WTF is going on??

public static bool BuildWithMSBuild(string solution)
        {
            string command = solution;
            ProcessStartInfo psi = new ProcessStartInfo(PathToMSBuild());
            psi.Arguments = command;
            psi.UseShellExecute = false;
            psi.RedirectStandardOutput = true;
            Process p = new Process();
            p.StartInfo = psi;
            p.Start();
            var v = p.StandardOutput.ReadToEnd();
            Console.Write(v);
            p.WaitForExit(60000);
            File.WriteAllText("buildresult.txt",v);
            if (v.Contains("Build succeeded"))
            {
                return true;
            }
            return false;
        }

Right now, I cannot get this to work when executed via a Windows Service. It always skips the custom target.

I am having to integrate the Regeneration step into my build task via Process.Start on the specflow.exe.

1

There are 1 best solutions below

1
C.J. On

Are you also programatically doing a nuget restore too?

Since it looks like this is evaluating to false:

Exists('$(SolutionDir).nuget\NuGet.targets')