Native support for Apple Silicon (M1 / arm64 architecture)
See original GitHub issueWhat would you like?
The native support of Apple Silicon by distributing an arm64 macos binary, without having to use Rosetta 2 at all.
Solution 1: by distributing two distinct binaries according to the macos architecture (x64 or arm64) Solution 2: by distributing an Universal macOS Binary
This is not a duplicate of https://github.com/cypress-io/cypress/issues/4478 because I am focusing only on Apple Silicon binaries, not ARM Linux binaries in general. This is however a duplicate of https://github.com/cypress-io/cypress/issues/18724 which was closed, but since my request and my proposed solutions are a bit different, I prefer to open a new issue.
Why is this needed?
@baversjo wrote a very good summary in https://github.com/cypress-io/cypress/issues/4478:
Running cypress on an ARM-based M1 mac is pretty slow under rosetta 2. Considering apple no longer seem to selling new intel macs as of q4 2021, I think it’s a pretty important feature to add?
On top of that, I think the workaround explained here https://cypress.io/blog/2021/01/20/running-cypress-on-the-apple-m1-silicon-arm-architecture-using-rosetta-2/ provides a very poor developer experience, having to maintain two different Nodejs installs with Rosetta.
Proposed solutions
My proposed solution relies on the ability of electron-packager, and thus electron-builder to be able to build macos arm64 binaries on an x64 host, therefore not having to deploy custom CircleCI arm runners, as explained here: https://github.com/electron-userland/electron-builder/issues/5689
Solution 1: two distinct binaries: x64 and arm64
electron-builder.json
becomes:
{
[...]
"mac": {
"target": {
"target": "zip",
"arch": [
"x64",
"arm64"
]
},
[...]
},
[...]
}
I tested: instead of creating one zip file (build/build/Cypress-9.4.0-mac.zip
), it creates two (build/build/Cypress-9.4.0-mac.zip
and build/build/Cypress-9.4.0-arm64-mac.zip
). And as you can see, the binaries are built against the correct architecture:
# Executed on an Intel macbook
➜ cypress git:(develop) ✗ yarn binary-build --platform darwin --version $(node ./scripts/get-next-version.js)
# [...]
➜ cypress git:(develop) ✗ lipo -archs build/build/mac/Cypress.app/Contents/MacOS/Cypress
x86_64
➜ cypress git:(develop) ✗ lipo -archs build/build/mac-arm64/Cypress.app/Contents/MacOS/Cypress
arm64
Then, I suppose we just would have to upload the two zip files, and modify the various mechanisms in place that detect the current arch to download the correct file.
At first sight, the only “tricky” part would be having to patch the arch lib used in cli/lib/tasks/download.js
(because of https://github.com/feross/arch/issues/19)
Disclamer: I was not able to test the full build process and thus to test the final binaries on both platforms because I am working on a Macbook behind a corporate proxy, so this is very difficult to install all the correct dev dependencies. Also, I am not a contributor yet so I have a very limited knowledge of the project architecture, so maybe I am missing something.
Solution 2: one universal app
In this case electron-builder.json
becomes:
{
[...]
"mac": {
"target": {
"target": "zip",
"arch": "universal"
},
[...]
},
[...]
}
And electron-builder builds the app twice, for each arch, and should bundle the two binaries into the same app. Unfortunately, the “merge” phase failed with this error on my side:
• packaging platform=darwin arch=universal electron=15.3.4 appOutDir=/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/cypress-build/darwin/build/mac-universal
⨯ Command failed with a non-zero return code (1):
lipo /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/electron-universal-YpnsKo/Tmp.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/cypress-build/darwin/build/mac-universal--arm64/Cypress.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node -create -output /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/electron-universal-YpnsKo/Tmp.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node
fatal error: /Library/Developer/CommandLineTools/usr/bin/lipo: /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/electron-universal-YpnsKo/Tmp.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node and /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/cypress-build/darwin/build/mac-universal--arm64/Cypress.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node have the same architectures (x86_64) and can't be in the same fat output file failedTask=build stackTrace=Error: Command failed with a non-zero return code (1):
lipo /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/electron-universal-YpnsKo/Tmp.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/cypress-build/darwin/build/mac-universal--arm64/Cypress.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node -create -output /private/var/folders/1v/csfvz7q15w10lvp83zzlp72w0000gp/T/electron-universal-YpnsKo/Tmp.app/Contents/Resources/app/node_modules/@cypress/get-windows-proxy/node_modules/registry-js/build/Release/registry.node
I didn’t have time to debug further.
Which solution should we choose?
I am not a Mac developer so I don’t know what is the best practice here. However, I was not able to test but if I understand well, the merged binary size should double in solution 2. This is why I think solution 1 is preferable in my opinion. And since all the arch detection/distribution is already in place in the project, it should not be difficult to implement.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:61
- Comments:22 (4 by maintainers)
Top GitHub Comments
Just a comment that the workaround (using Rosetta2 + multiple node installs) is an intractable mess.
Please do not let perfect become the enemy of good – give us a native ARM option even if you cannot test it adequately! Even if the default is AMD64 I need to perform some special step to install the ARM binary, I am happy to do that.
The developer experience with Cypress under the status quo is simply not sustainable – my developers will lose even more confidence in this tool if I force them to deal with crashes, memory leaks and slowness in addition to all of Cypress’ flakey test issues.
Been waiting for this feature too. For now, my workaround is(thx for @thomvaill):
Clone Cypress repo
git checkout tags/v9.5.4
yarn
Change
electron-builder.json
yarn binary-build --platform darwin --version 9.5.4
mkdir -p ~/Library/Caches/Cypress/9.5.4
cp -R build/build/mac-arm64/* ~/Library/Caches/Cypress/9.5.4
After that I have Cypress that works well