0%

termux-app修改包名

概述

Termux-app是一个Android上的Linux虚拟机工具,它拥有自己的包管理工具和软件源,可以实现很多Linux上的功能。源码地址:https://github.com/termux/termux-app

Termux在初始化安装时会从远端下载一个对应系统架构的bootstraps-arch.zip文件,它是一个Linux的基本环境,其目录结构如下:

1
2
3
4
5
6
7
8
9
10
bootstraps-arch/
├── bin
├── etc
├── include
├── lib
├── libexec
├── share
├── SYMLINKS.txt
├── tmp
└── var

bootstraps文件跟app的包名所绑定,如果需要将该功能集成到自己的Android项目中,则需要修改包名参数为自己的app包名,指定自定义的软件仓库,然后重新编译bootstraps.zip文件,在Termux-app项目中替换引入。

Compiling packages(termux-packages)

需要在机器上安装docker环境,由于编译termux-packages需要一些系统环境,因此使用docker来完成这个编译过程无疑是一个比较省时间的做法。docker的镜像名为:termux/package-builder

  1. 获取https://github.com/termux/termux-packages仓库

  2. 修改termux-packages/scripts/build/termux_step_setup_variables.sh(最好修改所有的package_name):

    1
    2
    "${TERMUX_PREFIX:="/data/data/$package_name/files/usr"}"
    "${TERMUX_ANDROID_HOME:="/data/data/$package_name/files/home"}"
  3. 运行docker脚本进入docker容器:./scripts/run-docker.sh

  4. 在容器中开始编译过程:./build-package.sh -a arch $lib_name,会在debs目录下生成对应的deb包。

    我在编译时使用的是-a参数值是all,必需的package可以在编译脚本中看到(下文会提到)。在build的过程中可能会出现依赖包下载失败的问题,可以在网上查找对应的依赖包下载之后放置到某个地方,然后修改对应packages的build.sh脚本参数,使之从自己定义的url出下载依赖包,build.sh的位置在:termux-packages/packages/$package/build.sh

Create apt repository

概述

由于termux软件源中的deb包是与包名绑定的,因此如果需要使用软件源则需要自己修改包名后重新编译对应的package包,然后将生成的deb包上传到自己的软件仓库上,本节主要介绍如何搭建一个apt repository。具体的指引可以参考https://wiki.debian.org/DebianRepository/Setup中的文档。

以下是我搭建的软件源目录结构:

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
hearing/
├── bootstraps
│   ├── bootstrap-aarch64
│   ├── bootstrap-aarch64.zip
│   ├── bootstrap-arm.zip
│   ├── bootstrap-i686.zip
│   └── bootstrap-x86_64.zip
├── repository
│   ├── dists
│ | └── stable
│ | ├── InRelease
│ | ├── main
│ | │   ├── binary-aarch64
│ | │   │   └── Packages
│ | │   ├── binary-all
│ | │   │   └── Packages
│ | │   ├── binary-arm
│ | │   │   └── Packages
│ | │   ├── binary-i686
│ | │   │   └── Packages
│ | │   └── binary-x86_64
│ | │   └── Packages
│ | ├── Release
| | ├── Release.gpg
| | └── repo.asc
│   └── pool
│   ├── aarch64
│    | ├── xxx.deb
│   ├── all
│    | ├── xxx.deb
│   ├── arm
│    | ├── xxx.deb
│   ├── i686
│    | ├── xxx.deb
│   └── x86_64
│    ├── xxx.deb
└── tmp
├── xxx.tar.gz
  • bootstraps目录下放的是最终编译成功的zip文件
  • repository是自己制作的软件仓库
  • tmp放置的是上一步编译中下载失败的一些依赖

引用

根据https://wiki.debian.org/DebianRepository/Setup中的说法,仓库分为两种,一种比较简单的是trivial archive,而另外一种复杂的仓库称为official archive。在一个official archive中,典型特征是顶层有个 dists 目录和 pool 目录。这样的好处是:

  • 将所有类型CPU的包列表(Packages或者Packages.gz文件)放在一个文件里面,这样每个机器要获取的包列表就比较小。
  • 不同套件/不同CPU可共用的deb包(主要是那些 _all.deb)和源代码包,也只在 pool/all目录下存放一份。
  • 源代码包(.dsc,orig.tar.xz)有路径存放,这样 dget / apt source 可以取到源代码包。

对应的/etc/apt/sources.list配置如下:

1
deb http://192.168.56.47:80/hearing/repository stable main

配置软件源的格式为:

1
deb|deb-src uri distribution [component1] [component2] [...]

生成Packages

Packages文件包括每个deb包的位置,描述,版本等信息,生成命令:

1
2
dpkg-scanpackages all/ /dev/null| gzip -9c > dists/stable/main/binary-all/Packages.gz
dpkg-scanpackages all/ /dev/null| > dists/stable/main/binary-all/Packages

生成Release

Release文件里面包含了 Packages 等文件的大小和校验和(包含MD5/SHA1/SHA256/SHA512 多种值),如果这个文件里面所描述的 Packages 大小与校验和与实际读取到的文件不一致,apt 会拒绝这个仓库。生成命令:

1
apt-ftparchive release $dir > Release

生成Release.gpg 和 InRelease 文件

Release.gpg 是一个签名文件,随同 Release 一起出现的,比较老的客户端只认这两个文件,而 InRelease 是内嵌签名的(也就是说,将原来 Release 的内容和 Release.gpg 的内容揉到一起了,注意这里不是简单地拼到一起),新的客户端才支持这个这个文件,观察一下 Debian 和 Ubuntu 的仓库 ( http://mirrors.ustc.edu.cn/debian/dists/jessie/, http://mirrors.ustc.edu.cn/ubuntu/dists/xenial/ ) ,可以看到 Debian 的仓库只有 Release 和 Release.gpg 这两个文件,而 Ubuntu 仓库里面这三个文件都有。

如何生成这两个文件:

  • 生成自己的gpg key: gpg --list-keys || gpg --gen-key
  • 生成Release.gpg: gpg --armor --detach-sign --sign -o Release.gpg Release
  • 生成InRelease: gpg --clearsign -o InRelease Release

导入公钥

当运行apt update的时候,会出现警告:The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 722D2AFAD8BAD548

也就是说 InRelease / Release.gpg 虽然签名了,但由于这个签名所用的公钥没有被接受,因此还是不能正常使用,有三种解决方法:

  1. 服务端将公钥导出,然后提供给客户端导入:

    1
    2
    3
    4
    5
    # 导出
    gpg --export --armor 722D2AFAD8BAD548 -o my-repo.gpg-key.asc

    # 导入
    sudo apt-key add my-repo.gpg-key.asc
  2. 客户端在执行apt update的时候,添加--allow-insecure-repositories选项;在执行apt install pkg的时候,添加--allow-unauthenticated选项。

  3. 用户修改仓库的配置,改为deb [trusted=yes] http://192.168.56.47:80/hearing/repository stable main

Getting bootstraps(termux-packaging)

  1. 将获得的deb文件上传到一个apt repository
  2. 获取https://github.com/termux/termux-packaging仓库
  3. 在scripts目录下运行:./generate-bootstraps.sh -p /data/data/$package_name/files/usr -r http://localhost/hearing/repository
  4. 上传得到的bootstraps.zip文件(可能需要修改bootstraps.zip中的source.list中的软件源)

generate-bootstraps.sh脚本中可以看到需要的依赖包:

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
# Package manager.
pull_package apt
pull_package game-repo
pull_package science-repo

# Core utilities.
pull_package bash
pull_package bzip2
pull_package command-not-found
pull_package coreutils
pull_package curl
pull_package dash
pull_package diffutils
pull_package findutils
pull_package gawk
pull_package grep
pull_package gzip
pull_package less
pull_package procps
pull_package psmisc
pull_package sed
pull_package tar
pull_package termux-exec
pull_package termux-tools
pull_package xz-utils

# Additional.
pull_package busybox
pull_package ed
pull_package dos2unix
pull_package inetutils
pull_package net-tools
pull_package patch
pull_package unzip
pull_package util-linux

Termux-app

将Termux-app源码中的bootstraps.zip的url替换成我们自己的url。

附录

Java自动将deb文件分目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.File;
import java.io.IOException;

public class Main {
public static void main(String[] args) throws IOException {
File file = new File("dir/debs");
File[] debs = file.listFiles();
for (File f : debs) {
if (f.getName().endsWith("aarch64.deb")) {
Runtime.getRuntime().exec("cp "+f.getPath() + " diraarch64");
} else if (f.getName().endsWith("all.deb")) {
Runtime.getRuntime().exec("cp "+f.getPath() + " dirall");
} else if (f.getName().endsWith("arm.deb")) {
Runtime.getRuntime().exec("cp "+f.getPath() + " dirarm");
} else if (f.getName().endsWith("i686.deb")) {
Runtime.getRuntime().exec("cp "+f.getPath() + " diri686");
} else if (f.getName().endsWith("x86_64.deb")) {
Runtime.getRuntime().exec("cp "+f.getPath() + " dirx86_64");
}
}
}
}