Switch between ARM and x86 emulation on Apple Silicon!
Setup your .bash_profile and other bash config files properly for your macOS or Linux to make your work comfortable. It is valuable for the owners of Apple Silicon where you need to switch between ARM and x86 architectures.
Table of content
- Introduction
- Understand the bash profile load sequence
- Understand why we need to switch between ARMv8 and Rosetta emulation for Mac M1
- Files structure and examples
- Switch between architectures
- Useful links
Introduction
I am a Mac M1 user and faced the necessity to get .bash_profile and other bash config files adequately set up to switch between ARMv8 and Rosetta with comfort. This note will serve as a memory note for me, and I hope some of you will find it helpful as well. I write the code as it works for Mac M1. But it will also work for Linux and probably for Windows users with some adoptions required. Your comments and feedback are very welcome since there is always room for improvement and adjustment.
1. Understand the bash profile load sequence
First of all, we need to understand what is the bash profile load sequence and what is the difference between the .bash_profile
and .bashrc
which are typically stored in the user folder ~/.
.
There are two great sources to read:
- Configuring your login session with dotfiles
- What is the difference between .bash_profile and .bashrc?
And here is the summary. Several dotfiles determine the way your system behaves at login time, setting up aliases, setting up environment variables, and so on.
The load sequence of dotfiles is as follows:
- The system reads
/etc/profile
first. - Then, bash looks in your home directory for
.bash_profile
, and if it finds it, it reads that. - If it doesn’t find
.bash_profile
, it looks for.bash_login
, and if it doesn’t find that, it looks for.profile
(the standard Bourne/POSIX/Korn shell configuration file). Otherwise, it stops looking for dotfiles and gives you a prompt.
Notes:
- On Linux systems, the system will typically source some or all files in
/etc/profile.d
(as suggested by the Linux Standard Base — generally/etc/profile
should include code for this) - Many Linux systems also have another layer called PAM, which is relevant here. Before “executing” bash, login will read the
/etc/pam.d/login
file (or its equivalent on your system), which may tell it to read various other files such as/etc/environment
. Other systems such as OpenBSD have a/etc/login.conf
file that controls resource limits for various classes of user accounts. So you may have some extra environment variables, process limits, and so on before your shell reads/etc/profile
.
You may have noted that .bashrc
is not being read in this situation. You should, therefore, always have source ~/.bashrc
at the end of your .bash_profile
to force it to be read by a login shell. If you use .profile
instead of .bash_profile
, you additionally need to test if the shell is bash first:
# .profile
if [ -n "$BASH" ] && [ -r ~/.bashrc ]; then
. ~/.bashrc
fi
Why is .bashrc
a separate file from .bash_profile
, then? There are a couple of reasons. The first is performance — when machines were extremely slow compared to today’s workstations, processing the commands in .profile
or .bash_profile
could take quite a long time, especially on machines where a lot of the work had to be done by external commands (before Korn/Bash shells). So the difficult initial setup commands, which create environment variables that can be passed down to child processes, are put in .bash_profile
. The transient settings and aliases/functions that are not inherited are put in .bashrc
to be re-read by every new interactive shell.
The second reason why .bashrc
is separate is due to work habits. If you work in an office setting with a terminal on your desk, you probably log in one time at the start of each day and log out at the end of the day. You may put various special commands in your .bash_profile
that you want to run at the beginning of each day, when you log in — checking for announcements from management, etc. You wouldn’t want those to be done every time you launch a new shell. So, having this separation gives you some flexibility.
Now you understand the load sequence of bash profile files and why we might need .bashrc
additionally to .bash_profile
file. For our purpose to make a comfortable switch between ARMv8 and Rosetta settings, we will use two different .bashrc
files and .bash_profile
file will store the common setting, which has to be loaded once.
2. Understand why we need to switch between ARMv8 and Rosetta emulation for Mac M1
As you know, the newest Apple MacBooks have chips with ARM
architecture (M1, M1Pro, M1Max etc.). To enable the usage of software designed for x86
architecture, Apple has introduced a Rosetta 2 emulation layer. And if you are a developer or an advanced user, you will come to the point when you need to switch your terminal between ARM
and x86
architectures. You need to understand that sometimes you would need to install an instance of Python built for x86
and another instance of Python built for ARM
just because some software will have one or another instance of Python as a dependency.
Get more information about the Rosetta 2 here and about the Rosetta 2 limitations here. Yes, Rosetta 2 is not bullet-proof, and it can’t emulate some x86
chip instructions right now. Thus, you can’t always rely on Rosetta 2, and you will need to switch back to ARM
architecture and search for an alternative solution.
3. Files structure and examples
Okay, the file structure will be pretty simple, and we will have four files:
~/.bash_profile
which will contain common settings loaded once at the start~/.bashrc64
contains ARMv8 settings (it is called arm64 architecture)~/.bashrc86
contains x86 settings~/.bash_aliases
contains useful aliases and is packed in~/.bash_profile
. It is optional, just for better transparency.
The example files are straightforward. Before you touch your files, please check what is inside your files and act correspondently. In my case, I have two instances of Homebrew and two instances of Python, depending on the architecture. The reasoning why you might need two Pythons is well explained here. Both these examples show that these instances are required to be on different paths, and without proper management with bash profile, their usage will be not possible. There will always be a mismatch between your current architecture and the required path to the software.
~/.bash_profile
# path to homebrew arm64 (for x86 we will use an alias ibrew)
export PATH=/opt/homebrew/bin:$PATH
# path to user bin folder
export PATH=/usr/local/bin:$PATHexport UNAME=$(arch)
export PS1="\h($UNAME): \W \u\$"# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!__conda_setup="$('~/miniforge3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "~/miniforge3/etc/profile.d/conda.sh" ]; then
. "~/miniforge3/etc/profile.d/conda.sh"
else
export PATH="~/miniforge3/bin:$PATH"
fi
fiunset __conda_setup
# <<< conda initialize <<<# Add aliases for profile
if [ -e ~/.bash_aliases ]; then
source ~/.bash_aliases
fi
~/.bashrc64
The ARM
Python is sourced from Miniforge arm64 release
# set explicetely arm64
export UNAME="arm64"
# add architecture to bash prompt
export PS1="\h($UNAME): \W \u\$"# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!__conda_setup="$('~/miniforge3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "~/miniforge3/etc/profile.d/conda.sh" ]; then
. "~/miniforge3/etc/profile.d/conda.sh"
else
export PATH="~/miniforge3/bin:$PATH"
fi
fiunset __conda_setup
# <<< conda initialize <<<# switch to arm64 architecture
arch -arm64 /bin/bash
~/.bashrc86
This file determines the path to x86
Python installed with Miniconda.
# set explicetely x86
export UNAME="x86"
# add architecture to bash prompt
export PS1="\h($UNAME): \W \u\$"# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!__conda_setup="$('~/opt/miniconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "~/opt/miniconda3/etc/profile.d/conda.sh" ]; then
. "~/opt/miniconda3/etc/profile.d/conda.sh"
else
export PATH="~/opt/miniconda3/bin:$PATH"
fi
fiunset __conda_setup
# <<< conda initialize <<<arch -x86_64 /bin/bash
~/.bash_aliases
This file contains an alias for x86 Homebrew. The Homebrew installation guide is here. It will depend on which current architecture is enabled. To switch the architecture, read this instruction. Now, you will be able to use brew
command for arm64
architecture and ibrew
for x86
architecture.
alias ibrew='/usr/local/Homebrew/bin/brew'
4. Switch between architectures
Now, it is time to switch between architectures and get your Python correctly changed. Run the following commands.
Switch to x86 architecture
source ~/.bashrc86
Switch back to ARM architecture
source ~/.bashrc64
Enjoy!
Useful Links
- Configuring your login session with dotfiles
- What is the difference between .bash_profile and .bashrc?
- Linux Standard Base
- child processes
- Get more information about the Rosetta 2 here
- about the Rosetta 2 limitations here.
- The reasoning why you might need two Pythons is well explained here.
- Miniforge arm64 release
- Python installed with Miniconda.
- The Homebrew installation guide is here
- To switch the architecture, read this instruction
For any questions please feel free to post a comment or you can connect with me on LinkedIn.