MacPorts -- 创建自己的 Port
摘要
- Portfile 是 MacPorts 的安装脚本,对应 Homebrew 的 formula。官方 ports 树里没有的软件,可以放在本机的一个目录里,在
sources.conf登记之后,用同样的port install安装。 - 本地目录要写成
file://,放在带[default]的那一行前面,并加上[nosync]。Portfile 放在分类/名字/Portfile。改完在仓库根目录执行portindex。 - 下面用 brew -- 创建自己的 Formula 里的
color_echo1.0.0 作例子。源码是 GitHub tagv1.0.0的归档,命令文件在bin/color_echo。安装和日常命令见 MacOS软件包管理器--MacPorts。没有brew tap这种命令,个人仓库是一份 Git 上的 ports 树,别人克隆后写进自己的sources.conf。 - 本机 MacPorts 是 2.12.7。
sources.conf里的默认行是ports.tar.gz,不是指南旧示例里的ports.tar。
本地仓库
-
官方 ports 树由
selfupdate覆盖。自己的 Portfile 放在另一个目录,port sync不去动它。步骤见 Local Portfile Repositories。 -
编辑
/opt/local/etc/macports/sources.conf。file://写在带[default]的那一行前面。同一个名字出现多次时,MacPorts 用先出现的那一份。[nosync]让selfupdate跳过这个目录。本机改完是:
1 | file:///Users/hanqf/ports [nosync] |
-
目录名的第一层是 Portfile 里的主分类,第二层是 port 名字。
color_echo放在sysutils:
1 | mkdir -p ~/ports/sysutils/color_echo |
Portfile
-
文件是 Tcl。第一行是编辑器模式标记,后面先声明
PortSystem,GitHub 上的项目再加github1.0。字段说明见 Portfile Reference,GitHub 这一组见 PortGroups 里的 github。 -
github.setup的四个参数是作者、仓库、版本、tag 前缀。1.0.0配v,tag 就是v1.0.0。github.tarball_from默认是releases,读的是 Releases 页面上挂的附件。color_echo用的是 tag 归档,要改成archive,地址才是https://github.com/hanqunfeng/color_echo/archive/refs/tags/v1.0.0.tar.gz。 -
这份归档解压后的目录是
color_echo-1.0.0。archive会打开extract.rename,工作目录和这个名字对齐。脚本在bin/color_echo,没有编译步骤。 -
创建
~/ports/sysutils/color_echo/Portfile文件:
1 | # -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:ft=Portfile:et:sw=4:ts=4:sts=4 |
-
各字段含义如下:
- 第一行:给编辑器看的模式行,标明这是 Tcl / Portfile,缩进 4 空格;MacPorts 本身不解析它。
PortSystem 1.0:启用 Portfile 基础设施,每个 Portfile 都要写。PortGroup github 1.0:引入 GitHub 相关辅助命令,后面才能用github.setup、github.tarball_from。github.setup:四个参数依次是 GitHub 作者、仓库名、版本号、tag 前缀。这里是hanqunfeng/color_echo/1.0.0/v,对应 tagv1.0.0。github.tarball_from archive:从 tag 归档下载;默认releases会去 Releases 附件,对不上时要改成archive。revision:同一上游版本下 Portfile 自己的修订号。只改安装步骤时加 1;换了上游版本则改github.setup里的版本,并把revision回到0。categories:软件分类,第一项应和本地目录名一致,这里是sysutils。platforms any:不限运行平台;纯脚本常见写法。supported_archs noarch:没有按 CPU 架构区分的二进制,arm64 / x86_64 共用同一份。license:上游许可证,这里是MIT。maintainers:维护者;只在本机用可写nomaintainer。description:一句话短描述,出现在port search、port info。long_description:长描述。{*}${description}表示直接展开复用短描述。checksums:校验下载到的压缩包。rmd160、sha256是摘要,size是字节数;三个都针对.tar.gz本身。use_configure no:跳过./configure。build {}:空的编译阶段,表示没有编译步骤。destroot { ... }:安装阶段。文件先写到${destroot}${prefix},MacPorts 登记后才出现在/opt/local。${worksrcpath}是解压后的源码目录,xinstall把bin/color_echo装成可执行文件。
-
sha256与 brew -- 创建自己的 Formula 里同一份v1.0.0.tar.gz一致。rmd160和size先占位,下一步用本机算出来的值换掉。只给自己用时,maintainers写nomaintainer即可。 -
安装阶段写到
${destroot}${prefix}。MacPorts 登记之后,文件才出现在/opt/local。${prefix}在本机是/opt/local。
校验和
-
三个值都是压缩包本身的,不是解压后的文件:
1 | curl -fL -o ~/Downloads/color_echo-1.0.0.tar.gz \ |
-
sha256的结果应是9450952a4b477c83ea2d7e28386d6ae38132bf68c46746aa218c03c21aa75f6d。rmd160写进 Portfile 的第一段,stat的字节数写到size。 -
也可以先按上面的占位保存,再执行
sudo port -v checksum color_echo。对不上时,终端会给出算出来的rmd160、sha256和size。改完 Portfile 后重新portindex。
索引、检查、安装
-
在仓库根目录重建索引。这个目录属于自己,不用
sudo:
1 | cd ~/ports |
-
索引建成之后,
port能看到它。port file用来确认读的是本地这一份:
1 | port info color_echo # 查看版本、描述、依赖等 |
-
lint检查必填字段、依赖是否存在、有没有过时写法。通过后再装:
1 | sudo port install color_echo # 安装本地 Portfile 定义的 color_echo |
-
port install把命令写到/opt/local/bin,不改当前终端已经读入的 PATH。which只在这份 PATH 里找。这个终端若是在写好~/.zprofile、~/.zshrc之前打开的,或者不是登录 shell、没读到~/.zprofile,which color_echo会显示找不到,文件其实已经在磁盘上。ls能看到它,就说明装上了。 -
新开一个终端再查。PATH 里已经有
/opt/local/bin时,同一终端里安装后which也能看到。结果应是/opt/local/bin/color_echo。同名命令排在更前面时,见 MacOS软件包管理器--MacPorts 里~/.zshrc末尾再声明 MacPorts 路径的那一节。
1 | which color_echo |
改过之后
-
只改了安装步骤、版本号没变,把
revision加 1。换了上游版本,就改github.setup里的版本,并重算校验和,revision回到 0。 -
Portfile 改完都要重新索引,再升级:
1 | cd ~/ports |
-
不再需要时:
1 | sudo port uninstall color_echo |
个人仓库
-
MacPorts 没有
brew tap user/repo。能分发给别人的,是一个目录结构和本机~/ports相同的 Git 仓库:分类/名字/Portfile。别人克隆下来,在自己的sources.conf里加一行file://,就能port install。说明见 sources.conf。 -
把已经写好的
~/ports推上去即可。PortIndex由使用者本机生成,不必提交:
1 | cd ~/ports |
-
使用者克隆后做一次索引,再把绝对路径写到带
[default]的那一行前面:
1 | git clone https://github.com/hanqunfeng/my-ports.git ~/my-ports |
1 | file:///Users/hanqf/my-ports [nosync] |
-
[nosync]时,selfupdate不会碰这个目录。仓库有更新时,自己git pull,再在仓库根目录执行portindex。 -
去掉
[nosync]后,selfupdate发现这是 Git 检出,会向默认远程 pull,并重建索引。执行同步的是macports用户,这个目录要让它可写。家目录通常不行,检出放到它能写的路径,例如/opt/local/var/macports/sources/github.com/hanqunfeng/my-ports。
提交到官方仓库
-
个人仓库要别人改自己的
sources.conf才能用。希望不改配置、一条port install color_echo就能装到,把 Portfile 交给 macports-ports。步骤就是 fork、单独一个分支、一条提交、开 pull request。耗时间的是这个仓库本身很大,以及 GitHub Actions 编译和别人审 Portfile。Portfile 写法见 Creating a Portfile,提交说明见 CommitMessages 和 Using Git and GitHub。 -
提交前在本机走通
port lint --nitpick color_echo和sudo port install color_echo。准备自己维护这个 port,把maintainers nomaintainer改成maintainers {@hanqunfeng}。留给别人维护就保持nomaintainer。 -
官方仓库是 macports/macports-ports。没有推送权限,直接克隆官方地址后,
git push会被拒绝。先在 GitHub 上点 Fork,得到自己的hanqunfeng/macports-ports,再克隆这一份。只克隆一层历史,避免把整个 ports 树的历史都拉下来:
1 | git clone --depth 1 https://github.com/hanqunfeng/macports-ports.git |
-
只加入 Portfile。
portindex生成的PortIndex属于本机索引,不放进仓库。提交标题先写 port 名字:
1 | git add sysutils/color_echo/Portfile |
-
在 GitHub 上对
macports/macports-ports的master开pull request。Actions 会按这份 Portfile 编译。本例的 pull request -
审查意见要改文件时,改在同一条提交上再推:
1 | git add sysutils/color_echo/Portfile |
-
几天没有回复,可以给 macports-dev 写信,信里放上这个 pull request 的链接。合并之后,
selfupdate就能从官方树里装到color_echo,本机sources.conf里的file://那一行可以删掉。本地目录排在[default]前面时,同名 port 仍会用本地这份。