Linux 发行版:“强迫症患者”们的共识社区

世界上有几百个还在更新的 Linux 发行版。新手常常感叹挑花了眼,换来换去也找不到自己满意的。维护一个发行版需要花费很多时间、精力,为何人们要这样“重复劳动”呢?

  • “强迫症患者”

我小时候追求整齐、秩序,无论是家里的电灯开关还是电脑上的图标,一定要排列的整整齐齐,不惜自己接电线、一个个重命名文件。“我的电脑”、“我的文档”、“网上邻居”,下面的蓝色 e 名字太长,就改叫“上网浏览”吧……然而随着安装了越来越多的软件,这些“秩序”被不断破坏,自己不断妥协。有的程序在我的文档里乱放目录——忍。有的程序会自动下载更新,然后在我代码写到一半的时候弹出来更新提示——忍。有的程序会带各种运行时包安装、替换系统文件导致另外一个程序运行不了——忍。

每次出了大问题的时候,所有人都告诉我:“现在只能重装了。”

2008年的时候,厌倦了折腾各种魔改定制 WinPE 的我首次下定决心安装了 Ubuntu 8.04,从此打开了新世界的大门。

不再需要依赖猜测。手握源代码,就如同掌握了施工的图纸,一切不符合心中秩序的地方都能找到原理。一群志同道合的前辈早已构建了井井有条的目录结构、依赖关系、软件仓库、……一切都显得那么美好。和每个第一次玩 Linux 桌面的折腾狂一样,我花了很多时间试图让 GTK+ 和 Qt 的程序界面一样而且好看,以及折腾 compiz 特效。

Continue reading Linux 发行版:“强迫症患者”们的共识社区

尝鲜可能比 sunpinyin 好一点的新拼音输入法

大概很多人还不知道,在老K同学偷偷默默开发了很久后,新一代的 fcitx5 已经“能用”了。不过因为还处在早期开发阶段,现在只有拼音输入法能用,而且输入界面极挫,没有配置界面和任何命令行帮助信息等。

先来一段 demo:

下面简单介绍一下 Arch 里的安装及配置方法:

首先从 AUR 中安装相关的软件包:

yaourt -S fcitx5-git fcitx5-qt-git fcitx5-gtk-git fcitx5-chinese-addons-git

依赖中其他的相关包会被 yaourt 自动安装。如果你更喜欢手动安装,可以参考下面的顺序:

xcb-imdkit-git -> fcitx5-git -> libime-git -> fcitx5-chinese-addons-git -> fcitx5-gtk-git -> fcitx5-qt-git

注意原来的 fcitx 和这系列软件包冲突,可能会被提示卸载。

Continue reading 尝鲜可能比 sunpinyin 好一点的新拼音输入法

Arch Linux devtools 简介 – 在干净的环境里编译软件包

devtools 是 Arch Linux 开发者们用来往官方仓库里推进软件包使用的一系列工具。由于里面有许多工具可能不是我们常用的,这里主要介绍里面的一部分——用于在干净的环境中编译软件包的命令。

为什么要在干净的环境里编译软件包?这里有几个常见的理由:

  • 避免忘记写依赖 – 当前环境中已安装的软件包可能在普通的 makepkg 过程中被忽略,最后在 depends 或者 makedepends 等列表中缺失。
  • 避免编译过程污染环境 – 因为一些你可能没有想到的原因,编译过程中可能会对你当前的系统产生污染,比如跑 npm install 的时候可能会把缓存塞到 $HOME。
  • 避免因环境不干净导致的奇怪编译错误 – 你的环境中可能有各种不干净的情况,比如 profile.d 里覆盖了 gcc 等常见命令、/usr/local/bin 里有覆盖常用命令、用非系统包管理器安装(覆盖)了一些东西等。
  • 或者你只是不想把这个软件包编译时需要的一堆乱七八糟的依赖都装在自己机器上。

如果你有这样的需求,可以考虑使用 devtools。安装过程很简单

pacman -S devtools
Continue reading Arch Linux devtools 简介 – 在干净的环境里编译软件包

给 Arch 打一个包 – Python 模块篇

这是一篇简化的教程,如果你有一个喜爱的 Python 模块不在 Arch 仓库里,AUR 里也没有,可以尝试读下去。

准备

对 Python 模块来说,一般仅仅一个 PKGBUILD 文件就足以完成所有的事情。现在你可以打开你最喜欢的文本编辑器,把下面这一个简单的 PKGBUILD 模板复制进去:

# $Id$
# Maintainer: Felix Yan <felixonmars@archlinux.org>

pkgbase=python-whatever
pkgname=('python-whatever' 'python2-whatever')
pkgver=0.4.3
pkgrel=1
pkgdesc='Easy way to make anonymous functions by partial application of operators'
arch=('any')
license=('BSD')
url='https://github.com/Suor/whatever'
makedepends=('python-setuptools' 'python2-setuptools')
checkdepends=('python-pytest-runner' 'python2-pytest-runner')
source=("$pkgbase-$pkgver.tar.gz::https://github.com/Suor/whatever/archive/$pkgver.tar.gz")
sha512sums=('162d66753ef4fb15279150b7fa953b4ecf086e2b36cc77531dac099ff4a25b3458af627bdf52e168b7b4b2163a1445f35c2c656b1c10c0c73502d2357ba42dd8')

prepare() {
  cp -a whatever-$pkgver{,-py2}
}

build() {
  cd "$srcdir"/whatever-$pkgver
  python setup.py build

  cd "$srcdir"/whatever-$pkgver-py2
  python2 setup.py build
}

check() {
  cd "$srcdir"/whatever-$pkgver
  python setup.py pytest

  cd "$srcdir"/whatever-$pkgver-py2
  python2 setup.py pytest
}

package_python-whatever() {
  depends=('python')

  cd whatever-$pkgver
  python setup.py install --root="$pkgdir" --optimize=1
  install -D -m644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
}

package_python2-whatever() {
  depends=('python2')

  cd whatever-$pkgver-py2
  python2 setup.py install --root="$pkgdir" --optimize=1
  install -D -m644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
}

# vim:set ts=2 sw=2 et:

因为距离 Python 2 的废弃时间(2020年)还早,Arch 仓库中的 Python 模块包通常同时提供 Python 2/3 模块。上面例子里的 Python 软件包名叫 whatever,这也同样是它在 PyPI 中的名字。如果原来的包名中包含大写字母,在制作软件包时需要改成小写。

Continue reading 给 Arch 打一个包 – Python 模块篇

记一次磁盘数据损坏的修复过程

昨晚我大概没有把硬盘插紧,零点(一堆计划任务执行时)在 dmesg 里看到了大量 ext4/SATA 错误。今天开机时 BIOS 直接提示没有可引导的设备。下面记录了我所有的测试和恢复步骤:

  • 用备份盘开机,首先发现 /dev/sda 存在,但没有任何 /dev/sda*。判断是分区表损坏。
  • 执行 testdisk 快速扫描找回分区表,因为盘里只有一个分区,这一步很顺利。继续操作写回分区表。
  • 此时 /dev/sda1 已经出现,尝试 mount,失败。提示 ext4 没有 journal。
  • 执行 fsck.ext4 /dev/sda1,期间提示包括 root node 不是 directory 等一系列错误,一路 y 下去重建了 root node,并把一堆目录丢到了 /lost+found。
  • 重新 mount,成功挂载到 /mnt。
  • 进去查看,发现只有一个 ./lost+found。果然 / 目录里的信息丢失了。
  • 进入 ./lost+found,里面有二十来个目录。一个个进去查看。
  • 根据目录内容,将 home、var、usr、etc、srv、opt、root、boot 猜出来,并移动回对应的 /mnt/*。剩下的多是空目录,放弃。
  • 尝试 arch-chroot,失败,想起来还需要重建 / 里的一些 symlink 和空目录。
  • mkdir dev media mnt net proc run sys,然后创建 {s,}bin -> usr/bin,lib{,64} -> usr/lib 的 symlink。
  • 再次 arch-chroot,成功。
  • 执行 pacman -S filesystem 以防万一。
  • 运行 grub-install 恢复引导。
  • 运行 pacman -Qkq 比对文件,发现只有 visual-studio-code 包内容有丢失,暂时不管。
  • 重启,成功进入系统,重新安装 visual-studio-code。

至此,虚惊一场的数据丢失已经完全恢复,总共历时 1 个小时(前面大量的时间花在了让备份盘能启动起来,上一次备份出现了一点差错……)。

最后赞美一下坚强的 ext4!

修复 Android ROM 的 Google 网络定位

一些定制、第三方 ROM 在安装了 Google 框架后,仍然无法使用其网络定位功能。我在网上搜索了许多资料,整理如下。

本文假设你的设备已经 Root,并已经安装了 Google 框架。我测试用的 ROM 为一加氢 OS。

一、准备工具

需要准备的工具有 zip、unzip、apktool、adb、zipalign,以及一个好使的文本编辑器。

(注意 zipalign 工具可能不在 $PATH 中,如 Arch AUR 包 android-sdk-build-tools 安装后会放在 /opt/android-sdk/build-tools/$pkgver/zipalign)

二、提取需要的资源

取出 ROM 中的 framework-res.apk,并反编译得到需要修改的文件:

adb pull /system/framework/framework-res.apk
apktool if framework-res.apk
apktool d framework-res.apk

三、修改文件

修改位置提供商相关设置,使用 Google 提供网络定位。

1、修改 framework-res/res/values/arrays.xml,找到 config_locationProviderPackageNames 的位置,确保 Google 在列表中。如我的 ROM 默认只有 com.android.location.fused 和 com.amap.android.location 两项,这时应当加入 com.google.android.gms,使得最终结果类似这样:

    
        com.google.android.gms
        com.android.location.fused
        com.amap.android.location
    

Continue reading 修复 Android ROM 的 Google 网络定位

不双清给一加氢 OS 刷上 Google Apps

最近入爪一台一加 3,解锁刷 root 后几乎配置完了所有东西,然后才发现忘记了 gapps。网上看到许多人在这种情况下刷 gapps 遇到了各种各样的问题,一般都被建议双清解决。我查找了一些资料后,决定试试不双清自己修复权限问题。

安装 OpenGApps

这里的假设是已经刷过第三方 Recovery,我这里是 TWRP。从 OpenGApps 网站下载对应的包(我这里对应的是 ARM64、6.0),我选择了 nano 包。

重启进入 Recovery 刷入此包。然后不要急着重启,因为大量网友反应此时重启后会不断 fc。我在一加论坛找到了这样的方法,经实测有效(针对 TWRP,其他 Recovery 请自行调整):此时应回到 Recovery 首页,进入 Mount 页面勾上 System,然后回到首页依次选择 Advanced -> File Manager -> system -> priv-app -> SetupWizard,然后点击右下的选择气泡,最后点击 Delete 删除这个文件夹。

重启正常进入系统后,点击 Google Play 或直接添加 Google 帐号即可。

遇到的问题

一、Google Play 无法正常下载应用

Google Play 下载或更新任何应用时,提示 DF-DLA-15 错误。我找到的方法是进入应用管理清空 Google Play 及 Google Play Services 的数据后重试。

二、Google 联系人同步选项消失

帐户管理中的 Google 帐号内只剩下健身、人脉、应用数据,联系人同步不见踪影。Reddit 上有人指出授权 sync adapter 读写联系人即可,但 H2OS 的应用管理界面中似乎没有办法直接操作,因此我用 adb 手动进行了授权:

$ adb shell
$ pm grant com.google.android.syncadapters.contacts android.permission.READ_CONTACTS
$ pm grant com.google.android.syncadapters.contacts android.permission.WRITE_CONTACTS

执行后,重启手机即可,不需要如原文所说删除、重新添加 Google 帐号。

更新:此方法在三星 Galaxy Note 7 原生系统上也测试成功,理论上还可以推广到更多系统。有遇到 Google Play Services 不断崩溃退出的问题,另 grant 了如下权限解决:

$ pm grant com.google.android.gms android.permission.ACCESS_FINE_LOCATION

Pacman Hooks 简介

Pacman 5.0 带来了 Hooks 支持,但在大规模应用前,我们留出了一个多月的时间来让用户先升级到 Pacman 5.0(因为同时升级 pacman 和有定义 hooks 的包会导致无法正常执行这些 hooks)。现在距离 Hooks 正式投入使用已经过去了一个月,我觉得是时候介绍一下 Hooks 和如何使用它了。

先来看一个简单的 Hook:

[Trigger]
Type = File
Operation = Install
Operation = Upgrade
Target = usr/lib/tmpfiles.d/*.conf

[Action]
Description = Creating temporary files...
When = PostTransaction
Exec = /bin/sh -c 'while read -r f; do /usr/bin/systemd-tmpfiles --create "/$f"; done'
NeedsTargets

这个 Hook 的作用是:当检测到安装或更新的包文件中存在 usr/lib/tmpfiles.d/*.conf 时,在更新后对每个文件调用 systemd-tmpfiles --create 方法。前面的检测部分定义在 [Trigger] 部分,后面执行的操作定义在 [Action] 部分。下面我们分别了解一下这两部分。

一、[Trigger] 部分

首先需要了解的是可以用于触发的条件类别(Type)。上面的例子里使用了文件(File),即当操作中的包中存在对应文件时触发。另一个可选的 Type 是软件包名(Package),即直接匹配操作中的包名。

接下来,操作(Operation)选项限制了对软件包的操作类别。可以选择的操作有安装(Install)、更新(Upgrade)和删除(Remove)。一个 Hook 需要在多种操作执行时,如例子中那样写成多行即可。常用的组合有三种全部写上(更新缓存、数据库等)、写 Install+Upgrade(执行安装时的一次性操作)及与之对应的 Upgrade+Remove(卸载时的一次性操作)。

最后,我们需要定义具体的目标(Target)。如果目标是文件,这里需要写其相对根目录的完整路径,但需要去掉开头的 / 字符。通配符(*、?)可以使用,具体匹配时会使用 fnmatch 方法。同样的,在一个 Hook 中可以定义多个目标,类似例子中的 Operation 那样写成多行即可。

Continue reading Pacman Hooks 简介

Arch Linux [testing] 系列仓库简介

Arch Linux 中的 [testing]、[community-testing] 和 [multilib-testing] 三个仓库构成了 [testing] 系列仓库。由于网上许多地方对 [testing] 系列仓库存在一定的误解,我作为一只开发者,想藉此文介绍一下 [testing] 系列仓库。

一、[testing] 系列仓库里有哪些包?
主要有三类,按照数量排序应该是:由 soname 等 TODO 产生的大批完全未经测试的包;重要软件包、软件集合的更新、不靠谱上游推出的新版本、上游大版本更新等维护者虽然测试过但是不敢肯定没问题的包;因为维护者没有时间等情况而完全未经测试的包。注意,这里多数的情况都是完全未经测试的包,keep in mind。

二、什么情况下软件包会留在 [testing] 系列仓库里很长时间?
主要也有三类:没人测试给 signoff 的;已知有问题但还未修复或不知道怎么修复还在研究的;维护者明明拿到 signoff 了(针对目标仓库是 [core] 的情况)或明明不需要 signoff(针对所有除了 [core] 以外的仓库)但还是觉得没把握的。简单来说,一般情况下你想用的新版软件不会在 [testing] 系列仓库里停留超过一个星期,除非它已经被发现有问题了。

三、开 [testing] 系列仓库需要满足什么条件?

  • 对当前系统中所有重要资料的备份。因为你不知道你装的下一个未经充分测试的包里是否会出现类似之前 bumblebee 或 steam 那样的灾难性错误。
  • 有从包括 bumblebee 在内的各种灾难中恢复的能力。几乎没有人能帮你完成这件事,所以你需要自己拥有这种能力。这里包括的不止是对修复针对性问题所需要的知识和/或找到这些知识的能力,还包括提前准备好修复工作中需要的工具,比如 USB 安装介质和你的备份。
  • 能接受系统在一段时间内不可用。 如果你正在工作中赶进度,或者明天就需要在一场演讲中用到,这不是一个开启 [testing] 跑更新的很好时机。请保证你在满足前两个条件的情况下,还能接受你的系统需要一段时间才能修复这个事实。无论是找到问题、修复还是重装系统,还有从备份中 恢复文件,都是需要一定时间的。

最后,如果读到这里还没被我吓跑,欢迎你像我一样启用 [testing] 系列仓库,并把发现的问题报告到 Arch Linux Bug Tracker

用 Jinja 的 Babel 支持来提取 Bottle STPL 模板里的待翻译文本

Bottle 的 SimpleTemplate (STPL) 模板引擎虽然颇为简单, 但是从我们长期的使用看来还是十分方便的. 然而美中不足的是, Bottle 并没有像 Jinja 那样对 Babel 的待翻译文本提取工具进行集成支持. 因此, 当我们需要提取模板中的待翻译文本的时候, 便只好把主意打到语法有些接近的 Jinja 上面了.

安装 jinja2 模块, 然后直接使用 Jinja 处理全部模板文件:

[jinja2: **.html]

(如果你仍在使用 Bottle STPL 的默认后缀 .tpl, 可以按照需要进行修改.)

这时我们会发现, 大部分的文本被提取出来了, 但是仍然可能会缺一些, 这是为什么呢?

阅读 Jinja 关于 Babel 集成部分的文档, 我们看到:

Until 2.7 template syntax errors were always ignored. This was done since many people are dropping non template html files into the templates folder and it would randomly fail.

也就是说, 如果遇到含有语法错误的模板, 这个模板会在提取时被忽略, 于是里面所有的待翻译文本都不会被成功提取出来.

在命令行里简单使用 jinja2.Template 测试了一下, 我发现问题主要出在 STPL 的 {{!variable}} 语法上面. STPL 使用 {{!variable}} 来表示禁用 XSS 过滤, 然而这个语法在 Jinja 中是非法的. 稍微研究了一下相关的代码, 我并没有找到通过配置就能解决的方案 (包括试图添加一个可选的 variable_start_string 以求 {{! 和 {{ 都能被识别, 但是这也被认为是不可能的).

于是, 我只好在 pybabel 的 wrapper 中用 monkey-patch 的方法来简单 hack 一下这个问题了:

#!/usr/bin/env python
import sys
from pkg_resources import load_entry_point

# Hack jinja2 to recognize bottle STPL's {{!variable}} syntax
try:
    from jinja2 import ext
    from StringIO import StringIO

    _babel_extract = ext.babel_extract
    def babel_extract_with_stpl_support(fileobj, *args, **kwargs):
        new_fileobj = StringIO(fileobj.read().replace("{{!", "{{"))
        return _babel_extract(new_fileobj, *args, **kwargs)

    ext.babel_extract = babel_extract_with_stpl_support
except ImportError:
    print("jinja2 not present, ignoring monkey-patching...")

if __name__ == '__main__':
    sys.exit(
        load_entry_point('Babel', 'console_scripts', 'pybabel')()
    )

这样, 所有的 {{! 会在尝试提取时被替换为 {{. 经过测试, 这个修改过的 babel_extract_with_stpl_support 成功地提取出了我们所有模板里的所有待翻译文本.

QR Code Business Card