Hi everyone! I finally upgraded to an M1. It’s really really great, but the main problem is that some projects I work on like TenderJIT and YJIT only really work on x86_64 and these new M1 machines use ARM chips. Fortunately we can run x86_64 software via Rosetta, so we can still do development work on x86 specific software.
I’ve seen some solutions for setting up a dev environment that uses Rosetta, but I’d like to share what I did.
I think most people recommend that you install two different versions of Homebrew, one that targets ARM, and the other that targets x86.
So far, I’ve found this to be the best solution, so I went with it. Just do the normal Homebrew installation for ARM like this:
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Then run the installer again under Rosetta like this:
$ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
After doing this, I ended up with a Homebrew installation in
/opt/homebrew (the ARM version),
and another installation in
/usr/local (the x86 version).
Configuring your Terminal
I read many places on the web that recommend you duplicate terminal, then rename it and modify the renamed version to run under Rosetta.
I really didn’t like this solution. The problem for me is that I’d end up with two
different terminal icons when doing
cmd-tab, and I really can’t be bothered
to read whether the terminal is the Rosetta one or not. It makes switching to
the right terminal take way too long.
Instead I decided to make my shell figure out what architecture I’m using, then
$PATH depending on whether I’m using x86 or ARM. To accomplish this,
I installed Fish (I use Fish shell) in both the x86 and ARM installations of
$ /opt/homebrew/bin/brew install fish $ arch -x86_64 /usr/local/bin/brew install fish
If you’re not using Fish you don’t need to do this step. 😆
Next is the “interesting” part. In my
config.fish, I added this:
if test (arch) = "i386" set HOMEBREW_PREFIX /usr/local else set HOMEBREW_PREFIX /opt/homebrew end # Add the Homebrew prefix to $PATH. -m flag ensures it's at the beginning # of the path since the path might already be in $PATH (just not at the start) fish_add_path -m --path $HOMEBREW_PREFIX/bin alias intel 'arch -x86_64 /usr/local/bin/fish'
arch command will tell you which architecture you’re on. If I’m on i386,
set the Homebrew prefix to
/usr/local, otherwise set it to
fish_add_path to prepend the Homebrew prefix to my
-m switch moves the path to the front if
$PATH already contained
the path I’m trying to add.
Finally I added an alias
intel that just starts a new shell but under Rosetta.
So my default workflow is to open a terminal under ARM, and if I need to work
on an intel project, just run
How do I know my current architecture?
arch command will tell you the current architecture, but I don’t want to
run that every time I want to verify my current architecture. My solution was
to add an emoji to my prompt. I don’t like adding more text to my prompt, but
this seems important enough to warrant the addition.
fish_prompt function looks like this:
function fish_prompt --description 'Write out the prompt' if not set -q __fish_prompt_normal set -g __fish_prompt_normal (set_color normal) end if not set -q __fish_prompt_cwd set -g __fish_prompt_cwd (set_color $fish_color_cwd) end if test (arch) = "i386" set emote 🧐 else set emote 💪 end echo -n -s "[$USER" @ (prompt_hostname) $emote ' ' "$__fish_prompt_cwd" (prompt_pwd) (__fish_vcs_prompt) "$__fish_prompt_normal" ']$ ' end
If I’m on ARM, the prompt will have an 💪 emoji, and if I’m on x86, the prompt will have a 🧐 emoji.
Just to give an example, here is a sample session in my terminal:
Last login: Fri Jan 7 12:37:59 on ttys001 Welcome to fish, the friendly interactive shell [aaron@tc-lan-adapter💪 ~]$ which brew /opt/homebrew/bin/brew [aaron@tc-lan-adapter💪 ~]$ arch arm64 [aaron@tc-lan-adapter💪 ~]$ intel Welcome to fish, the friendly interactive shell [aaron@tc-lan-adapter🧐 ~]$ which brew /usr/local/bin/brew [aaron@tc-lan-adapter🧐 ~]$ arch i386 [aaron@tc-lan-adapter🧐 ~]$ exit [aaron@tc-lan-adapter💪 ~]$ arch arm64 [aaron@tc-lan-adapter💪 ~]$ which brew /opt/homebrew/bin/brew [aaron@tc-lan-adapter💪 ~]$
Now I can easily switch back and forth between x86 and ARM and my prompt tells me which I’m using.
Ruby with chruby
My Ruby dev environment is still a work in progress. I use chruby for changing Ruby versions. The problem is that all Ruby versions live in the same directory. chruby doesn’t know the difference between ARM versions and x86 versions. So for now I’m adding the architecture name to the directory:
[aaron@tc-lan-adapter💪 ~]$ chruby ruby-3.0.2 ruby-arm64 ruby-i386 ruby-trunk [aaron@tc-lan-adapter💪 ~]$
So I have to be careful which Ruby I switch to. I’ve filed a ticket on ruby-install, and I think we can make this nicer.
Specifically I’d like to add a subfolder in
~/.rubies for each architecture,
then point chruby at the right subfolder depending on my current architecture.
Essentially the same trick I used for
$PATH and Homebrew, but for pointing
chruby at the right place given my current architecture.
For now I just have to be careful though!
One huge caveat for Fish users is that the current version of chruby-fish is
broken such that changes to
$PATH end up getting lost (see this issue).
To work around that issue, I’m using @ioquatix’s fork of chruby-fish which can
be found here. I just checked out
that version of chruby-fish in my git project folder and added this to my
# Point Fish at our local checkout of chruby-fish set fish_function_path $fish_function_path $HOME/git/chruby-fish/share/fish/vendor_functions.d
Getting a dev environment up and running with Rosetta wasn’t too bad, but I think
having the shell fix up
$PATH is a better solution than having two copies of Terminal.app
The scripts I presented here were all Fish specific, but I don’t think it should be too hard to translate them to whatever shell you use.
Anyway, I hope you have a good weekend!