dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.23k stars 1.35k forks source link

Exec task fails in Windows Nano Server docker containers using MSBuild 15.3 #2273

Closed natemcmaster closed 7 years ago

natemcmaster commented 7 years ago

Something in the Exec task is failing to launch basic command line processes in MSBuild 15.3, but only when running in Nano Server docker containers. This worked fine in MSBuild 15.1.

Repro Install Docker for Windows and ensure "Windows containers" are used (not Linux containers).

Create a dockerfile like this and a file named test.proj.

FROM microsoft/dotnet-nightly:2.0.0-preview3-sdk-nanoserver
WORKDIR /app
COPY test.proj .
RUN dotnet msbuild test.proj
<!-- test.proj -->
<Project>
    <Target Name="Build">
        <Exec Command="dir" />
    </Target>
</Project>

Execute

docker build .

Expected Build target runs and executed the "dir" command.

Actual The dotnet msbuild test.proj command fails with:

C:\app\test.proj(3,9): error MSB6004: The specified task executable location "C:\Users\ContainerAdministrator\cmd.exe" is invalid.

Details

MSBuild version: 15.3.246.41955 for .NET Core Docker base image: microsoft/nanoserver:10.0.14393.1358 OS: Windows 10 Docker: 17.06 Environment variables inside the container:

PATH = C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Users\ContainerAdministrator\AppData\Local\Microsoft\WindowsApps;C:\Program Files\dotnet;
USERPROFILE = C:\Users\ContainerAdministrator

You can gather logs by launching a container with a volume mount to the folder containing the test.proj file.

docker run --rm -v "$(get-location):C:/app" -it -w C:/app microsoft/dotnet-nightly:2-sdk
C:\app> dotnet msbuild test.proj /bl

cmd.exe itself runs run, and is located in C:\Windows\system32\cmd.exe. There is no file named C:\Users\ContainerAdministrator\cmd.exe

Full diagnostic log: msbuild-2-sdk.txt

For comparision This works just fine using MSBuild 15.1. Example

FROM microsoft/dotnet:1-sdk

WORKDIR /app
COPY test.proj .
RUN dotnet msbuild test.proj

msbuild-1-sdk.txt

natemcmaster commented 7 years ago

A clue: Environment.GetFolderPath(Environment.SpecialFolder.System) returns an empty string in Nano on .NET Core 2.0.0-preview3-25502-01. https://github.com/dotnet/corefx/issues/22049

rainersigwald commented 7 years ago

Yeah, that sure looks like the problem.

rainersigwald commented 7 years ago

I'm less sure now. We have our own implementation of the locate-the-special-folder code.

I made this local change:

diff --git a/src/Shared/FileUtilities.GetFolderPath.cs b/src/Shared/FileUtilities.GetFolderPath.cs
index 315354fc..613cafbd 100644
--- a/src/Shared/FileUtilities.GetFolderPath.cs
+++ b/src/Shared/FileUtilities.GetFolderPath.cs
@@ -35,6 +35,8 @@ namespace Microsoft.Build.Shared
                                                       Win32Native.SHGFP_TYPE_CURRENT, /* dwFlags:   [in] retrieve current path */
                                                       sb);                            /* pszPath:   [out]resultant path */

+            Console.WriteLine($"ShGetFolderPath for {folder} returned {hresult} and gave up {sb.ToString()}");
+
             String s;
             if (hresult < 0)
             {
diff --git a/src/Utilities/ToolTask.cs b/src/Utilities/ToolTask.cs
index 38dcad86..985ccc6b 100644
--- a/src/Utilities/ToolTask.cs
+++ b/src/Utilities/ToolTask.cs
@@ -619,6 +619,8 @@ namespace Microsoft.Build.Utilities
                 // Otherwise, try to find the tool ourselves.
                 pathToTool = GenerateFullPathToTool();

+                Console.WriteLine($"** {pathToTool}");
+
                 // We have no toolpath, but we have been given an override
                 // for the tool exe, fix up the path, assuming that the tool is in the same location
                 if (pathToTool != null && !String.IsNullOrEmpty(_toolExe))

And get this output when plunking current 15.3 bits on top of CLI 1.0.4:

ShGetFolderPath for LocalApplicationData returned 0 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for LocalApplicationData returned -2147024693 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for ApplicationData returned 0 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for ProgramFiles returned -2147024693 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for ProgramFilesX86 returned 0 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for System returned 0 and gave up C:\Users\ContainerAdministrator
** C:\Users\ContainerAdministrator\cmd.exe
C:\app\test.proj(3,9): error MSB6004: The specified task executable location "C:\Users\ContainerAdministrator\cmd.exe" is invalid.

That should isolate against the runtime, but it's still failing.

rainersigwald commented 7 years ago

Worked on MSBuild v15.3.118.39484

C:\app>dotnet msbuild test.proj
Microsoft (R) Build Engine version 15.3.118.39484
Copyright (C) Microsoft Corporation. All rights reserved.

ShGetFolderPath for LocalApplicationData returned 0 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for LocalApplicationData returned -2147024693 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for ApplicationData returned 0 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for ProgramFiles returned -2147024693 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for ProgramFilesX86 returned 0 and gave up C:\Users\ContainerAdministrator
ShGetFolderPath for System returned -2147024693 and gave up C:\Users\ContainerAdministrator
** cmd.exe
rainersigwald commented 7 years ago

Apparently a regression from c113d5be19016ceef76b55107e6b16e0b920aabd (thanks, git bisect!). Investigating why.

rainersigwald commented 7 years ago

Related: https://github.com/dotnet/corefx/issues/19110. CoreFX has similar calls and is getting bad results too--though that doesn't explain why this apparently started failing with a specific commit in MSBuild.

rainersigwald commented 7 years ago

We can work around the problem by changing Exec.GenerateFullPathToTool to return %ComSpec% instead of ToolLocationHelper.GetPathToSystemFile("cmd.exe"). I'm not sure of the impact of changing the other calls, and would like to avoid doing this on any other environment given how late it is in the release, so I'm trying to find a good way to do that only on Nano Server.

natemcmaster commented 7 years ago

Thanks @rainersigwald. I've verified this has been fixed in the aspnetcore Nano Server docker images.