标签 MacPorts 下的文章

虽然最理想的情况是所有依赖的项目都分别打包到不同的 port,但不同包对同一个依赖项目可能有不兼容的要求。OpenSSL、Boost 这种基础包可以通过拆包来解决兼容问题,例如 Boost 在 MacPorts 就打包了 boost169boost171boost173 等等很多个版本,但这也要求所有这些子包的使用方都要对编译 flags 进行修改,才能让编译器找到正确版本的依赖,导致整个过程非常复杂,维护也非常耗费时间。

一般使用方不多的库不会有分版本的 subport,为了提供一个小众软件要求的版本实现多版本并存有点得不偿失。此时如果项目的构建系统本来就把依赖 vendor 到 Git 子模块里,我们就可以通过 distfiles 的手段把依赖库的源代码拉下来放到子模块的文件夹里。通过 master_sitesdistfiles-appendgithub 1.0 等关键字进行组合搜索可以找到 macports-ports 中可以参考的方案,最终效果如下。

PortGroup           github  1.0
PortGroup           meson   1.0

github.setup        joshkunz ashuffle 3.13.3 v
github.tarball_from archive
master_sites        ${github.master_sites}:ashuffle
distfiles           ${distname}${extract.suffix}:ashuffle

...

# BEGIN abseil (requires C++17 build)
set abseil_project  abseil-cpp
set abseil_version  20211102.0

master_sites-append https://github.com/abseil/${abseil_project}/archive/${abseil_version}:abseil
distfiles-append    ${abseil_project}-${abseil_version}${extract.suffix}:abseil
checksums-append    ${abseil_project}-${abseil_version}${extract.suffix} \
                    rmd160  bca4a16eaab1602cdc7ace8dd1ff82467b71b59e \
                    sha256  dcf71b9cba8dc0ca9940c4b316a0c796be8fab42b070bb6b7cab62b48f0e66c4 \
                    size    1884080

post-extract {
    foreach submodule [list subprojects/absl:${abseil_project}] {
        set submodule_target [lindex [split ${submodule} :] 0]
        set submodule_package [lindex [split ${submodule} :] 1]
        delete ${worksrcpath}/${submodule_target}
        move {*}[glob ${workpath}/${submodule_package}-*] ${worksrcpath}/${submodule_target}
    }
}
# END abseil

micromamba

首先使用 MacPorts 安装 micromamba$HOME/micromamba 并初始化 shell 配置。

sudo port install micromamba
micromamba shell init --shell=zsh --prefix=~/micromamba

执行完成后需要重开 shell 以加载 .zshrc,如果使用的 shell 不是 zsh 需要对应调整 --shell 参数。

此时 micromamba 已安装完成。如果不需要完整的 conda 功能,到这里就可以使用 micromamba 命令了。

mamba

为了兼容 conda 可以用 micromamba 安装 mamba 并重新初始化 shell 配置。

micromamba install mamba -n base -c conda-forge
mamba init zsh

重开一次 shell 之后就有完整的 mamba 环境了,可以运行 mambaconda 命令,但 activate 命令还需要用 micromamba

环境

除了安装 mamba 外,不建议使用 base 环境安装其他包。以下命令可以新创建一个名叫 sci 的环境并安装。

mamba create -n sci <list of packages>

如果觉得默认源包太少或太旧,可以启用 conda-forge 并设置为默认。源配置的调整建议在新环境创建前完成,否则依赖关系可能出问题。

conda config --add channels conda-forge
conda config --set channel_priority strict

试着使用 MacPorts 提供的 cmake 命令从源码编译 Krita,但编译依赖就出现了 include、链接缺少符号等问题。原因是 MacPorts 的 cmake 包有个 patch,会优先从 MacPorts 的 prefix 下查找。

--- Modules/Platform/Darwin.cmake.orig
+++ Modules/Platform/Darwin.cmake
@@ -237,7 +237,9 @@
     endforeach()
   endif()
 endif()
-list(APPEND CMAKE_SYSTEM_PREFIX_PATH
-  /sw        # Fink
-  /opt/local # MacPorts
-  )
+
+# prepend the MacPorts prefix to CMAKE_SYSTEM_PREFIX_PATH
+# might already exist after this if the user has specified it
+list(INSERT CMAKE_SYSTEM_PREFIX_PATH 0
+  __PREFIX__
+)

无奈之下只能自己编译一个 cmake 了。如果你的 MacPorts 使用了默认的 /opt/local 作为 prefix,需要在解压缩 cmake 源码后修改 Darwin.cmake 去掉这行 /opt/local # MacPorts 再安装。

qt.qpa.plugin: Could not find the Qt platform plugin "cocoa" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

这是由于 MacPorts 的 qt5 不适用普通 macdeployqt 命令,打包时去掉这个命令就能正常启动了。

AddressSanitizer,简称 ASan,是一个非常实用的内存错误类 bug 检测工具。但在 macOS 上调试符号是不会直接写到二进制文件里的,想要获取带有行号的堆栈追踪十分麻烦。

以下是在没有调试符号的情况下 ASan 报出的堆栈追踪示例,可以看到函数名,但无法精确定位到文件和行号。

    #0 0x110a17ab0 in __sanitizer::internal_strlen(char const*)+0x10 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5eab0)
    #1 0x1109dd87e in printf_common(void*, char const*, __va_list_tag*)+0x7ee (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x2487e)
    #2 0x1109ddae6 in wrap_vfprintf+0x66 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x24ae6)
    #3 0x10fe355d0 in v_do_log_to_file+0x260 (nvim:x86_64+0x1002cd5d0)
    #4 0x10fe35123 in logmsg+0x163 (nvim:x86_64+0x1002cd123)
    #5 0x1100f107b in tui_tk_ti_getstr+0xdb (nvim:x86_64+0x10058907b)

MacPorts 中开启 ASan 有 -fsanitize=address 就行,但想要获得更清晰的堆栈就复杂多了。

首先你需要在 Portfile 文件中添加以下几行,并确保编译过程有用到这些 flags。

configure.cflags    -O1 -g -fsanitize=address
configure.cxxflags  -O1 -g -fsanitize=address
configure.ldflags-append   -fsanitize=address -Wl,-object_path_lto,lto.o

-Wl,-object_path_lto,lto.o 用于保留 LTO 生成的临时文件,使用 -flto 链接时需要加到链接参数里。

修改完后安装时需要加 -s(从源码编译)和 -k(保留构建目录)选项,例如 sudo port -vsk install,否则无法生成调试用的 .dSYM 包。

安装完成后,就可以执行 dsymutil <binary> 在同目录下生成调试符号包 <binary>.dSYM。最后运行编译好的 <binary> 就能看到文件名:行号信息了。效果如下

    #0 0x101d3eab0 in __sanitizer::internal_strlen(char const*)+0x10 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x5eab0)
    #1 0x101d0487e in printf_common(void*, char const*, __va_list_tag*)+0x7ee (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x2487e)
    #2 0x101d04ae6 in wrap_vfprintf+0x66 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x24ae6)
    #3 0x1011575d0 in v_do_log_to_file log.c:327
    #4 0x101157123 in logmsg log.c:151
    #5 0x10141307b in tui_tk_ti_getstr tui.c:2155

测试完后记得删除 .dSYM 目录,避免日后二进制文件更新导致调试符号对不上。

参考资料

https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports

Note that on macOS you may need to run dsymutil on your binary to have the file:line info in the AddressSanitizer reports.

https://clang.llvm.org/docs/CommandGuide/clang.html

On Darwin, when using -flto along with -g and compiling and linking in separate steps, you also need to pass -Wl,-object_path_lto,<lto-filename>.o at the linking step to instruct the ld64 linker not to delete the temporary object file generated during Link Time Optimization (this flag is automatically passed to the linker by Clang if compilation and linking are done in a single step). This allows debugging the executable as well as generating the .dSYM bundle using dsymutil(1).