鉴于目前OpenWrt官方已经更新到25.12版本了。而lede为了兼容性还是基于较旧的版本,并使用iptables等旧的核心模块。
做为x86_64软路由的用户,决定迁移到更活跃的ImmortalWrt分支上。紧跟官方步调。
使用的是ImmortalWrt的官方源:

https://github.com/immortalwrt/immortalwrt

大体步骤与文档中描述的基本一致,但是因为是在arm64的主机上。因此有几个点与x86机器编译不同,这里记录一下。

检出代码官方推荐基于release tag,这样的代码虽然不是最新,但是更为稳定。
比如基于最新的v25.12.1tag:

git clone -b v25.12.1 --single-branch --filter=blob:none https://github.com/immortalwrt/immortalwrt

编译的系统是Ubuntu 24.04 aarch64。
首先安装编译环境的包就与x86不太一样。具体是:

sudo apt update -y
sudo apt full-upgrade -y
sudo apt install -y ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \
  bzip2 ccache clang cmake cpio curl device-tree-compiler ecj fastjar flex gawk gettext gcc-x86-64-linux-gnu \
  g++-x86-64-linux-gnu git golang gnutls-dev gperf haveged help2man intltool libelf-dev \
  libglib2.0-dev libgmp3-dev libltdl-dev libmpc-dev libmpfr-dev libncurses-dev libpython3-dev \
  libreadline-dev libssl-dev libtool libyaml-dev libz-dev lld llvm lrzsz mkisofs msmtp nano \
  ninja-build p7zip p7zip-full patch pkgconf python3 python3-pip python3-ply python3-docutils \
  python3-pyelftools qemu-utils re2c rsync scons squashfs-tools subversion swig texinfo uglifyjs \
  upx-ucl unzip vim wget xmlto xxd zlib1g-dev zstd

因为本人需要集成加强版geodata文件,要添加一个本地feeds:

src-link custom /home/ubuntu/openwrt/feeds

自用的feeds配置总结为:

src-link custom /home/ubuntu/openwrt/feeds
src-git lucky https://github.com/gdy666/luci-app-lucky.git
src-git passwall_packages https://github.com/Openwrt-Passwall/openwrt-passwall-packages.git;main
src-git passwall_luci https://github.com/Openwrt-Passwall/openwrt-passwall.git;main
src-git passwall2_luci https://github.com/Openwrt-Passwall/openwrt-passwall2.git;main

更新 feeds 什么的跟官方文档一样。

./scripts/feeds update -a
./scripts/feeds install -a

接下来,重点来了。必须多做一件与x86系统不同的配置,否则会报这个错误。

Makefile:366: * go-bootstrap cannot be installed on linux/arm64. Stop.

看报错信息,是编译过程无法安装go到arm64系统上。所以需要手动安装golang,并把路径设置到编译配置中。
在配置编译参数时:

make menuconfig

要加入以下设置:

Languages > Go > Configuration > External bootstrap Go root directory

按上面的环境安装后,go应该默认在 /usr/lib/go 目录下。将 /usr/lib/go 写入这项配置。

(/usr/lib/go) External bootstrap Go root directory

选好其它需要的配置后,开始编译:

make download -j8
make V=s -j1

慢慢等待编译完成吧。编译完成后输出路径:bin/targets

需要更新代码再次编译的话:

cd lede
git pull
./scripts/feeds update -a
./scripts/feeds install -a
make defconfig
make download -j8
make V=s -j$(nproc)

如果需要重新配置:

rm -rf ./tmp && rm -rf .config
make menuconfig
make V=s -j$(nproc)

地址:
https://winger.alundra.uk/dq7tools/

《勇者斗恶龙7》重制版中有一个全道具收集的成就。
想要达成就必须玩其中一个叫“好运对对碰”的小游戏。因为其中有很多限定道具。
但是这个小游戏非常坐牢。高难度下,即使录屏了也要反复倒带确认卡牌的位置。
所以搞了这个辅助工具,至少不用反复倒带……

使用chromium的工具链中的gn来生成的静态库,默认为thin archive。不适合用来发布为第三方可用的静态库。

在static_library()这个配置节点下,添加以下两个配置:

configs -= [ "//build/config/compiler:thin_archive" ]
complete_static_lib = true

这样生成的静态库,就是一个完整的full archive。可以发布第三方编译链接。

必须与上一个脚本配套使用!

import os
import pickle
import sys

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {os.path.basename(__file__)} <human_patch.dat>")
        exit(0)

    data_list = []
    with open(sys.argv[1], "rb") as data_file:
        data_list = pickle.load(data_file)

    print("Start review auto patch failed files!")
    for work_file, rej_file in data_list:
        print(work_file)
        os.system("code %s" % work_file)
        os.system("view %s" % rej_file)

与chromium相关:

# coding: utf-8
import filecmp
import os
import pickle
import shutil
import subprocess
import sys

__src_dir_name = "src" + os.sep
__patch_dir_name = "patch" + os.sep
__reject_dir_name = "rejects" + os.sep

__copy_log_name = "copy_file.log"
__miss_log_name = "miss_file.log"
__patch_success_log_name = "patch_success.log"
__patch_failed_log_name = "patch_failed.log"
__human_patch_data_name = "human_patch.dat"

__force_copy_type = (".png", ".dll", ".exe", ".ico", ".icns")

__chromium_version_file = "chrome/VERSION"
__self_servion_file = "third_party/superBrowser/version"

__human_patch_list = []


def __reset_self_servion(src_dir):
    chromium_version_path = os.path.join(src_dir, __chromium_version_file)
    self_servion_path = os.path.join(src_dir, __self_servion_file)
    patch = 0
    with open(chromium_version_path) as chromium_file:
        for it in chromium_file.readlines():
            idx = it.find('=')
            if -1 != idx and "PATCH" == it[:idx]:
                patch = int(it[idx + 1:])
                break
    with open(self_servion_path, "r+") as self_file:
        ver = float(self_file.read())
        self_file.seek(0)
        if patch > 50:
            if ver >= 1.0:
                minor = int((ver - 1.0) * 10) + 1
                if minor > 9:
                    self_file.write("{:.2f}".format(minor / 100 + 1.0))
                else:
                    self_file.write("{:.1f}".format(minor / 10 + 1.0))
            else:
                self_file.write("1.0")
        else:
            self_file.write("0.1")


def __check_base_version():
    base_git_version_file = os.path.join(base_git_path,
                                         __chromium_version_file)
    base_version_file = os.path.join(base_version_path,
                                     __chromium_version_file)
    if not filecmp.cmp(base_version_file, base_git_version_file):
        raise Exception(
            "Git file version does not match the base file version.")


def __save_human_patch():
    with open(os.path.join(output_path, __human_patch_data_name),
              "wb") as data_file:
        pickle.dump(__human_patch_list, data_file)


def __make_dirs(file_path):
    dest_dir = os.path.dirname(file_path)
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)


def save_miss_file(file_list, log_path):
    with open(log_path, "w") as log_file:
        for file in file_list:
            print(file)
            print(file, file=log_file)


def safe_copy(src, dest):
    __make_dirs(dest)
    shutil.copyfile(src, dest)


def generate_patch(old_file_path, new_file_path, work_file_path):
    patch_file_path = output_path + __patch_dir_name + work_file_path + ".patch"
    cmd_line = f"diff -c {old_file_path} {new_file_path} > {patch_file_path}"

    __make_dirs(patch_file_path)
    return 256 == os.system(cmd_line)


def apply_patch(base_file_path, work_file_path):
    output_file_path = output_path + __src_dir_name + work_file_path
    patch_file_path = output_path + __patch_dir_name + work_file_path + ".patch"
    reject_file_path = output_path + __reject_dir_name + work_file_path + ".rej"
    cmd_line = [
        "patch", "-c", base_file_path, patch_file_path, "-o", output_file_path,
        "-r", reject_file_path
    ]
    __make_dirs(output_file_path)
    __make_dirs(reject_file_path)
    rv = subprocess.run(cmd_line, capture_output=True, text=True)
    if 0 != rv.returncode:
        __human_patch_list.append((output_file_path, reject_file_path))
    return rv


def delete_empty_directories(path):
    for dirpath, dirnames, _ in os.walk(path, topdown=False):
        for dirname in dirnames:
            dir_to_check = os.path.join(dirpath, dirname)
            if not os.listdir(dir_to_check):
                # print(f"Removing empty directory: {dir_to_check}")
                os.rmdir(dir_to_check)


if __name__ == "__main__":
    if "nt" == os.name:
        print("This scripts can`t run in windows platform!")
        exit(1)
    try:
        if len(sys.argv) < 5:
            print(
                "Usage: upgrade_version_file.py <base_git_path> <base_version_path> <new_version_path> <output_path>."
            )
            exit(0)

        base_git_path = os.path.abspath(sys.argv[1])
        base_version_path = os.path.abspath(sys.argv[2])
        new_version_path = os.path.abspath(sys.argv[3])
        output_path = os.path.abspath(sys.argv[4]) + os.sep

        __check_base_version()

        copy_list = []
        patch_list = []
        miss_list = []

        print("Strat check file ...", end="", flush=True)
        for root, _, files in os.walk(base_git_path):
            for file in files:
                git_file = os.path.join(root, file)
                work_file = os.path.relpath(git_file, base_git_path)
                base_version_file = os.path.join(base_version_path, work_file)
                new_version_file = os.path.join(new_version_path, work_file)

                # find copy file
                if not os.path.exists(base_version_file):
                    copy_list.append((git_file, work_file))
                elif not os.path.exists(
                        new_version_file):  # new version miss file.
                    miss_list.append(work_file)
                elif filecmp.cmp(
                        base_version_file,
                        new_version_file) or os.path.splitext(
                            base_version_file)[1] in __force_copy_type:
                    if not filecmp.cmp(git_file, base_version_file):
                        copy_list.append((git_file, work_file))
                # make patch file list.
                else:
                    patch_list.append((base_version_file, git_file,
                                       new_version_file, work_file))
        # add version file
        copy_list.append(
            (os.path.join(new_version_path,
                          __chromium_version_file), __chromium_version_file))
        print(" do!")

        # create output dir
        if not os.path.exists(output_path):
            os.makedirs(output_path)
        # generate patch file list
        print("----------------------patch file:----------------------")
        with open(output_path + __patch_success_log_name,
                  "w") as success_log, open(
                      output_path + __patch_failed_log_name,
                      "w") as failed_log:
            for base_version_file, git_file, new_version_file, work_file in patch_list:
                print(f"{work_file} ... ", end="")
                if generate_patch(base_version_file, git_file, work_file):
                    result = apply_patch(new_version_file, work_file)
                    if 0 == result.returncode:
                        print("OK!")
                        print(work_file, file=success_log)
                    else:
                        print("Failed!")
                        print(work_file, file=failed_log)
                        print(result.stdout, file=failed_log)
                        print(
                            "------------------------------------------------------------\n",
                            file=failed_log)
                else:
                    print("Skip!")
                    print(f"{work_file} ... Skip!", file=failed_log)
                    print(
                        "\n------------------------------------------------------------\n",
                        file=failed_log)
        delete_empty_directories(output_path + __reject_dir_name)
        __save_human_patch()
        # copy file
        print("----------------------copy file:----------------------")
        src_dir = os.path.join(output_path, __src_dir_name)
        with open(output_path + __copy_log_name, "w") as copy_log:
            for src, work_file in copy_list:
                print(work_file)
                safe_copy(src, src_dir + work_file)
                print(work_file, file=copy_log)
        __reset_self_servion(src_dir)
        icon_dir = os.path.join(new_version_path, "chrome", "app", "theme",
                                "chromium")
        dest_dir = os.path.join(src_dir, "third_party", "superBrowser",
                                "resource", "product", "other")
        shutil.copy(os.path.join(icon_dir, "win", "chromium.ico"), dest_dir)
        shutil.copy(os.path.join(icon_dir, "mac", "app.icns"), dest_dir)
        shutil.copy(os.path.join(icon_dir, "linux", "product_logo_48.png"),
                    dest_dir)
        # save miss file list
        print("----------------------miss file:----------------------")
        save_miss_file(miss_list, output_path + __miss_log_name)

    except Exception as error:
        print(error)
        exit(1)