MacOS软件包管理器--MacPorts

摘要

  • MacPorts 是 macOS 上的软件包管理器,用来安装命令行工具和库,例如 git、wget、nginx。角色和 MacOS软件包管理器--brew 相同,默认目录是 /opt/local,可以和 Homebrew 装在同一台机器上。
  • 安装脚本叫 Portfile,对应 Homebrew 的 formula。可选功能做成变体,安装时用 + / - 选择。日常命令是 port,改动系统的操作要加 sudo。自己编写 Portfile 并在本机安装,见 MacPorts -- 创建自己的 Port。
  • Homebrew 计划在 2027 年 9 月或更晚停止在 Intel Mac 上运行,官方建议改用 MacPorts。已经用 brew 装好的软件不会自动搬过来,要在 MacPorts 里重新装。
  • 本文命令对照 MacPorts Guide,安装包版本以指南页上的 2.12.6 为例。本机是 MacOS Intel Sequoia 15.7.9,安装包选带 15-Sequoia 的 pkg。

安装

安装前

  • 需要 Xcode Command Line Tools。系统里已经为 Homebrew 装过的话,不用再装一遍。

1
2
3
xcode-select --version
xcode-select --install # 没有装过时才需要
xcode-select -p # /Library/Developer/CommandLineTools

安装 MacPorts

  • 官方推荐用和当前 macOS 版本对应的 pkg,装到 /opt/local。只有准备换安装前缀时才从源码装。下载页:GitHub releases,各系统的直链写在 Install MacPorts。

  • Sequoia 15 对应 MacPorts-2.12.6-15-Sequoia.pkg。pkg 按 macOS 大版本区分,15.7.8 升到 15.7.9 不用换包。一个系统版本一个包,Intel 和 Apple Silicon 用同一个 pkg。双击安装即可。安装程序会自己改 shell 配置,并把 ports 树更新到最新。

  • 新开一个终端再验证:

1
2
3
4
5
$ port version
Version: 2.12.6

$ which port
/opt/local/bin/port
  • 安装程序会把下面这行写进 ~/.zprofile 或 ~/.profile,让 MacPorts 的命令排在 PATH 前面:

1
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
  • 登录 shell 先读 ~/.zprofile,再读 ~/.zshrc。~/.zshrc 里如果还有 PATH=目录:$PATH,那些目录会插到 MacPorts 前面,同名命令就不再先用 /opt/local/bin。最稳妥的做法是在 ~/.zshrc 里所有 PATH 赋值之后再声明一次。typeset -U path 按第一次出现去重,.zprofile 里留下的那一份不会重复出现:

1
2
3
4
# 放在 ~/.zshrc 最后,所有 PATH=目录:$PATH 之后
path=(/opt/local/bin /opt/local/sbin $path) # 把 MacPorts 目录插到 path 最前
typeset -U path # 按第一次出现去重,避免重复条目
export PATH # 同步导出 PATH 环境变量
  • 新开一个终端后用 which git 看当前是哪一份。以后再往 ~/.zshrc 里加 PATH=目录:$PATH,写在这三行上面。bash 里 ~/.bash_profile 先于 ~/.bashrc,同样把上面的 export PATH=... 再写进 ~/.bashrc 末尾。

  • 提示 command not found 时,先新开终端。仍没有,就检查上面的 PATH 是否写进了当前 shell 会读的文件。说明见指南 MacPorts and the Shell。

命令补全

  • 安装包不含 Tab 补全。本机 shell 是 zsh,用 MacPorts 的 zsh-completions 0.36.0。它来自 zsh-users/zsh-completions,是一批 zsh 自带补全之外的定义。本机装上后,/opt/local/share/zsh/site-functions 里有 180 个补全脚本,例如 cmake、certbot、clang-format。port 只是其中的 _port。MacPorts 这个 port 的说明里特意写了包含 port 的补全。

1
sudo port install zsh-completions
  • 补全文件在 /opt/local/share/zsh/site-functions,port 对应 _port。zsh 只在执行 compinit 时扫描 fpath。本机用 oh-my-zsh,source $ZSH/oh-my-zsh.sh 时已经扫过一遍,所以这行写在它后面,并再执行一次 compinit:

1
2
3
fpath=(/opt/local/share/zsh/site-functions $fpath)  # 把 MacPorts 补全目录插到 fpath 最前
autoload -Uz compinit # 加载 zsh 的补全初始化函数
compinit # 扫描 fpath 并启用补全
  • 新开一个终端,输入 port 后按 Tab,会补出 install、search、selfupdate 这些子命令。写在 source $ZSH/oh-my-zsh.sh 前面也可以,那样不用再跑一次 compinit。

  • bash 用 bash-completion 2.18.0。它要求 bash 4.1+。

1
sudo port install bash-completion
  • 安装说明要求把下面几行写进 ~/.bash_profile。port 的补全脚本在 /opt/local/share/bash-completion/completions/port。

1
2
3
if [ -f /opt/local/etc/profile.d/bash_completion.sh ]; then
. /opt/local/etc/profile.d/bash_completion.sh
fi

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/opt/local                          # 安装前缀,对应 brew --prefix
/opt/local/bin # 命令
/opt/local/etc # 已装软件的配置
/opt/local/etc/macports # MacPorts 自己的配置,sources.conf、macports.conf
/opt/local/include # 头文件
/opt/local/lib # 库
/opt/local/libexec # 不放进 PATH 的程序。MacPorts 本体在 libexec/macports
/opt/local/Library # 框架在 Library/Frameworks
/opt/local/man # 符号链接,指向 share/man
/opt/local/sbin # 系统管理命令
/opt/local/share # 手册、补全、locale 等与架构无关的文件
/opt/local/var # 运行数据:cache、db、run、spool,以及下面的 macports
/opt/local/var/macports # 下载缓存、编译目录、已安装归档、索引
/opt/local/var/macports/distfiles # 下载的源码包
/opt/local/var/macports/build # 编译中间目录
/opt/local/var/macports/software # 已安装 port 的归档
/Applications/MacPorts # 个别带图形界面的 port 会装到这里
  • 通过 MacPorts 安装的软件在 /opt/local 下面,不在 Homebrew 的 /usr/local/Cellar 或 /opt/homebrew/Cellar。

国内镜像

  • 这一步可选。要换镜像的话,放在后面的 selfupdate 之前做。软件列表来自 sources.conf 里带 [default] 的那一行,默认是:

1
rsync://rsync.macports.org/macports/release/tarballs/ports.tar.gz [default]
  • 北京镜像由清华大学维护,主机名是 pek.cn.rsync.macports.org。编辑 /opt/local/etc/macports/sources.conf,把上面那一行换成:

1
rsync://pek.cn.rsync.macports.org/macports/release/tarballs/ports.tar.gz [default]
  • 改完执行 sudo port -v selfupdate。其它地区的镜像列表在 Mirrors。

  • 2.10.0 起,MacPorts 本体的更新默认走 HTTP。macports.conf 里的 rsync_server、rsync_dir 只在 HTTP 失败、退回 rsync 时才用到。ports 树仍然看 sources.conf。

日常使用

  • 查帮助:port help,某个动作再加名字,例如 port help install。完整列表在 man 1 port,在线版是 Using MacPorts。

  • 和 Homebrew 的对应关系。后面按这个顺序说明每条命令做什么。

要做的事 Homebrew MacPorts
更新软件列表和工具本身 brew update sudo port selfupdate
搜索 brew search wget port search wget
查看信息 brew info wget port info wget
安装选项 一种构建 port variants vim
安装 brew install wget sudo port install wget
已安装 brew list port installed
装了哪些文件 brew list wget port contents wget
点名安装的包 brew list --installed-on-request port echo requested
可升级 brew outdated port outdated
升级 brew upgrade sudo port upgrade outdated
卸载 brew uninstall wget sudo port uninstall wget
删掉升级留下的旧版本 brew cleanup sudo port uninstall inactive
依赖 brew deps wget port deps wget
递归依赖 brew deps --tree wget port rdeps wget
谁依赖它 brew uses --installed wget port dependents wget
间接依赖它的包 brew uses --installed --recursive wget port rdependents wget
先看不再被需要的依赖 brew autoremove --dry-run port echo leaves
卸掉不再被需要的依赖 brew autoremove sudo port uninstall leaves
清编译目录 安装结束会清掉 sudo port clean wget
清理下载缓存 brew cleanup sudo port clean --dist all
回收磁盘 brew cleanup 与 brew autoremove sudo port reclaim
启动后台服务 brew services start nginx sudo port load nginx
停止后台服务 brew services stop nginx sudo port unload nginx
检查环境 brew doctor port diagnose

更新

  • 装新软件、查过期包之前,先更新 MacPorts 本体和 ports 树:

1
sudo port selfupdate
  • 环境异常时用 port diagnose,对应 brew doctor。

查找

  • 按名字和描述搜索。port list 会把索引里所有包都打出来,又长又慢。查某个软件用 port search,查已安装的包用「查看已安装」里的 port installed。

1
2
3
4
5
6
port search wget              # 按名字和描述搜索
port search --name --line 'php*' # 按名字匹配,一行一条;可用通配符
port info wget # 版本、描述、依赖、homepage、variants
port variants vim # 安装前可勾选的编译选项
port deps wget # 直接依赖
port rdeps wget # 递归依赖

安装软件

  • 有预编译包、并且许可证允许再分发时,port install 会从 packages.macports.org 下载,跳过编译。没有和当前变体匹配的二进制包时,就在本机按这次的选择编译。变体见下一节。

1
sudo port install wget        # 安装 wget
  • 编译失败时先清掉编译目录再重试,日志路径会打在终端里。clean 还能删下载缓存,见「清理」。

1
2
3
4
sudo port clean wget          # 清掉编译目录
sudo port -v install wget # 详细输出,便于排查
# 编译读到 /usr/local 里的 Homebrew 文件时,加上 -t,安装过程不看这些路径
sudo port -t install wget # 安装时忽略 /usr/local 等路径

变体

  • MacPorts 把可选功能做成变体,安装时才选定。默认只打开 Portfile 里标出的那些,port variants 行首的 [+] 就是默认项。vim 的默认是 +huge,Python、Lua、Perl、Ruby 都不在里面。+python312 会把 python312 加进依赖;不加,依赖里就没有 Python。本机 port info vim 的库依赖是 gettext、libiconv、ncurses。

  • Homebrew 官方仓库里,一个 formula 只有一种构建。vim 的 configure 打开 Python 3、Lua、Ruby、Perl 和 cscope,并关掉 GUI。Python 3、Lua、Ruby 标成编译和测试依赖:有 bottle 时,二进制里已经带这些接口,语言包要另外装才用得上;从源码编译时,这三个会作为编译依赖装进来。当前这份 formula 的 bottle 只有 Apple Silicon 和 Linux,没有 macOS x86_64。本机是 Intel,brew install vim 会按这一份配置在本机编译。想要别的组合,需要自己改 formula,或用仍保留选项的第三方 tap。

  • 两边的二进制包都是已经编好的一份文件,安装时不能再改编译选项。Homebrew 2.0.0 起,官方仓库拿掉了 brew install vim --with-python 这类选项,只分发这一份 bottle;带选项的构建进不了 CI,也做不成 bottle。MacPorts 仍接受变体:有和当前变体匹配的 archive 就下载,没有就按这次的选择在本机编译。

MacPorts Homebrew
功能 安装时用变体选择 formula 里固定一种构建
依赖 选中的变体才引入 写在 formula 里
二进制 变体对上 archive 就下载,否则按该变体编译 bottle 是那一种构建;没有就按同一配置编译
  • 装之前先看有哪些变体。+ 打开,- 关掉默认项。多个变体写在同一条命令里。名字写错时不会提示,因为它也可能是某个依赖的变体。装完用 port installed 核对。

1
2
3
4
port variants vim                              # 查看可用变体,行首 [+] 为默认项
port info vim # 版本、描述、依赖、homepage、variants
sudo port install vim +python312 # + 打开变体
sudo port install apache2 -preforkmpm +workermpm # - 关掉默认项,+ 打开另一项
  • 给 YouCompleteMe 用时,Vim 需要 Python 接口,加上 +python312。已经装过默认的 vim 时,再执行这条会按新组合重新构建,原来的 +huge 还在。本机当前是:

1
vim @9.2.0321_2+huge+python312 (active)
  • 以后升级会沿用这次的变体。要改组合,在安装或升级命令里把变体写上。变体说明见 Port Variants。Homebrew 拿掉官方仓库安装选项的说明见 Homebrew 2.0.0。

查看已安装

1
2
3
4
port installed                # active 是当前在用的版本
port installed wget # 只看某一个包
port contents wget # 这个包装了哪些文件
port echo requested # 你点名安装的包,不含自动拉进来的依赖

升级

  • 先跑过「更新」里的 selfupdate,再看已安装的包里哪些有新版本。官方建议整批升级。变体沿用安装时的选择,见「变体」。

1
2
3
port outdated                 # 列出有新版本的已安装包
sudo port upgrade wget # 只升这一个包
sudo port upgrade outdated # 升级全部过期的包
  • 升级后旧版本还留在磁盘上,标成 inactive,出问题可以切回去。确认新版本可用之后再删,命令在「卸载软件」。

卸载软件

  • 这里卸的是某一个 port。卸掉 MacPorts 本体见文末「卸载 MacPorts」。

  • 还有别的包依赖它时,单独 uninstall 会被拒绝。先看谁依赖它,再决定是只卸这一个,还是连同依赖它的包一起卸。

1
2
3
4
5
port dependents openssl       # 已安装的包里,谁直接依赖它
port rdependents openssl # 递归:谁间接依赖它

sudo port uninstall wget # 只卸这一个
sudo port uninstall --follow-dependents wget # 连同依赖它的包一起卸
  • 自动拉进来、已经没有人依赖、也不是你点名安装的包,先看再卸:

1
2
port echo leaves              # 列出不再被需要的自动依赖
sudo port uninstall leaves # 卸掉这些包
  • 升级留下的旧版本:

1
sudo port uninstall inactive  # 删掉升级留下的旧版本

清理

  • clean 删的是安装过程留下的临时文件,已安装的软件还在。源码包在 /opt/local/var/macports/distfiles,安装成功后默认留着,下次再装可以不再下载。编译目录在 build,成功安装后一般已经清掉;编译失败时会留下。

1
2
3
4
5
sudo port clean wget              # 默认 --work,只删编译目录
sudo port clean --dist wget # 只删这个包下载的源码包
sudo port clean --dist all # 删掉全部源码包。对应 brew cleanup 的下载缓存
sudo port clean --all wget # 源码包、编译目录、下到一半的二进制包、日志
sudo port clean --all installed # 对已安装的包做上一行这一套,软件本身还在
  • --archive 只删临时目录里没下完的二进制包,不会动 /opt/local/var/macports/software 里已经装好的归档。旧版本占的空间用「卸载软件」里的 sudo port uninstall inactive。

  • reclaim 范围更大:卸掉 inactive 的包和不再被需要的依赖,并删用不到的源码包。先看清单再删:

1
2
3
sudo port reclaim -y     # 只列出,不删除
sudo port reclaim # 逐项确认
sudo port reclaim -N # 不询问,直接删

服务

  • 带后台进程的 port,用 load / unload 交给 launchd。对应 Homebrew 的 brew services。

1
2
3
sudo port load nginx                   # 启动服务,交给 launchd
sudo port unload nginx # 停止服务
sudo launchctl list | grep macports # 标签一般是 org.macports.<名字>

和 Homebrew 装在一起

  • 两个前缀不同,文件不会写进对方的目录。/opt/local/bin 排在 PATH 最前面时,同名命令会先用到 MacPorts 的那一份。~/.zshrc 若在 ~/.zprofile 之后又改了 PATH,见「安装 MacPorts」里末尾再声明的那三行。用 which git 确认当前是哪一个。

  • 同一个软件只保留一套。编译 MacPorts 的包时如果和 /usr/local 里的库冲突,安装命令加上 -t,见「安装软件」。

  • gem 只安装 Ruby 的库,不能用来装 git、wget、nginx 这种通用软件,也不能代替 MacPorts。

升级 macOS 之后

  • 同一个大版本里的小更新,例如 Sequoia 15.7 升到 15.7.9,原来的安装继续用。更新 MacPorts 和已装软件:

1
2
sudo port selfupdate          # 更新 MacPorts 本体和 ports 树
sudo port upgrade outdated # 升级已安装的过期软件
  • 跨大版本,例如 Sequoia 15 升到 Tahoe 26,已装的 port 是按旧系统编译的,只跑 selfupdate 不够。MacPorts 2.10.0 起用 port migrate,它会按当前系统重装 MacPorts 本体,并重装和当前系统不兼容的 port。步骤见 Migration。

1
2
3
4
5
6
7
# 1. 先把 Xcode Command Line Tools 更新到新系统对应的版本
xcode-select --install

# 2. 重装本体和不兼容的 port。默认只处理你点名安装的包及其依赖
sudo port migrate
# 连自动装上的依赖一起迁移
sudo port migrate --all
  • 迁移前会给已安装的包做快照。有的包装失败时,等上游修好后再执行 sudo port restore --last。快照不再需要时,用 port snapshot --list 看编号,再 sudo port snapshot --delete <编号> 删掉。

  • 2.10.0 之前没有 port migrate。那种情况要先装上当前系统对应的 pkg(已安装的 port 还在),再执行上面的 sudo port migrate。

卸载 MacPorts

  • 先卸掉所有 port,再删掉 MacPorts 本体。配置和数据库在 /opt/local/etc,需要保留就先挪走。完整步骤见 Uninstall MacPorts。

1
2
3
4
5
6
7
8
9
10
11
12
sudo port -fp uninstall installed   # 强制卸掉全部已装 port;-f 强制,-p 遇错继续

sudo dscl . -delete /Users/macports # 删掉安装时创建的 macports 用户
sudo dscl . -delete /Groups/macports # 删掉 macports 组

sudo rm -rf \
/opt/local \ # 安装前缀与配置、数据库
/Applications/MacPorts \ # 带图形界面的 port
/Library/LaunchDaemons/org.macports.* \ # launchd 服务
/Library/Receipts/MacPorts*.pkg \ # 安装包收据
/Library/Tcl/macports1.0 \ # Tcl 扩展
~/.macports # 用户级配置
  • Catalina 及更新的系统开着 SIP 时,要先删 macports 用户,再删 /opt/local。