2021-02-14

M1 MacBook 编程环境搭建

该文章写于 M1 芯片刚刚发布之时,部分内容距今已有较大变化,仅供参考。

关于M1芯片的评测网上都有一大堆,但是对于开发者的实际评测和arm架构下的环境配置教程几乎还没有。因此我就分享一下这几天折腾这台M1 MacBook Air的经验,即如何在M1芯片的Mac上配置常用的编程环境。

为充分体现M1芯片的优势,这里的编程环境,大部分只考虑已经支持原生arm版本可以在M1芯片上成功编译成arm版本的软件。其架构说明一般为 macos-arm64osx-arm64macOS Universal App。同时还将对arm版本的软件进行性能和续航上的对比评测。

1. 编辑器/IDE

编辑器/IDE的支持情况如下表:

IDE 支持状况 原生arm版(Universal App)下载方式
VS Code Insider Preview支持arm架构 https://code.visualstudio.com/insiders/
JetBrains 系列(PyCharm、Intellij IDEA、Clion等) 原生arm支持 不要直接点下载,要点击右边的三角箭头,选择Universal
XCode 原生arm支持 App Sotre
Vim 原生arm支持 macOS自带,或通过brew install vim安装
Visual Studio for Mac 不支持(可通过 Rosetta 2 运行)
Sublime 不支持(可通过 Rosetta 2 运行)

2. 编程语言

2.1 Homebrew

Homebrew是macOS上常用的软件包管理工具之一,类似于Linux中的aptyum。前两天(2月5日)Homebrew刚刚发布了支持M1芯片的3.0.0正式版。可以打开终端,通过以下命令安装:

1
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

安装时可能无法访问该网站,可通过在家目录下的.bashrc文件(没有则创建)中加入export HTTPS_PROXY="http://127.0.0.1:代理端口号"设置bash的HTTP代理

M1芯片的Mac的Homebrew默认安装位置在/opt/homebrew,而非/usr/local,也就说明:

  • 原来的/usr/local/bin对应/opt/homebrew/bin
  • 原来的/usr/local/sbin对应/opt/homebrew/sbin
  • 原来的/usr/local/lib对应/opt/homebrew/lib
  • 原来的/usr/local/include对应/opt/homebrew/include
  • 原来的/usr/local/etc对应/opt/homebrew/etc
  • 以此类推

这个路径为之后的环境配置埋下了很大的坑!要注意。首先是PATH变量,在安装完成后,可以根据安装脚本的提示,将

1
eval $(/opt/homebrew/bin/brew shellenv)

添加到家目录下的.zprofile文件。这样即可直接使用brew命令和/opt/homebrew/bin下安装的软件。

在Homebrew网站上可以查询到所有可安装的软件包列表,点进去之后可以看到每个软件包对M1芯片的支持情况。虽然它不算编程语言,但是按照逻辑顺序放在这里说。

2.2 Python

以arm架构的3.9.1版本为例,虽然系统有自带的arm版本的python3.8.2,但是不建议使用,因为根据我打开VSCode的警告说,系统自带的Python会缺失部分功能。

2.2.1 通过Homebrew安装

https://formulae.brew.sh/formula/python@3.9

通过

1
brew install python3

即可安装arm版本的3.9.1,其路径在/opt/homebrew/bin/python3

安装好之后,需要通过pip3安装一些基本的第三方库。由于这里是macos-arm64平台,因此很多库都没有来得及提供该平台的wheel,导致很多库都需要通过手动编译安装,而且编译过程往往出错。下面是常用库的arm版本的安装方法整理:

库名称 pip3 install 是否需要编译,编译是否成功 安装方法
numpy 需要编译,编译失败 通过Homebrew:brew install numpy
scipy 需要编译,编译失败 通过Homebrew:brew install scipy
matplotlib 否(纯Python库)
pandas 需要编译,编译成功
sympy 否(纯Python库)
cv2 (opencv-python) 没有这个平台的wheel,也没有源代码,无法安装 通过Homebrew:brew install opencv
pycrypto 需要编译,编译成功
requests 否(纯Python库)
flask 否(纯Python库)
jupyter 需要编译,编译成功
scapy 需要编译,编译成功
pillow 需要编译,编译成功
pwntools 需要编译,编译失败 编译失败的原因是其中的一个依赖cryptography编译失败,提示说是缺少rust语言的编译器,因此先通过brew install rust,再安装即可成功(编译过程会较长)
regex 否(纯Python库)
tensorflow 需要编译,编译失败 根据说明安装Apple发布的tensorflow-macos(仅限python3.8)

这些库在安装成功后,使用时目前没有发现什么问题。下面是我目前的pip3 list结果:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
Package             Version
------------------- ----------
anyio               2.1.0
appnope             0.1.2
argon2-cffi         20.1.0
astroid             2.4.2
async-generator     1.10
attrs               20.3.0
autopep8            1.5.5
Babel               2.9.0
backcall            0.2.0
bcrypt              3.2.0
bleach              3.3.0
capstone            4.0.2
certifi             2020.12.5
cffi                1.14.4
chardet             4.0.0
click               7.1.2
cryptography        3.4.4
cycler              0.10.0
decorator           4.4.2
defusedxml          0.6.0
entrypoints         0.3
Flask               1.1.2
idna                2.10
intervaltree        3.1.0
ipykernel           5.4.3
ipython             7.20.0
ipython-genutils    0.2.0
ipywidgets          7.6.3
isort               5.7.0
itsdangerous        1.1.0
jedi                0.18.0
Jinja2              2.11.3
json5               0.9.5
jsonschema          3.2.0
jupyter             1.0.0
jupyter-client      6.1.11
jupyter-console     6.2.0
jupyter-core        4.7.1
jupyter-server      1.3.0
jupyterlab          3.0.7
jupyterlab-pygments 0.1.2
jupyterlab-server   2.2.0
jupyterlab-widgets  1.0.0
kiwisolver          1.3.1
lazy-object-proxy   1.4.3
Mako                1.1.4
MarkupSafe          1.1.1
matplotlib          3.3.4
mccabe              0.6.1
mistune             0.8.4
mpmath              1.2.1
nbclassic           0.2.6
nbclient            0.5.2
nbconvert           6.0.7
nbformat            5.1.2
nest-asyncio        1.5.1
netifaces           0.10.9
notebook            6.2.0
numpy               1.20.1
packaging           20.9
pandocfilters       1.4.3
paramiko            2.7.2
parso               0.8.1
pexpect             4.8.0
pickleshare         0.7.5
Pillow              8.1.0
pip                 21.0.1
prometheus-client   0.9.0
prompt-toolkit      3.0.14
protobuf            3.14.0
psutil              5.8.0
ptyprocess          0.7.0
pwntools            4.3.1
pybind11            2.6.2
pycodestyle         2.6.0
pycparser           2.20
pycrypto            2.6.1
pyelftools          0.27
Pygments            2.7.4
pylint              2.6.0
PyNaCl              1.4.0
pyparsing           2.4.7
PyQt5               5.15.2
PyQt5-sip           4.19.24
pyrsistent          0.17.3
pyserial            3.5
PySocks             1.7.1
python-dateutil     2.8.1
pytz                2021.1
pyzbar              0.1.8
pyzmq               22.0.2
qtconsole           5.0.2
QtPy                1.9.0
regex               2020.11.13
requests            2.25.1
ROPGadget           6.5
scapy               2.4.4
scipy               1.6.0
Send2Trash          1.5.0
setuptools          52.0.0
six                 1.15.0
sniffio             1.2.0
sortedcontainers    2.3.0
sympy               1.7.1
TBB                 0.1
terminado           0.9.2
testpath            0.4.4
toml                0.10.2
tornado             6.1
traitlets           5.0.5
unicorn             1.0.2rc3
urllib3             1.26.3
wcwidth             0.2.5
webencodings        0.5.1
Werkzeug            1.0.1
wheel               0.36.2
widgetsnbextension  3.5.1
wrapt               1.12.1

2.2.2 通过Miniforge安装

Miniforge是Conda-forge的发型版本。虽然Anaconda没有支持arm,但是miniforge已经支持。可以通过

1
brew install --cask miniforge

安装miniforge,或者手动去GitHub下载脚本安装

之后,通过

1
conda install xxxxxxxx

命令即可安装众多的库的osx-arm64版本。这个方法的好处是不需要编译,支持的包多了很多,可在此处搜索软件包,并点进去查看是否存在osx-arm64版本。2.2.1列表中所有的除了Tensorflow之外,均可安装。Tensorflow在Conda下的安装方法可以参考Apple发布的tensorflow-macos中发布的这个issue(仅限python3.8版本,可通过conda install指定版本安装)

但是!通过这种方式安装的python库,在需要用到lib目录下的库时,问题就来了!在Mac版本的Python中,find_library函数的实现并不是查看PATH,而是搜索常用的几个路径,那么显然/opt/homebrew/lib肯定不在这些常用的路径内,因此在使用如pyzbar这种库时,就算安装成功,import的时候还是会报错!

解决方法有以下两种:

  • 创建/usr/local/lib的符号连接,指向/opt/homebrew/lib
1
sudo ln -s /opt/homebrew/lib /usr/local/lib

此外在conda环境下用pip安装,需要编译时,如果提示头文件找不到,也需要将include目录进行链接:

1
sudo ln -s /opt/homebrew/include /usr/local/include
  • 将Homebrew的安装路径在一开始安装时就手动改为/usr/local

我就是感觉上述两种做法都不是很优雅,因此还是弃用了conda,直接用Homebrew+pip+手动编译装的各种库。下面是我目前的conda list结果:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
# packages in environment at /opt/homebrew/Caskroom/miniforge/base:
#
# Name                    Version                   Build  Channel
brotlipy                  0.7.0           py39h51e6412_1001    conda-forge
bzip2                     1.0.8                h27ca646_4    conda-forge
c-ares                    1.17.1               h27ca646_0    conda-forge
ca-certificates           2020.12.5            h4653dfc_0    conda-forge
cairo                     1.16.0            h11c81c9_1007    conda-forge
certifi                   2020.12.5        py39h2804cbe_1    conda-forge
cffi                      1.14.4           py39h702c04f_1    conda-forge
chardet                   4.0.0            py39h2804cbe_0    conda-forge
click                     7.1.2              pyh9f0ad1d_0    conda-forge
conda                     4.9.2            py39h2804cbe_0    conda-forge
conda-package-handling    1.7.2            py39h51e6412_0    conda-forge
cryptography              3.3.1            py39h0bed37e_0    conda-forge
cycler                    0.10.0                     py_2    conda-forge
ffmpeg                    4.3.1                h22aafd7_2    conda-forge
flask                     1.1.2              pyh9f0ad1d_0    conda-forge
fontconfig                2.13.1            h751047c_1004    conda-forge
freetype                  2.10.4               h17b34a0_1    conda-forge
gettext                   0.19.8.1          hea66d9f_1005    conda-forge
gmp                       6.2.1                h9f76cd9_0    conda-forge
gmpy2                     2.1.0b1          py39ha931add_1    conda-forge
gnutls                    3.6.13               h706517b_1    conda-forge
graphite2                 1.3.13            h9f76cd9_1001    conda-forge
harfbuzz                  2.7.4                head39c9_0    conda-forge
hdf5                      1.10.6          nompi_h0fc092c_1114    conda-forge
icu                       68.1                 h17758a7_0    conda-forge
idna                      2.10               pyh9f0ad1d_0    conda-forge
itsdangerous              1.1.0                      py_0    conda-forge
jasper                    2.0.14               h9af5a26_2    conda-forge
jinja2                    2.11.3             pyh44b312d_0    conda-forge
joblib                    1.0.1              pyhd8ed1ab_0    conda-forge
jpeg                      9d                   h27ca646_0    conda-forge
kiwisolver                1.3.1            py39h5a63225_1    conda-forge
krb5                      1.17.2               h17618d6_0    conda-forge
lame                      3.100             h27ca646_1001    conda-forge
lcms2                     2.12                 had6a04f_0    conda-forge
libblas                   3.9.0                8_openblas    conda-forge
libcblas                  3.9.0                8_openblas    conda-forge
libcurl                   7.71.1               hd2aec06_8    conda-forge
libcxx                    11.0.1               h168391b_0    conda-forge
libedit                   3.1.20191231         hc8eb9b7_2    conda-forge
libev                     4.33                 h642e427_1    conda-forge
libffi                    3.3                  h9f76cd9_2    conda-forge
libgfortran               5.0.0.dev0      11_0_0_hdc626ea_17    conda-forge
libgfortran5              11.0.0.dev0         hdc626ea_17    conda-forge
libglib                   2.66.6               h59da672_3    conda-forge
libiconv                  1.16                 h642e427_0    conda-forge
liblapack                 3.9.0                8_openblas    conda-forge
liblapacke                3.9.0                8_openblas    conda-forge
libnghttp2                1.43.0               hf3018f0_0    conda-forge
libopenblas               0.3.12          openmp_h2ecc587_1    conda-forge
libopencv                 4.5.1            py39hd230a0b_0    conda-forge
libpng                    1.6.37               hf7e6567_2    conda-forge
libssh2                   1.9.0                h1c49ba1_5    conda-forge
libtiff                   4.2.0                h70663a0_0    conda-forge
libwebp-base              1.2.0                h27ca646_0    conda-forge
libxml2                   2.9.10               h8f9ca65_3    conda-forge
llvm-openmp               11.0.1               hb3022d6_0    conda-forge
lz4-c                     1.9.3                h9f76cd9_0    conda-forge
markupsafe                1.1.1            py39h46acfd9_3    conda-forge
matplotlib                3.3.4            py39hdf13c20_0    conda-forge
matplotlib-base           3.3.4            py39h3e8bbd8_0    conda-forge
mpc                       1.1.0             hb760245_1009    conda-forge
mpfr                      4.0.2                hbc63f68_1    conda-forge
mpmath                    1.2.1              pyhd8ed1ab_0    conda-forge
ncurses                   6.2                  h9aa5885_4    conda-forge
netifaces                 0.10.9          py39h46acfd9_1003    conda-forge
nettle                    3.6                  hc6a1b29_0    conda-forge
numpy                     1.20.0           py39h69a04d8_0    conda-forge
olefile                   0.46               pyh9f0ad1d_1    conda-forge
opencv                    4.5.1            py39hdf13c20_0    conda-forge
openh264                  2.1.1                habe5f53_0    conda-forge
openssl                   1.1.1i               h27ca646_0    conda-forge
pcre                      8.44                 hb904e53_0    conda-forge
pillow                    8.1.0            py39hf007017_2    conda-forge
pip                       20.3.3             pyhd8ed1ab_0    conda-forge
pixman                    0.40.0               h27ca646_0    conda-forge
py-opencv                 4.5.1            py39hfa6204d_0    conda-forge
pycosat                   0.6.3           py39h51e6412_1005    conda-forge
pycparser                 2.20               pyh9f0ad1d_2    conda-forge
pycrypto                  2.6.1           py39h51e6412_1005    conda-forge
pyopenssl                 20.0.1             pyhd8ed1ab_0    conda-forge
pyparsing                 2.4.7              pyh9f0ad1d_0    conda-forge
pysocks                   1.7.1            py39h0caf4da_2    conda-forge
python                    3.9.1           hcbd9b3a_2_cpython    conda-forge
python-dateutil           2.8.1                      py_0    conda-forge
python_abi                3.9                      1_cp39    conda-forge
readline                  8.0                  hc8eb9b7_2    conda-forge
regex                     2020.11.13       py39h46acfd9_1    conda-forge
requests                  2.25.1             pyhd3deb0d_0    conda-forge
ruamel_yaml               0.15.80         py39h51e6412_1003    conda-forge
scikit-learn              0.24.1           py39hb966dd2_0    conda-forge
scipy                     1.6.0            py39h73ea49b_0    conda-forge
setuptools                49.6.0           py39h0caf4da_2    conda-forge
six                       1.15.0             pyh9f0ad1d_0    conda-forge
sqlite                    3.34.0               h6d56c25_0    conda-forge
sympy                     1.7.1            py39h2804cbe_1    conda-forge
threadpoolctl             2.1.0              pyh5ca1d4c_0    conda-forge
tk                        8.6.10               hf7e6567_1    conda-forge
tornado                   6.1              py39h46acfd9_1    conda-forge
tqdm                      4.55.0             pyhd8ed1ab_0    conda-forge
tzdata                    2020f                he74cb21_0    conda-forge
urllib3                   1.26.2             pyhd8ed1ab_0    conda-forge
werkzeug                  1.0.1              pyh9f0ad1d_0    conda-forge
wheel                     0.36.2             pyhd3deb0d_0    conda-forge
x264                      1!161.3030           h27ca646_0    conda-forge
xz                        5.2.5                h642e427_1    conda-forge
yaml                      0.2.5                h642e427_0    conda-forge
zlib                      1.2.11            h31e879b_1009    conda-forge
zstd                      1.4.8                h5b28eab_1    conda-forge

2.3 Java

2.3.1 OpenJDK

Homebrew中提供了OpenJDK 15的arm版本:

1
brew install openjdk

但是不会安装在macOS的Library目录下。而且默认不将javajavac放入/opt/homebrew/bin内,因为macOS自带有javajavac命令,会在Library目录下找安装的JDK/JRE来执行。

并且,在IDEA中打开时,要选择JDK。macOS的Finder默认不会显示/opt这个目录,导致JDK选择困难。因此建议通过下面的方法安装。

2.3.2 Azul Zulu JDK

可在官网下载全部的JDK8~JDK16的macos-arm64版本的JDK。相比于上述只有15的版本友好了很多:

https://www.azul.com/downloads/zulu-community/?package=jdk

2.3.3 maven、gradle

由于都是基于Java的运行环境,因此均可通过Homebrew安装,只要安装的Java的运行环境是arm的即可发挥出M1芯片的全部性能:

1
2
brew install maven
brew install gradle

2.4 C/C++

2.4.1 GCC/GDB

目前只能通过Homebrew安装arm版本的GCC,而无法安装GDB:

1
brew install gcc

因此不建议使用GCC。

2.4.2 clang/LLDB

arm版本clang/LLDB的只需安装XCode的命令行工具即可:

1
xcode-select --install

VSCode的C/C++插件目前不支持调试功能。是我自己测的,会有报错,然后在GitHub上搜到了issue,大概意思不支持,但是也不确定,可能自己再折腾折腾。

不过这年头谁还不会手动命令行编译调试C/C++呢?或者可以使用XCode配置C/C++开发环境,具体配置方法是建立macOS的command line tool project,然后就可以开始写了。界面和代码补全不输VSCode,而且早就支持了arm版本,更稳定,也支持调试功能。

2.5 NodeJS

从v15版本开始支持,可通过Homebrew直接安装:

1
brew install node

2.6 PHP

从PHP8版本开始支持,可通过Homebrew直接安装:

1
brew install php

2.7 TexLive(Latex)

根据MacTex的官网,其目前只提供了MacTex2020的intel版本,但是提供了一个补丁用来尝鲜。正式版的支持arm的版本将在今年4月10日随TexLive2021推出。

首先可通过Homebrew安装TexLive:

1
brew install --cask mactex-no-gui

之后在MacTex官网的About ARM网页 http://www.tug.org/mactex/aboutarm.html 下载补丁mactex-2020-universal.pkg。即可。

可以配合VSCode的arm预览版的Latex Workshop插件使用。

3. arm版本软件性能评测

将这台M1 MacBook Air(16G内存+1T SSD)运行arm64版本的软件,与我之前使用的 Windows 10 笔记本,CPU:i5-8300H,内存:16G DDR4,显卡:Nvidia GTX 1050Ti,存储:128G SSD+1T 5600转机械硬盘运行x86-64的软件进行简单的对比:

项目 16G M1 MacBook Air 16G i5-8300H 1050Ti Windows 10(过去的经验)
使用VSCode对Latex Workshop插件,编译23页tex论文,内含大量图表,公式,代码 1.9s >5s
使用PyCharm,启动包括Tensorflow、OpenCV、zbar、Flask等框架的Python AI项目,包括后端服务器和前端的可视化界面 14s >1min
使用PyCharm IDE,通过自己训练的Tensorflow模型对比两张2000x2000左右大小的两张图片 2-3s 5s

4. arm版本软件续航评测

从晚上10点左右开始,使用arm版本的Typora一直编写本文章。起始电池电量为78%,截至发稿前,电池电量为66%。

这个是今天早上起来截图的电池电量变化情况: