Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[build.ps1] Set the default architecture using an environment variable #104474

Closed
wants to merge 4 commits into from

Conversation

eiriktsarpalis
Copy link
Member

I'm creating a PR with the original prototype since my dev workflow is currently blocked by this.

Fix #104033.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-infrastructure-libraries
See info in area-owners.md if you want to be subscribed.

@jkotas
Copy link
Member

jkotas commented Jul 5, 2024

The usual way to deal with this problem is to have a small local wrapper script(s) that are optimized for your dev workflows.

For example, I like release configuration as the default for my dev workflow and only override the specific components to debug/checked as necessary. Should we have a environment variable for that too?

eng/build.ps1 Outdated
function Get-Help() {
Write-Host "Common settings:"
Write-Host " -arch (-a) Target platform: x86, x64, arm, arm64, or wasm."
Write-Host " Pass a comma-separated list to build for multiple architectures."
Write-Host (" [Default: {0} (Depends on your console's architecture.)]" -f [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant())
Write-Host (" [Default: {0} (Depends on your console's architecture or the DOTNET_RUNTIME_DEFAULT_ARCH environment variable.)]" -f @(Get-Default-Arch))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This covers for the top level build.sh1, but it does not cover other entrypoints into the dev workflow. For example, it does not cover running dotnet build .... directly that is part of number of documented dev workflows. If we want to go with the environment variables as the solution for overriding defaults, should it be respected for all entrypoints into the dev workflow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peculiarity of build.sh1 is that it resolves the default architecture from the process architecture of the calling shell. The architecture of dotnet build ... is controlled by what SDKs you have installed and the PATH environment variable. I've never had issues running the correct sdk from a 32-bit shell.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peculiarity of build.sh1 is that it resolves the default architecture from the process architecture of the calling shell.

build.ps1 resolves the default architecture from the process architecture of the calling powershell.exe. It sounds like that you have the x64 powershell.exe on the PATH in your environment, but you actually want to have the arm64 powershell.exe. Is there arm64 powershell on Windows Arm64?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows Arm does have native builds for Powershell, however I'm using mingw which only has x64 builds at the moment. A similar issue with SSH connections to Windows arm devices.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to workaround the problem by changing the path in your mingw environment to point to native powershell? It would cover this and all other powershell.exe invocations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if you run it as %windir%\sysnative\cmd.exe ?

That folder doesn't exist on ARM builds. Similar to powershell.exe it's the same executable that adapts depending on the parent architecture:

image

The non-native shells keep you on non-native architecture. It has been always the case.

I understand that this is the case, however like I said this does create problems in the workflows for those that do rely on tools that aren't arm64 native yet. While creating a wrapper script does technically resolve the issue, the dev loop workflow largely relies on muscle memory: I work on x64 and arm64 machines interchangeably so even with the workaround I have to explicitly remember that the current machine is arm64 to make sure the right script/arch parameter is being used. If I don't, I will have lost > 5 minutes waiting on a build that was targeting the wrong architecture. This has been happening frequently enough to me for it to be a problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That folder doesn't exist on ARM builds.

That's surprising. It has been a virtual folder that only exists on non-native contexts. It has been the escape hatch to get back to native context. For example, this is how you can relaunch native x64 cmd from the emulated x86 cmd on Windows x64:

C:\Windows>set PROCESSOR_ARCHITECTURE
PROCESSOR_ARCHITECTURE=x86

C:\Windows>%windir%\sysnative\cmd.exe
Microsoft Windows [Version 10.0.22631.3810]
(c) Microsoft Corporation. All rights reserved.

C:\Windows>set PROCESSOR_ARCHITECTURE
PROCESSOR_ARCHITECTURE=AMD64

Is it really the case that these same steps do not work in emulated x64 cmd on Windows arm64?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming there existed a build of cmd.exe or powershell.exe that consistently launched an arm64 process, then it would solve my issue. The only copy of cmd.exe I could find other than the System32 one is the one at SysWOW64 which launches as an x86 process.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I have done some reading on this topic. The system .exes are multi-arch on Windows Arm, and so there is no way to control the system process architecture by selecting different .exes. Instead, the process launching the .exe controls the process architecture.

For example, start command allows you to set the desired process architecture.start /B /WAIT /MACHINE arm64 %windir%\System32\cmd.exe should always launch native cmd prompt on Windows arm64.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the /machine parameter seems to work for me. By installing the following script called powershell in my PATH:

#!/usr/bin/env bash
start //b //wait //machine arm64 powershell.exe "$@"

I'm able to force native powershell process from the mingw environment. This solves the problem using build.sh as the entrypoint, but not for the build.cmd entrypoint.

@eiriktsarpalis
Copy link
Member Author

For example, I like release configuration as the default for my dev workflow and only override the specific components to debug/checked as necessary. Should we have a environment variable for that too?

It's common practice in CLI tools in general, so I don't see why we couldn't do this for other parameters as well.

@jkotas
Copy link
Member

jkotas commented Jul 5, 2024

It's common practice in CLI tools in general, so I don't see why we couldn't do this for other parameters as well.

The downside is that it results into harder to debug systems and problems like "this build command works on one machine, but not on the other machine".

@eiriktsarpalis
Copy link
Member Author

The downside is that it results into harder to debug systems and problems like "this build command works on one machine, but not on the other machine".

True, but we have embraced that approach in the product itself. Maybe it makes to be cautious about what knobs we do expose via environment variables, but in this case it seems warranted to me.

@jkotas
Copy link
Member

jkotas commented Jul 5, 2024

If you set this, does your build environment use arm64 .NET runtime and tools to build, or does it use the slow emulated x64 .NET runtime and tools to build?

@eiriktsarpalis
Copy link
Member Author

If you set this, does your build environment use arm64 .NET runtime and tools to build, or does it use the slow emulated x64 .NET runtime and tools to build?

It should be equivalent to passing the -arch arm64 argument, which is fast enough on my machine. Do we have reason to suspect that this could fall back to using x64 and if so, how can I verify this?

@jkotas
Copy link
Member

jkotas commented Jul 5, 2024

how can I verify this?

Verify that <repo_root>\.dotnet\dotnet.exe --info is native arm64 build and check in task manager details view that the dotnet.exe processes that are launched during the build are native arm64 processes.

Do we have reason to suspect that this could fall back to using x64

The primary use of the -arch and -os options are cross-builds. It is used when you want to build binaries for a different architecture than the architecture that the build environment is running on.

@jkotas
Copy link
Member

jkotas commented Jul 5, 2024

It's common practice in CLI tools in general, so I don't see why we couldn't do this for other parameters as well.

Would it make sense to have a more general environment variable that allows you to set defaults for any option? For example, if you set it to -arch arm64 -c release, build.cmd/sh would produce arm64 release build by default.

@eiriktsarpalis
Copy link
Member Author

That seems somewhat brittle to me, there's also the UX issue of dealing with space escaping in all different shell types.

@eiriktsarpalis
Copy link
Member Author

how can I verify this?

Verify that <repo_root>\.dotnet\dotnet.exe --info is native arm64 build and check in task manager details view that the dotnet.exe processes that are launched during the build are native arm64 processes.

I've verified that it's running all arm64 processes when the arch parameter is specified explicitly.

@jkotas
Copy link
Member

jkotas commented Jul 6, 2024

Would it make sense to have a more general environment variable that allows you to set defaults for any option? For example, if you set it to -arch arm64 -c release, build.cmd/sh would produce arm64 release build by default.

That seems somewhat brittle to me, there's also the UX issue of dealing with space escaping in all different shell types.

We use this pattern in other places today, e.g.:

echo "EXTRA_CFLAGS=-fstack-clash-protection EXTRA_CXXFLAGS=-fstack-clash-protection ./build.sh clr"
. I have seen it used outside .NET as well.

My thinking behind this suggestion was about making this feature as powerful as possible while keeping it as simple as possible. Having a new environment variable for every build.cmd/sh option that somebody may want to have a custom default for does not look pretty.

@eiriktsarpalis
Copy link
Member Author

We use this pattern in other places today, e.g.:

It should be ok as long as the order of parameters isn't significant and there is no existing trailing parameters scheme that is being passed to nested processes. I defer to @ViktorHofer on whether it's appropriate to use.

@ViktorHofer
Copy link
Member

My thinking behind this suggestion was about making this feature as powerful as possible while keeping it as simple as possible. Having a new environment variable for every build.cmd/sh option that somebody may want to have a custom default for does not look pretty.

I agree that it would be better to have one env var to rule them all instead of multiple separate for every potential input. In general, I'm not a fan of introducing a new env var as the current input configuration system is already complex enough but if this is just meant to be set for an enhanced dev workflow (and gets documented) then I'm OK with that. As long as it doesn't further complicate our CI / local dev workflow configuration matrix.

@eiriktsarpalis
Copy link
Member Author

eiriktsarpalis commented Jul 8, 2024

One issue with the general purpose environment variable is that it doesn't seem to have a good override mechanism other than unsetting it. If I had set my DOTNET_RUNTIME_BUILD_ARGS=-arch arm64 on my machine then there would be no way to cross compile to x64 since specifying a parameter would expand to two -arch parameters being specified. It might be helpful in other use cases, but definitely not mine.

@eiriktsarpalis
Copy link
Member Author

Closing since we have a workaround via #104033 (comment)

@jkotas
Copy link
Member

jkotas commented Jul 9, 2024

If I had set my DOTNET_RUNTIME_BUILD_ARGS=-arch arm64 on my machine then there would be no way to cross compile to x64 since specifying a parameter would expand to two -arch parameters being specified.

The actual -arch parameter would override the default -arch argument. This works fine since -arch parameter can be specified only once.

You are right that it would not work for parameters that can be specified more than once.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support configuring the default build.cmd/.sh parameters using an environment variable
4 participants