From 3730c0660b6877ea002c11526f2a1b69a4086f9d Mon Sep 17 00:00:00 2001 From: Gerhard Tan Date: Sat, 12 Jul 2025 22:08:03 +0800 Subject: [PATCH] Support for ARM64 (#257) * Support for ARM64 * Rename environment variable * Switch to MinGW toolchain * Fix invalid path * Fix error handling * Fix error level * Delete installer/actions/actions.def --- .github/workflows/releaser.yml | 10 ++++++++- .github/workflows/tests.yml | 10 ++++++++- README.md | 6 ++--- README_zh.md | 6 ++--- build.bat | 16 ++++++++----- installer/actions/actions.def | 8 ------- installer/build.bat | 41 +++++++++++++++++++++++----------- installer/msi/frpmgr.wxs | 5 ++++- installer/setup/setup.c | 12 +++++----- resource.go | 34 +++++++++++++++++++++------- 10 files changed, 97 insertions(+), 51 deletions(-) delete mode 100644 installer/actions/actions.def diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml index 3436246..bb8c02c 100644 --- a/.github/workflows/releaser.yml +++ b/.github/workflows/releaser.yml @@ -10,7 +10,7 @@ jobs: runs-on: windows-latest strategy: matrix: - architecture: [x64, x86] + architecture: [x64, x86, arm64] steps: - name: Checkout uses: actions/checkout@v4 @@ -49,6 +49,14 @@ jobs: goto :eof "@ | Out-File -Encoding ascii -FilePath safe_copy.bat + - name: Setup toolchain + shell: cmd + run: | + curl "https://github.com/mstorsjo/llvm-mingw/releases/download/20250709/llvm-mingw-20250709-msvcrt-x86_64.zip" -o llvm-mingw-20250709-msvcrt-x86_64.zip -L + tar -xf llvm-mingw-20250709-msvcrt-x86_64.zip + echo %CD%\llvm-mingw-20250709-msvcrt-x86_64\bin>>%GITHUB_PATH% + vcvarsall x64 && set WindowsSdkVerBinPath >> %GITHUB_ENV% + - name: Build main application shell: cmd run: build.bat -p ${{ matrix.architecture }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index da4cb8c..205f97e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: runs-on: windows-latest strategy: matrix: - architecture: [x64, x86] + architecture: [x64, x86, arm64] steps: - name: Checkout uses: actions/checkout@v4 @@ -27,6 +27,14 @@ jobs: shell: powershell run: echo "$(vswhere.exe -latest -property installationPath)\VC\Auxiliary\Build" >> $env:GITHUB_PATH + - name: Setup toolchain + shell: cmd + run: | + curl "https://github.com/mstorsjo/llvm-mingw/releases/download/20250709/llvm-mingw-20250709-msvcrt-x86_64.zip" -o llvm-mingw-20250709-msvcrt-x86_64.zip -L + tar -xf llvm-mingw-20250709-msvcrt-x86_64.zip + echo %CD%\llvm-mingw-20250709-msvcrt-x86_64\bin>>%GITHUB_PATH% + vcvarsall x64 && set WindowsSdkVerBinPath >> %GITHUB_ENV% + - name: Add commit hash to version number shell: powershell if: github.event_name == 'pull_request' diff --git a/README.md b/README.md index 87108ef..cdafe85 100644 --- a/README.md +++ b/README.md @@ -36,11 +36,11 @@ Visit the **[Wiki](https://github.com/koho/frpmgr/wiki)** for comprehensive guid To build FRP Manager from source, you need to install the following dependencies: - Go -- Visual Studio -- [MinGW](https://www.mingw-w64.org/) +- [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) +- [MinGW](https://github.com/mstorsjo/llvm-mingw) - [WiX Toolset](https://wixtoolset.org/) v3.14 -Once Visual Studio is installed, add the [developer command file directory](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#developer_command_file_locations) (e.g., `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build`) to the `PATH` environment variable. Likewise, do the same for the `bin` directory of MinGW. +Once installed, the `WindowsSdkVerBinPath` environment variable should be set to tell build script where to find the specific version of Windows SDK, e.g., `set WindowsSdkVerBinPath=C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\`. You should also add the `bin` directory of MinGW to the `PATH` environment variable. You can compile the project by opening the terminal: diff --git a/README_zh.md b/README_zh.md index 34faf19..8ccf46e 100644 --- a/README_zh.md +++ b/README_zh.md @@ -36,11 +36,11 @@ FRP 管理器是一个多节点、图形化反向代理工具,专为 Windows 要从源代码构建 FRP 管理器,您需要安装以下依赖项: - Go -- Visual Studio -- [MinGW](https://www.mingw-w64.org/) +- [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) +- [MinGW](https://github.com/mstorsjo/llvm-mingw) - [WiX Toolset](https://wixtoolset.org/) v3.14 -安装 Visual Studio 后,将 [开发者命令文件目录](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#developer_command_file_locations)(例如 `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build`)添加到 `PATH` 环境变量中。同样,将 MinGW 的 `bin` 目录也添加到其中。 +安装完成后,您需要设置 `WindowsSdkVerBinPath` 环境变量,以指示构建脚本在哪里找到特定版本的 Windows SDK,例如 `set WindowsSdkVerBinPath=C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\`。您还需要将 MinGW 的 `bin` 目录添加到 `PATH` 环境变量中。 您可以通过打开终端来编译项目: diff --git a/build.bat b/build.bat index 7429020..717d0c3 100644 --- a/build.bat +++ b/build.bat @@ -6,12 +6,12 @@ set BUILDDIR=%~dp0 cd /d %BUILDDIR% || exit /b 1 if "%~1" == "-p" ( - set TARGET=%~2 + set FRPMGR_TARGET=%~2 ) else ( - set TARGET=%~1 + set FRPMGR_TARGET=%~1 ) -if "%TARGET%" == "" set TARGET=x64 x86 +if "%FRPMGR_TARGET%" == "" set FRPMGR_TARGET=x64 x86 :packages echo [+] Downloading packages @@ -27,8 +27,12 @@ if "%TARGET%" == "" set TARGET=x64 x86 set MOD=github.com/koho/frpmgr set GO111MODULE=on set CGO_ENABLED=0 - for %%a in (%TARGET%) do ( - set GOARCH=!GOARCH_%%a! + for %%a in (%FRPMGR_TARGET%) do ( + if defined GOARCH_%%a ( + set GOARCH=!GOARCH_%%a! + ) else ( + set GOARCH=%%a + ) go build -trimpath -ldflags="-H windowsgui -s -w -X %MOD%/pkg/version.BuildDate=%BUILD_DATE%" -o bin\%%a\frpmgr.exe .\cmd\frpmgr || goto :error ) @@ -36,7 +40,7 @@ if "%~1" == "-p" goto :success :installer echo [+] Building installer - for %%a in (%TARGET%) do ( + for %%a in (%FRPMGR_TARGET%) do ( call installer\build.bat %VERSION% %%a || goto :error ) diff --git a/installer/actions/actions.def b/installer/actions/actions.def deleted file mode 100644 index 26455bf..0000000 --- a/installer/actions/actions.def +++ /dev/null @@ -1,8 +0,0 @@ -LIBRARY actions -EXPORTS - EvaluateFrpServices - KillFrpGUIProcesses - KillFrpProcesses - MoveFrpProfiles - RemoveFrpFiles - SetLangConfig diff --git a/installer/build.bat b/installer/build.bat index 90ec017..1fc059e 100644 --- a/installer/build.bat +++ b/installer/build.bat @@ -5,6 +5,9 @@ set ARCH=%~2 set STEP="%~3" set BUILDDIR=%~dp0 cd /d %BUILDDIR% || exit /b 1 +set TARGET_x64=x86_64 +set TARGET_x86=i686 +set TARGET_arm64=aarch64 if "%VERSION%" == "" ( echo ERROR: no version provided. @@ -16,25 +19,34 @@ if "%ARCH%" == "" ( exit /b 1 ) +if not defined TARGET_%ARCH% ( + echo ERROR: unsupported architecture. + exit /b 1 +) + :build if not exist build md build set PLAT_DIR=build\%ARCH% set SETUP_FILENAME=frpmgr-%VERSION%-setup-%ARCH%.exe if %STEP% == "dist" goto :dist - call vcvarsall.bat %ARCH% + set CC=!TARGET_%ARCH%!-w64-mingw32-gcc + set WINDRES=!TARGET_%ARCH%!-w64-mingw32-windres if not exist %PLAT_DIR% md %PLAT_DIR% set MSI_FILE=%PLAT_DIR%\frpmgr.msi - if %STEP:"actions"=""% == "" call :build_actions - if %STEP:"msi"=""% == "" call :build_msi - if %STEP:"setup"=""% == "" call :build_setup + if %STEP:"actions"=""% == "" call :build_actions || goto :error + if %STEP:"msi"=""% == "" call :build_msi || goto :error + if %STEP:"setup"=""% == "" call :build_setup || goto :error if %STEP% == "" goto :dist :success exit /b 0 :build_actions - rc /DVERSION_ARRAY=%VERSION:.=,% /DVERSION_STR=%VERSION% /Fo %PLAT_DIR%\actions.res actions\version.rc || goto :error - cl /O2 /LD /MD /DNDEBUG /Fe%PLAT_DIR%\actions.dll /Fo%PLAT_DIR%\actions.obj actions\actions.c /link /DEF:actions\actions.def %PLAT_DIR%\actions.res msi.lib shell32.lib advapi32.lib shlwapi.lib ole32.lib || goto :error + %WINDRES% -DVERSION_ARRAY=%VERSION:.=,% -DVERSION_STR=%VERSION% -o %PLAT_DIR%\actions.res.obj -i actions\version.rc -O coff -c 65001 || exit /b 1 + set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -municode -DUNICODE -D_UNICODE -DNDEBUG + set LDFLAGS=-shared -s -Wl,--kill-at -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -Wl,--export-all-symbols + set LDLIBS=-lmsi -lole32 -lshlwapi -lshell32 -ladvapi32 + %CC% %CFLAGS% %LDFLAGS% -o %PLAT_DIR%\actions.dll actions\actions.c %PLAT_DIR%\actions.res.obj %LDLIBS% || exit /b 1 goto :eof :build_msi @@ -45,19 +57,19 @@ if "%ARCH%" == "" ( set WIX_CANDLE_FLAGS=-dVERSION=%VERSION% set WIX_LIGHT_FLAGS=-ext "%WIX%bin\WixUtilExtension.dll" -ext "%WIX%bin\WixUIExtension.dll" -sval set WIX_OBJ=%PLAT_DIR%\frpmgr.wixobj - "%WIX%bin\candle" %WIX_CANDLE_FLAGS% -out %WIX_OBJ% -arch %ARCH% msi\frpmgr.wxs || goto :error - "%WIX%bin\light" %WIX_LIGHT_FLAGS% -cultures:en-US -loc msi\en-US.wxl -out %MSI_FILE% %WIX_OBJ% || goto :error + "%WIX%bin\candle" %WIX_CANDLE_FLAGS% -out %WIX_OBJ% -arch %ARCH% msi\frpmgr.wxs || exit /b 1 + "%WIX%bin\light" %WIX_LIGHT_FLAGS% -cultures:en-US -loc msi\en-US.wxl -out %MSI_FILE% %WIX_OBJ% || exit /b 1 for %%l in (zh-CN zh-TW ja-JP ko-KR es-ES) do ( set WIX_LANG_MSI=%MSI_FILE:~0,-4%_%%l.msi - "%WIX%bin\light" %WIX_LIGHT_FLAGS% -cultures:%%l -loc msi\%%l.wxl -out !WIX_LANG_MSI! %WIX_OBJ% || goto :error + "%WIX%bin\light" %WIX_LIGHT_FLAGS% -cultures:%%l -loc msi\%%l.wxl -out !WIX_LANG_MSI! %WIX_OBJ% || exit /b 1 for /f "tokens=3 delims=><" %%a in ('findstr /r "Id.*=.*Language" msi\%%l.wxl') do set LANG_CODE=%%a - "%WindowsSdkVerBinPath%x86\MsiTran" -g %MSI_FILE% !WIX_LANG_MSI! %PLAT_DIR%\!LANG_CODE! || goto :error - "%WindowsSdkVerBinPath%x86\MsiDb" -d %MSI_FILE% -r %PLAT_DIR%\!LANG_CODE! || goto :error + "%WindowsSdkVerBinPath%x86\MsiTran" -g %MSI_FILE% !WIX_LANG_MSI! %PLAT_DIR%\!LANG_CODE! || exit /b 1 + "%WindowsSdkVerBinPath%x86\MsiDb" -d %MSI_FILE% -r %PLAT_DIR%\!LANG_CODE! || exit /b 1 ) goto :eof :build_setup - rc /DFILENAME=%SETUP_FILENAME% /DVERSION_ARRAY=%VERSION:.=,% /DVERSION_STR=%VERSION% /DMSI_FILE=%MSI_FILE:\=\\% /Fo %PLAT_DIR%\setup.res setup\resource.rc || goto :error + %WINDRES% -DFILENAME=%SETUP_FILENAME% -DVERSION_ARRAY=%VERSION:.=,% -DVERSION_STR=%VERSION% -DMSI_FILE=%MSI_FILE:\=\\% -o %PLAT_DIR%\setup.res.obj -i setup\resource.rc -O coff -c 65001 || exit /b 1 set ARCH_LINE=-1 for /f "tokens=1 delims=:" %%a in ('findstr /n /r ".*=.*\"%ARCH%\"" msi\frpmgr.wxs') do set ARCH_LINE=%%a if %ARCH_LINE% lss 0 ( @@ -71,7 +83,10 @@ if "%ARCH%" == "" ( echo ERROR: UpgradeCode was not found. exit /b 1 ) - cl /O2 /MD /DUPGRADE_CODE=L\"{%UPGRADE_CODE%}\" /DVERSION=L\"%VERSION%\" /DNDEBUG /Fe%PLAT_DIR%\setup.exe /Fo%PLAT_DIR%\setup.obj setup\setup.c /link /subsystem:windows %PLAT_DIR%\setup.res shlwapi.lib msi.lib user32.lib advapi32.lib ole32.lib || goto :error + set CFLAGS=-O3 -Wall -std=gnu11 -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -municode -DUNICODE -D_UNICODE -DNDEBUG -DUPGRADE_CODE=L\"{%UPGRADE_CODE%}\" -DVERSION=L\"%VERSION%\" + set LDFLAGS=-s -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 -Wl,--tsaware -Wl,--dynamicbase -Wl,--nxcompat -mwindows + set LDLIBS=-lmsi -lole32 -lshlwapi -ladvapi32 -luser32 + %CC% %CFLAGS% %LDFLAGS% -o %PLAT_DIR%\setup.exe setup\setup.c %PLAT_DIR%\setup.res.obj %LDLIBS% || exit /b 1 goto :eof :dist diff --git a/installer/msi/frpmgr.wxs b/installer/msi/frpmgr.wxs index a686ee4..0652029 100644 --- a/installer/msi/frpmgr.wxs +++ b/installer/msi/frpmgr.wxs @@ -6,6 +6,9 @@ + + + @@ -13,7 +16,7 @@ - + diff --git a/installer/setup/setup.c b/installer/setup/setup.c index e82d28b..397074c 100644 --- a/installer/setup/setup.c +++ b/installer/setup/setup.c @@ -1,7 +1,4 @@ -#define UNICODE -#define _UNICODE - -#include +#include #include #include #include @@ -120,6 +117,7 @@ out: INT_PTR CALLBACK LanguageDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { + INT_PTR nResult; switch (message) { case WM_INITDIALOG: for (size_t i = 0; i < _countof(languages); i++) @@ -128,7 +126,7 @@ INT_PTR CALLBACK LanguageDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM l return (INT_PTR)TRUE; case WM_COMMAND: - INT_PTR nResult = LOWORD(wParam); + nResult = LOWORD(wParam); if (nResult == IDOK || nResult == IDCANCEL) { if (nResult == IDOK) @@ -158,7 +156,7 @@ static int cleanup(void) return 0; } -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { INT langIndex = -1; BOOL installed = FALSE, showDlg = TRUE; @@ -178,7 +176,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine MsiGetProductInfo(productCode, INSTALLPROPERTY_VERSIONSTRING, product.version, &product.versionLen); if (MsiGetProductInfo(productCode, INSTALLPROPERTY_INSTALLLOCATION, product.path, &product.pathLen) == ERROR_SUCCESS && product.path[0]) langIndex = GetApplicationLanguage(product.path, product.pathLen); - if (MsiGetProductInfo(productCode, INSTALLPROPERTY_INSTALLEDLANGUAGE, product.lang, &product.langLen) == ERROR_SUCCESS && langIndex < 0) + if (MsiGetProductInfo(productCode, L"InstalledLanguage", product.lang, &product.langLen) == ERROR_SUCCESS && langIndex < 0) { for (size_t i = 0; i < _countof(languages); i++) { diff --git a/resource.go b/resource.go index 6dcac68..8420105 100644 --- a/resource.go +++ b/resource.go @@ -15,10 +15,7 @@ import ( "github.com/koho/frpmgr/pkg/version" ) -var ( - versionArray = strings.ReplaceAll(version.Number, ".", ",") - archMap = map[string]string{"amd64": "pe-x86-64", "386": "pe-i386"} -) +var versionArray = strings.ReplaceAll(version.Number, ".", ",") func main() { rcFiles, err := filepath.Glob("cmd/*/*.rc") @@ -26,11 +23,32 @@ func main() { println(err.Error()) os.Exit(1) } - for _, rc := range rcFiles { - for goArch, resArch := range archMap { + arch := os.Getenv("FRPMGR_TARGET") + if arch == "" { + arch = os.Getenv("GOARCH") + } + for _, arch := range strings.Split(arch, " ") { + var args []string + var goArch string + switch strings.TrimSpace(arch) { + case "x64", "amd64": + goArch = "amd64" + args = append(args, "windres", "-F", "pe-x86-64") + case "x86", "386": + goArch = "386" + args = append(args, "windres", "-F", "pe-i386") + case "arm64": + goArch = "arm64" + args = append(args, "aarch64-w64-mingw32-windres") + default: + continue + } + for _, rc := range rcFiles { output := strings.TrimSuffix(rc, filepath.Ext(rc)) + fmt.Sprintf("_windows_%s.syso", goArch) - res, err := exec.Command("windres", "-DVERSION_ARRAY="+versionArray, "-DVERSION_STR="+version.Number, - "-i", rc, "-o", output, "-O", "coff", "-c", "65001", "-F", resArch).CombinedOutput() + res, err := exec.Command(args[0], append([]string{ + "-DVERSION_ARRAY=" + versionArray, "-DVERSION_STR=" + version.Number, + "-i", rc, "-o", output, "-O", "coff", "-c", "65001", + }, args[1:]...)...).CombinedOutput() if err != nil { println(err.Error(), string(res)) os.Exit(1)