diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e3d4148..85f003f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,40 +1,42 @@ name: 构建二进制可执行文件 on: - workflow_dispatch: + workflow_dispatch: + pull_request: + paths: + - "language.cs" + # 只在源代码被更新时运行 push: paths: - - "language.cpp" - # 只在源代码被更新时运行 - + - "language.cs" jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.runner }} strategy: + fail-fast: false matrix: include: - - target: x86_64-linux-musl + - runner: ubuntu-latest artifact: language - - target: aarch64-linux-musl + - runner: ubuntu-26.04-arm artifact: language_arm steps: - - uses: actions/checkout@v6 - - name: 下载 musl 工具链 - uses: lmq8267/dl-musl@main - with: - target: ${{ matrix.target }} - static: true + - uses: actions/checkout@v7 - name: 编译 run: | - if [ "${{ matrix.target }}" = "x86_64-linux-musl" ]; then - "$CXX" -o "${{ matrix.artifact }}" -std=c++23 -s --static language.cpp - else - "$CXX" -o "${{ matrix.artifact }}" -std=c++23 -s --static language.cpp - fi - - - uses: actions/upload-artifact@v5 + docker run --rm \ + -v "${{ github.workspace }}":/src \ + -w /src \ + alpine:latest /bin/sh -c ' + apk update + apk add build-base dotnet10-sdk clang + dotnet publish language.cs /t:Rebuild --disable-build-servers --force ' + - name: 重命名 + if: ${{ matrix.runner == 'ubuntu-26.04-arm' }} + run: sudo mv artifacts/language/language artifacts/language/language_arm + - uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact }} - path: ${{ matrix.artifact }} \ No newline at end of file + path: artifacts/language/${{ matrix.artifact }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index ca3423e..b8395df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,2 @@ -################################################################################ -# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 -################################################################################ - -.vs -.vscode -a.out -build -language -language_arm \ No newline at end of file +.vscode +artifacts \ No newline at end of file diff --git a/README.md b/README.md index 9c844bb..b875a08 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ## 支持的系统 - Debian +- Armbian - Ubuntu - Arch Linux - CachyOS @@ -36,4 +37,38 @@ wget https://github.com/TheWhiteDog9487/lag2cn/releases/latest/download/language chmod +x language_arm ./language_arm rm language_arm -``` \ No newline at end of file +``` + +## 关于可用性 +对每一个系统版本进行实际测试是不现实的,我会尽量保证可用性,但是更新之后的测试只会在最新版本进行 +并且,由于各种系统版本众多来源复杂,哪怕是最新版本也未必100%可用 +故此,如果您在使用过程中遇到问题,请在GitHub仓库打开一个新的issue,我会尽力处理 + +## 如果你想自行编译 +所需依赖项: +- .NET 10 SDK +- Clang +- build-base或其他等效工具集 + +推荐使用Alpine Linux进行编译,因为Alpine的系统libc就是musl,编译出的产物可以直接用而不需要管目标系统glibc的版本问题 +您可以使用下面的Docker命令获得一个Alpine环境 +```shell +docker run --rm -it -v .:/src -w /src alpine +``` +如果您位于中国大陆境内,推荐先对apk进行换源,以加速软件包下载 +```shell +# https://help.mirrors.cernet.edu.cn/alpine/ +printf '%s' 'https://mirrors.cernet.edu.cn/alpine/latest-stable/main +https://mirrors.cernet.edu.cn/alpine/latest-stable/community +' | tee /etc/apk/repositories +``` +安装依赖项 +```shell +apk update +apk add --no-cache clang build-base dotnet10-sdk +``` +然后进行编译 +```shell +dotnet publish language.cs +``` +你需要的ELF会出现在`artifacts/language/`文件夹内 \ No newline at end of file diff --git a/language.cpp b/language.cpp deleted file mode 100644 index a469dae..0000000 --- a/language.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; - -enum class System{ - Ubuntu, - Debian, - ArchLinux, - Unsupported }; -enum class User{ - Root, - Other }; - -auto CheckIfRunningInsideWSL(){ - if (filesystem::exists("/proc/version")) { - ifstream version_file("/proc/version"); - string version_info = ""; - getline(version_file, version_info); - if (version_info.find("Microsoft") != string::npos or - version_info.find("WSL") != string::npos) { - return true; } - version_file.close(); } - if (filesystem::exists("/proc/sys/kernel/osrelease")) { - ifstream osrelease_file("/proc/sys/kernel/osrelease"); - string osrelease_info = ""; - getline(osrelease_file, osrelease_info); - if (osrelease_info.find("microsoft") != string::npos or - osrelease_info.find("WSL") != string::npos) { - return true; } - osrelease_file.close(); } - return false; } - -auto GetCurrentUser(){ - if (getenv("USER") != nullptr){ - string user = getenv("USER"); - if (user == "root" or geteuid() == 0){ - return User::Root; } - else{ - return User::Other; } } - else{ - throw runtime_error("无法获取用户信息"); } } - -/// @brief 获取当前系统的发行版名称 -/// @return 发行版名称,带有版本号 -/// @author ChatGPT -auto GetCurrentSystem() { - string distro_name = ""; - ifstream release_file("/etc/os-release"); - if (release_file.is_open()) { - string line = ""; - while (getline(release_file, line)) { - if (line.find("PRETTY_NAME=") != string::npos) { - distro_name = line.substr(line.find("=") + 1); - distro_name.erase(0, 1); // remove opening quote - distro_name.erase(distro_name.size() - 1); // remove closing quote - break; } } - release_file.close(); } - - if(distro_name.find("Ubuntu") != string::npos){ - return pair(System::Ubuntu, distro_name); } - else if(distro_name.find("Debian") != string::npos or - distro_name.find("Armbian") != string::npos){ - return pair(System::Debian, distro_name); } - else if(distro_name.find("Arch Linux") != string::npos or - distro_name.find("CachyOS") != string::npos){ - return pair(System::ArchLinux, distro_name); } - else{ - return pair(System::Unsupported, distro_name); } } - -auto check() { - cout << "请输入您的选项(输入y或者yes继续)"; - string userin = ""; - cin >> userin; - if (userin == "y" or userin == "yes") - return; - else{ - cout << "输入不正确欸,如果想停止本程序的话请按下Ctrl+C" << endl; - check(); } } - -auto Ubuntu(){ - cout << "即将开始安装并配置简体中文语言包,请输入y或者yes继续,或者按Ctrl+C终止本程序"; - check(); - cout << "开始更新语言配置" << endl; - system("apt update"); - system("apt install -y language-pack-zh-hans"); - system("update-locale LANG=zh_CN.UTF-8"); - cout << "配置完成,更改将在您下一次登录Shell时应用" << endl; } - -auto Debian(){ - cout << "即将开始更新语言配置,请输入y或者yes继续,或者按Ctrl+C终止本程序"; - check(); - cout << "开始更新语言配置" << endl; - fstream locale_gen("/etc/locale.gen"); - if (locale_gen.is_open() == false){ - throw system_error(errno, system_category(), "未能正确打开/etc/locale.gen"); } - vector FileContent; - string Buffer = ""; - while (getline(locale_gen, Buffer)){ - FileContent.push_back(Buffer); } - for (auto& Line : FileContent){ - if (Line == "zh_CN.UTF-8 UTF-8"){ - goto skip_write; } } - locale_gen.clear(); - locale_gen.seekp(0, ios::end); - locale_gen << '\n' << "zh_CN.UTF-8 UTF-8" << endl; -skip_write: - locale_gen.close(); - system("locale-gen"); - system("update-locale LANG=zh_CN.UTF-8"); - cout << "配置完成,更改将在您下一次登录Shell时应用" << endl; } - -auto ArchLinux(){ - // https://wiki.archlinuxcn.org/wiki/Locale - cout << "即将开始更新语言配置,请输入y或者yes继续,或者按Ctrl+C终止本程序"; - check(); - cout << "开始更新语言配置" << endl; - system("localectl set-locale LANG=zh_CN.UTF-8"); - if (CheckIfRunningInsideWSL() == true){ - // https://wiki.archlinuxcn.org/wiki/%E5%9C%A8_WSL_%E4%B8%8A%E5%AE%89%E8%A3%85_Arch_Linux#%E4%BF%AE%E6%94%B9%E5%8C%BA%E5%9F%9F%E8%AE%BE%E7%BD%AE - cout << "检测到您正在WSL中运行Arch Linux,正在创建 /etc/locale.conf -> /etc/default/locale 符号链接" << endl; - filesystem::create_symlink("/etc/locale.conf", "/etc/default/locale"); } - cout << "配置完成,更改将在您下一次登录Shell时应用" << endl; } - -auto ThrowUnsupportedSystemError(){ - cout << "错误:未知操作系统" << endl - << "当前仅适配了Ubuntu,Debian和Arch Linux" << endl - << "强行使用可能会造成不可预知的问题" << endl - << "为避免出现故障,程序会强制退出" << endl - << "如果是误报,请在GitHub仓库打开一个新的issue。" << endl; - throw runtime_error("不支持的操作系统"); } - -int main() { - auto OsType = GetCurrentSystem(); - if (OsType.first == System::Unsupported){ - ThrowUnsupportedSystemError(); } - - if (GetCurrentUser() == User::Other){ - cout << "更改语言文件需要root权限,请使用sudo重新运行本程序" << endl; - throw runtime_error("权限不足"); } - - cout << "检测到您使用的系统为:" << OsType.second << endl; - if (OsType.first == System::Ubuntu){ Ubuntu(); } - if (OsType.first == System::Debian){ Debian(); } - if (OsType.first == System::ArchLinux){ ArchLinux(); } } \ No newline at end of file diff --git a/language.cs b/language.cs new file mode 100644 index 0000000..0fef0bd --- /dev/null +++ b/language.cs @@ -0,0 +1,146 @@ +#!/usr/bin/env -S dotnet -- +// https://learn.microsoft.com/zh-cn/dotnet/core/sdk/file-based-apps + +#:property PublishAot=true +#:property StaticExecutable=true +#:property OptimizationPreference=Speed +// #:property OptimizationPreference=Size +#:property InvariantGlobalization=true +// ↑ 这个东西需要libicu +// #:property StackTraceSupport=false + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +void CheckUser(string DisplayMessage = "即将开始更新语言配置"){ + Console.WriteLine(DisplayMessage); + Console.WriteLine("按回车继续,按Esc取消"); + while (true){ + var KeyInfo = Console.ReadKey(true); + if (KeyInfo.Key == ConsoleKey.Enter){ + return; } + else if (KeyInfo.Key == ConsoleKey.Escape){ + Environment.Exit(0); } } } + +bool IsRunningInsideWSL(){ + string VersionFile = File.ReadAllText("/proc/version"); + string KernelOsReleaseFile = File.ReadAllText("/proc/sys/kernel/osrelease"); + if (VersionFile.Contains("Microsoft") || VersionFile.Contains("WSL")){ + return true; } + if (KernelOsReleaseFile.Contains("microsoft") || KernelOsReleaseFile.Contains("WSL")){ + return true; } + return false; } + +void ExecuteCommand(string Command, + List? Arguments = null, + Action? WhenErrorThrown = null){ + List StandardError = []; + int ExitCode = 0; + + try{ + using var Process = new Process(); + Process.StartInfo.FileName = Command; + if (Arguments != null){ + foreach (var Argument in Arguments){ + Process.StartInfo.ArgumentList.Add(Argument); } } + Process.StartInfo.RedirectStandardError = true; + Process.ErrorDataReceived += (_, Event) => { + if (Event.Data != null){ + Console.WriteLine(Event.Data); + StandardError.Add(Event.Data + "\n"); } }; + Process.Start(); + Process.BeginErrorReadLine(); + Process.WaitForExit(); + ExitCode = Process.ExitCode; + if (ExitCode != 0){ + throw new Exception($"命令执行失败,退出代码: {ExitCode}"); } } + catch (Exception ExceptionInstance){ + WhenErrorThrown ??= (ExitCode, StandardError, ExceptionMessage) => { + Console.WriteLine(@$" + 命令执行失败 + 退出代码: {ExitCode} + stderr: {StandardError} + 异常信息: {ExceptionMessage ?? ""}"); }; + WhenErrorThrown(ExitCode, string.Join("\n", StandardError), ExceptionInstance.Message); + throw; } } + +string OsDescription = RuntimeInformation.OSDescription; +if (Enum.TryParse(OsDescription.Split(' ')[0], out var CurrentSystem) == false){ + if (OsDescription.Contains("Arch Linux")){ + CurrentSystem = SupportedLinuxDistributions.ArchLinux; } + else{ + string CurrentSupportedLinuxDistributions = string.Join(", ", Enum.GetNames()); + string Message = @$" + 当前系统为 {OsDescription} + 当前仅支持 {CurrentSupportedLinuxDistributions} + 强行使用可能会造成不可预知的问题 + 为避免出现故障,程序会强制退出 + 如果是误报,请在GitHub仓库打开一个新的issue。 + ".Trim(); + throw new NotSupportedException(Message); } } +if (Environment.IsPrivilegedProcess == false) { + using var Process = new Process(); + Process.StartInfo.FileName = "sudo"; + Process.StartInfo.ArgumentList.Add(Environment.ProcessPath ?? throw new Exception("无法获取当前程序路径")); + Console.WriteLine("更改语言文件需要root权限,当前用户权限不足"); + Console.WriteLine("正在尝试使用sudo重新运行程序"); + Console.WriteLine("如果您当前登录的用户拥有密码,请在sudo提示符中输入密码"); + Process.Start(); + Process.WaitForExit(); + Environment.Exit(Process.ExitCode); } + +Console.WriteLine($"探测到当前系统为 {OsDescription}"); +switch (CurrentSystem){ + case SupportedLinuxDistributions.Debian or SupportedLinuxDistributions.Armbian: { + CheckUser(); + Console.WriteLine("正在检查语言配置"); + string[] NewContent = []; + using var FileStream = new FileStream("/etc/locale.gen", FileMode.Open, FileAccess.ReadWrite, FileShare.None); + using (var StreamReader = new StreamReader(FileStream, Encoding.UTF8, true, 1024, true)){ + NewContent = [.. StreamReader.ReadToEnd() + .Split("\n") + .Select(Line => Line.Trim()) + .Select(Line => { + if (Line.Contains("zh_CN.UTF-8 UTF-8")){ + Line = "zh_CN.UTF-8 UTF-8"; } + return Line; } ) ]; } + FileStream.SetLength(0); + FileStream.Seek(0, SeekOrigin.Begin); + using (var StreamWriter = new StreamWriter(FileStream, Encoding.UTF8)){ + foreach (var Line in NewContent){ + StreamWriter.WriteLine(Line); } } + Console.WriteLine("正在生成语言配置"); + ExecuteCommand("locale-gen"); + Console.WriteLine("正在更新语言配置"); + ExecuteCommand("update-locale", ["LANG=zh_CN.UTF-8"]); + break; } + case SupportedLinuxDistributions.Ubuntu: { + CheckUser("即将开始安装中文语言包"); + Console.WriteLine("正在更新apt软件包源"); + ExecuteCommand("apt", ["update"]); + Console.WriteLine("正在安装中文语言包"); + ExecuteCommand("apt", ["install", "language-pack-zh-hans", "-y"]); + Console.WriteLine("正在更新语言配置"); + ExecuteCommand("update-locale", ["LANG=zh_CN.UTF-8"]); + break; } + case SupportedLinuxDistributions.ArchLinux or SupportedLinuxDistributions.CachyOS: { + CheckUser(); + Console.WriteLine("开始更新语言配置"); + ExecuteCommand("localectl", ["set-locale", "LANG=zh_CN.UTF-8"]); + if (IsRunningInsideWSL()){ + // https://wiki.archlinuxcn.org/wiki/%E5%9C%A8_WSL_%E4%B8%8A%E5%AE%89%E8%A3%85_Arch_Linux#%E4%BF%AE%E6%94%B9%E5%8C%BA%E5%9F%9F%E8%AE%BE%E7%BD%AE + Console.WriteLine("检测到您正在WSL中运行Arch Linux,正在创建 /etc/locale.conf -> /etc/default/locale 符号链接"); + if (File.Exists("/etc/default/locale")){ + File.Move("/etc/default/locale", "/etc/default/locale.bak", true); } + File.CreateSymbolicLink("/etc/default/locale", "/etc/locale.conf"); } + break; } } +Console.WriteLine("配置完成,更改将在您下一次登录Shell时生效,按任意键退出"); +Console.ReadKey(true); + +enum SupportedLinuxDistributions { + Debian, + Armbian, + Ubuntu, + ArchLinux, + CachyOS } \ No newline at end of file