Skip to content

Commit 341a962

Browse files
authored
dotnet-local & net8: local dotnet first in PATH (#8222)
The .NET 8 `dotnet` command appears to require that `dotnet` be inThe .NET 8 `dotnet` command appears to require that `dotnet` be in `$PATH`, lest it invoke a *different* `dotnet`. This can result in weird error messages: % ~/Developer/src/xamarin/xamarin-android/dotnet-local.sh build *.csproj … …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : You must install or update .NET to run this application. …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : App: …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/bincore/csc.dll …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : Architecture: x64 …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : Framework: 'Microsoft.NETCore.App', version '8.0.0-rc.1.23371.3' (x64) …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : .NET location: /usr/local/share/dotnet/ …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : The following frameworks were found: …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : 6.0.16 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : 7.0.3 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : 7.0.4 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : 7.0.5 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : 7.0.9 at [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : Learn about framework resolution: …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : https://aka.ms/dotnet/app-launch-failed …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : To install missing framework, download: …/dotnet/sdk/8.0.100-rc.1.23373.1/Roslyn/Microsoft.CSharp.Core.targets(80,5): error : https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=8.0.0-rc.1.23371.3&arch=x64&rid=osx.13-x64 … To focus on the weirdness: `dotnet-local.sh` uses the .NET 8 that we provision into `bin/$(Configuration)/dotnet`, which is .NET 8, while the error message states that it can only find .NET 6 and 7 from `/usr/local/share/dotnet`, and *isn't* looking at `bin/*/dotnet`! .NET 8 Roslyn apparently requires that the "executing" `dotnet` be in `$PATH`: % PATH=…/xamarin-android/bin/Debug/dotnet:$PATH \ …/xamarin-android/dotnet-local.sh build *.csproj # no errors Update `dotnet-local.sh` and `dotnet-local.cmd` so that `bin/*/dotnet` is prepended to `$PATH`. Additionally, for readability in `dotnet-loca.sh`, explicitly export all `DOTNET*` environment variables so that they don't all need to be on one line. Update build scripts to use `-nodeReuse:false` instead of `-nr:false` for readability. …and some notes about `dotnet-local.cmd`: [`CMD.EXE`][0] environment variable handling is "baroque". By default, evironment variables added within a `.cmd` script are exported to the caller. This is similar to Unixy platforms ["source"][1]ing a script instead of executing a script. To avoid this behavior, the script should use [SETLOCAL][2]. As we want `dotnet-local.cmd` to update `%PATH%`, add use of `SETLOCAL` so that we don't update `%PATH%` on every `dotnet-local.cmd` invocation. Environment variables are evalated *when a statement is read*, ***not*** when that statement is *executed*. This can make for some "weird" behavior: IF EXIST "%ROOT%\bin\Release\dotnet\dotnet.exe" ( SET XA_DOTNET_ROOT=%ROOT%\bin\Release\dotnet echo XA_DOTNET_ROOT=%XA_DOTNET_ROOT% ) The above fragment will print `XA_DOTNET_ROOT=` because `%XA_DOTNET_ROOT%` is evaluated when the entire `IF EXIST …` statment is read, *not* when the `echo` is executed. This has similar "wierdness" if we instead attempt to update `PATH`: IF EXIST "%ROOT%\bin\Release\dotnet\dotnet.exe" ( SET PATH=%ROOT%\bin\Release\dotnet;%PATH% ) The above invariably fails with weirdness such as: \NSIS\ was unexpected at this time. or \Microsoft was unexpected at this time. The workaround is one of two options: 1. Consider use "delayed environment variable expansion"; see the `SET /?` output [^0]. 2. Don't Do That™: don't `SET` an environment variable to include values which are also `SET` in the same statement, and `(…)` is evaluated as a single statement. We opt to go with (2), drastically simplifying `dotnet-local.cmd` so that the `COMMAND` within `IF EXIST filename COMMAND` *only* sets environment variables which do not reference other environment variables within the same `COMMAND` [0]: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd [1]: https://linuxize.com/post/bash-source-command/ [2]: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setlocal [^0]: From `SET /?`: Delayed environment variable expansion is useful for getting around the limitations of the current expansion which happens when a line of text is read, not when it is executed. The following example demonstrates the problem with immediate variable expansion: set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked ) would never display the message, since the %VAR% in BOTH IF statements is substituted when the first IF statement is read, since it logically includes the body of the IF, which is a compound statement. So the IF inside the compound statement is really comparing "before" with "after" which will never be equal. Similarly, the following example will not work as expected: set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST% in that it will NOT build up a list of files in the current directory, but instead will just set the LIST variable to the last file found. Again, this is because the %LIST% is expanded just once when the FOR statement is read, and at that time the LIST variable is empty. So the actual FOR loop we are executing is: for %i in (*) do set LIST= %i which just keeps setting LIST to the last file found. Delayed environment variable expansion allows you to use a different character (the exclamation mark) to expand environment variables at execution time. …
1 parent 7bf75ba commit 341a962

File tree

8 files changed

+63
-35
lines changed

8 files changed

+63
-35
lines changed

Diff for: Documentation/building/windows/instructions.md

+4-7
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,18 @@ MSBuild version 15 or later is required.
2020

2121
5. In a [Developer Command Prompt][developer-prompt], prepare the project:
2222

23-
dotnet msbuild Xamarin.Android.sln -t:Prepare
23+
dotnet msbuild Xamarin.Android.sln -t:Prepare -nodeReuse:false
24+
dotnet msbuild external\Java.Interop\Java.Interop.sln -t:Prepare -nodeReuse:false
2425

2526
This will ensure that the build dependencies are installed, perform
2627
`git submodule update`, download NuGet dependencies, and other
2728
"preparatory" and pre-build tasks that need to be performed.
2829

2930
6. Build the project:
3031

31-
dotnet-local.cmd build Xamarin.Android.sln -m:1
32+
dotnet-local.cmd build Xamarin.Android.sln -nodeReuse:false
3233

33-
7. In order to use the in-tree Xamarin.Android, build xabuild:
34-
35-
msbuild tools/xabuild/xabuild.csproj /restore
36-
37-
8. (For Microsoft team members only - Optional) In a [Developer Command
34+
7. (For Microsoft team members only - Optional) In a [Developer Command
3835
Prompt][developer-prompt], build external proprietary git
3936
dependencies:
4037

Diff for: build-tools/automation/azure-pipelines.yaml

+16-4
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ stages:
112112
restoreNUnitConsole: false
113113
updateMono: false
114114
xaprepareScenario: EmulatorTestDependencies
115-
115+
116116
- task: DownloadPipelineArtifact@2
117117
inputs:
118118
artifactName: $(TestAssembliesArtifactName)
@@ -140,7 +140,7 @@ stages:
140140
arguments: -t:PrepareJavaInterop -c $(XA.Build.Configuration) --no-restore
141141
displayName: prepare java.interop $(XA.Build.Configuration)
142142
continueOnError: false
143-
143+
144144
- template: yaml-templates/start-stop-emulator.yaml
145145

146146
- template: yaml-templates/apk-instrumentation.yaml
@@ -201,7 +201,7 @@ stages:
201201
extraBuildArgs: -p:TestsFlavor=AotLlvm -p:EnableLLVM=true -p:AndroidEnableProfiledAot=false
202202
artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab
203203
artifactFolder: $(DotNetTargetFramework)-AotLlvm
204-
204+
205205
- template: yaml-templates/apk-instrumentation.yaml
206206
parameters:
207207
configuration: $(XA.Build.Configuration)
@@ -220,13 +220,19 @@ stages:
220220
artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Xamarin.Android.JcwGen_Tests-Signed.apk
221221
artifactFolder: $(DotNetTargetFramework)-FastDev_Assemblies_Dexes
222222
extraBuildArgs: /p:AndroidFastDeploymentType=Assemblies:Dexes
223-
223+
224224
- template: yaml-templates/run-nunit-tests.yaml
225225
parameters:
226226
testRunTitle: Xamarin.Android.Tools.Aidl-Tests - macOS
227227
testAssembly: $(System.DefaultWorkingDirectory)/bin/Test$(XA.Build.Configuration)/$(DotNetStableTargetFramework)/Xamarin.Android.Tools.Aidl-Tests.dll
228228
testResultsFile: TestResult-Aidl-Tests-macOS-$(XA.Build.Configuration).xml
229229

230+
- task: ShellScript@2
231+
displayName: Test dotnet-local.sh
232+
inputs:
233+
scriptPath: dotnet-local.sh
234+
args: build samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj
235+
230236
- ${{ if ne(parameters.macTestAgentsUseCleanImages, true) }}:
231237
- template: yaml-templates/start-stop-emulator.yaml
232238
parameters:
@@ -276,6 +282,12 @@ stages:
276282
dotNetTestExtraArgs: --filter "TestCategory = SmokeTests"
277283
testResultsFile: TestResult-NETSmokeMSBuildTests-Linux-$(XA.Build.Configuration).xml
278284

285+
- task: ShellScript@2
286+
displayName: Test dotnet-local.sh
287+
inputs:
288+
scriptPath: dotnet-local.sh
289+
args: build samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj
290+
279291
- template: yaml-templates/upload-results.yaml
280292
parameters:
281293
configuration: $(XA.Build.Configuration)

Diff for: build-tools/automation/yaml-templates/build-windows.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ stages:
9898
testResultsFile: TestResult-SmokeMSBuildTests-WinDotnetBuild-$(XA.Build.Configuration).xml
9999
dotNetTestExtraArgs: --filter "TestCategory = SmokeTests"
100100

101+
- task: BatchScript@1
102+
displayName: Test dotnet-local.cmd
103+
inputs:
104+
filename: dotnet-local.cmd
105+
arguments: build samples\HelloWorld\HelloWorld\HelloWorld.DotNet.csproj
106+
101107
- template: upload-results.yaml
102108
parameters:
103109
artifactName: ${{ parameters.buildResultArtifactName }}

Diff for: build-tools/scripts/PrepareWindows.targets

+1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@
2424
/>
2525
<Exec Command="dotnet $(_XAPrepareExe) $(_XAPrepareStandardArgs) -a" WorkingDirectory="$(_TopDir)" />
2626
<MSBuild Projects="$(_TopDir)\Xamarin.Android.BootstrapTasks.sln" Targets="Restore;Build" />
27+
<CallTarget Targets="PrepareJavaInterop" />
2728
</Target>
2829
</Project>

Diff for: build.cmd

+7-9
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,21 @@ IF ERRORLEVEL 1 CALL:FAILED_CASE
88
IF ERRORLEVEL 1 CALL :DEFAULT_CASE
99

1010
:Prepare_CASE
11-
dotnet build Xamarin.Android.sln -t:Prepare -nr:false
11+
dotnet msbuild Xamarin.Android.sln -t:Prepare -nodeReuse:false
1212
GOTO END_CASE
1313
:PrepareExternal_CASE
14-
dotnet build Xamarin.Android.sln -t:PrepareExternal -nr:false
14+
dotnet build Xamarin.Android.sln -t:PrepareExternal -nodeReuse:false
1515
GOTO END_CASE
1616
:Build_CASE
17-
dotnet-local.cmd build Xamarin.Android.sln -nr:false
18-
dotnet-local.cmd build tools/xabuild/xabuild.csproj -nr:false
17+
dotnet-local.cmd build Xamarin.Android.sln -nodeReuse:false
1918
GOTO END_CASE
2019
:Pack_CASE
21-
dotnet-local.cmd build Xamarin.Android.sln -t:PackDotNet -nr:false
20+
dotnet-local.cmd build Xamarin.Android.sln -t:PackDotNet -nodeReuse:false
2221
GOTO END_CASE
2322
:DEFAULT_CASE
24-
dotnet build Xamarin.Android.sln -t:Prepare -nr:false
25-
dotnet-local.cmd build Xamarin.Android.sln -nr:false
26-
dotnet-local.cmd build tools/xabuild/xabuild.csproj -nr:false
27-
dotnet-local.cmd build Xamarin.Android.sln -t:PackDotNet -nr:false
23+
dotnet msbuild Xamarin.Android.sln -t:Prepare -nodeReuse:false
24+
dotnet-local.cmd build Xamarin.Android.sln -nodeReuse:false
25+
dotnet-local.cmd build Xamarin.Android.sln -t:PackDotNet -nodeReuse:false
2826
GOTO END_CASE
2927
:FAILED_CASE
3028
echo "Failed to find an instance of Visual Studio. Please check it is correctly installed."

Diff for: dotnet-local.cmd

+16-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
@echo off
2+
SETLOCAL
3+
24
SET ROOT=%~dp0
5+
36
IF EXIST "%ROOT%\bin\Release\dotnet\dotnet.exe" (
4-
SET DOTNETSDK_WORKLOAD_MANIFEST_ROOTS=%ROOT%\bin\Release\lib\sdk-manifests
5-
SET DOTNETSDK_WORKLOAD_PACK_ROOTS=%ROOT%\bin\Release\lib
6-
call "%ROOT%\bin\Release\dotnet\dotnet.exe" %*
7+
SET XA_CONFIG=Release
78
) ELSE IF EXIST "%ROOT%\bin\Debug\dotnet\dotnet.exe" (
8-
SET DOTNETSDK_WORKLOAD_MANIFEST_ROOTS=%ROOT%\bin\Debug\lib\sdk-manifests
9-
SET DOTNETSDK_WORKLOAD_PACK_ROOTS=%ROOT%\bin\Debug\lib
10-
call "%ROOT%\bin\Debug\dotnet\dotnet.exe" %*
9+
SET XA_CONFIG=Debug
1110
) ELSE (
1211
echo "You need to run 'msbuild Xamarin.Android.sln /t:Prepare' first."
13-
)
12+
goto :exit
13+
)
14+
15+
SET XA_DOTNET_ROOT=%ROOT%\bin\%XA_CONFIG%\dotnet
16+
SET PATH=%XA_DOTNET_ROOT%;%PATH%
17+
SET DOTNETSDK_WORKLOAD_MANIFEST_ROOTS=%ROOT%\bin\%XA_CONFIG%\lib\sdk-manifests
18+
SET DOTNETSDK_WORKLOAD_PACK_ROOTS=%ROOT%\bin\%XA_CONFIG%\lib
19+
20+
call "%XA_DOTNET_ROOT%\dotnet.exe" %*
21+
22+
:exit

Diff for: dotnet-local.sh

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
#!/bin/bash
22
ROOT="$(dirname "${BASH_SOURCE}")"
33
FULLROOT="$(cd "${ROOT}"; pwd)"
4-
if [[ -x "${ROOT}/bin/Release/dotnet/dotnet" ]]; then
5-
DOTNETSDK_WORKLOAD_MANIFEST_ROOTS=${FULLROOT}/bin/Release/lib/sdk-manifests DOTNETSDK_WORKLOAD_PACK_ROOTS=${FULLROOT}/bin/Release/lib exec ${ROOT}/bin/Release/dotnet/dotnet "$@"
6-
elif [[ -x "${ROOT}/bin/Debug/dotnet/dotnet" ]]; then
7-
DOTNETSDK_WORKLOAD_MANIFEST_ROOTS=${FULLROOT}/bin/Debug/lib/sdk-manifests DOTNETSDK_WORKLOAD_PACK_ROOTS=${FULLROOT}/bin/Debug/lib exec ${ROOT}/bin/Debug/dotnet/dotnet "$@"
8-
else
9-
echo "You need to run 'make prepare' first."
10-
fi
4+
for config in Release Debug ; do
5+
if [[ ! -x "${ROOT}/bin/${config}/dotnet/dotnet" ]] ; then
6+
continue
7+
fi
8+
XA_DOTNET_ROOT="${FULLROOT}/bin/${config}/dotnet"
9+
export PATH="${XA_DOTNET_ROOT}:${PATH}"
10+
export DOTNETSDK_WORKLOAD_MANIFEST_ROOTS="${FULLROOT}/bin/${config}/lib/sdk-manifests"
11+
export DOTNETSDK_WORKLOAD_PACK_ROOTS="${FULLROOT}/bin/${config}/lib"
12+
exec "${ROOT}/bin/${config}/dotnet/dotnet" "$@"
13+
done
14+
15+
echo "You need to run 'make prepare' first."

Diff for: src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/DotNetCLI.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ List<string> GetDefaultCommandLineArgs (string verb, string target = null, strin
152152
$"/flp1:LogFile=\"{BuildLogFile}\";Encoding=UTF-8;Verbosity={Verbosity}",
153153
$"/bl:\"{Path.Combine (testDir, $"{(string.IsNullOrEmpty (target) ? "msbuild" : target)}.binlog")}\"",
154154
"-m:1",
155-
"-nr:false",
155+
"-nodeReuse:false",
156156
"/p:_DisableParallelAot=true",
157157
};
158158
if (!string.IsNullOrEmpty (target)) {

0 commit comments

Comments
 (0)