当前位置: 首页 > news >正文

AI书签管理工具开发全记录(二十):打包(完结篇)

文章目录

  • AI书签管理工具开发全记录(二十):打包
    • 1.前言 📝
    • 2.修改前端配置 ✏️
    • 3.打包 🔄
      • 3.1 Makefile
      • 3.2 build.py
      • 3.3 完整编译流程
    • 4.编译多平台包 🏗️
        • 4.1 gox简介
        • 4.2 主要特点
        • 4.3 基本用法
        • 4.4 与标准Go工具的区别
        • 4.5 安装
        • 4.6 适用场景
        • 4.7 build.py改造

AI书签管理工具开发全记录(二十):打包

1.前言 📝

在上一篇文章中,我们完成了嵌入资源部分相关代码实现,至此,本项目预期功能已基本全部实现,剩下的部分就是修复使用过程中相关bug以及项目打包。本文将聚焦于打包部分实现。

2.修改前端配置 ✏️

前端部分需要动态获取实际运行中端口,动态配置基础url,否则会造成接口数据异常。

// web/src/utils/request.jsfunction getApiBaseUrl() {// 1. 优先使用显式配置的环境变量if (import.meta.env.VUE_APP_API_URL) {return import.meta.env.VUE_APP_API_URL}// 2. 开发环境特殊处理if (process.env.NODE_ENV === 'development') {return 'http://localhost:8080/api'}// 3. 生产环境使用当前域名const { protocol, hostname, port } = window.locationlet basePort = port ? `:${port}` : ''// 处理非标准端口if ((protocol === 'http:' && port === '80') || (protocol === 'https:' && port === '443')) {basePort = ''}return `${protocol}//${hostname}${basePort}/api`
}// 创建 axios 实例
const service = axios.create({baseURL: getApiBaseUrl(),timeout: 10000
})

3.打包 🔄

由于需要将将前端资源嵌入exe中,所有每次打包前需要先运行npm run build,将打包的dist目录中内容放置到resources\static目录中。

流程可以表示为

开始打包
运行 npm run build
删除旧静态资源
复制新构建文件
编译Go应用
打包完成

打包完的可执行文件一般较大,不利用分发,我们可以利用upx进行压缩,因此完整流程应该为

开始打包
运行 npm run build
删除旧静态资源
复制新构建文件
编译Go应用
使用UPX压缩可执行文件
打包完成

在此,我们提供两个脚本,来加速构建过程(任选其一即可)

  • Makefile 需要安装了make
  • build.py 需要安装了python

3.1 Makefile

# 应用名称
APP_NAME=aibookmark# 检测操作系统
ifeq ($(OS),Windows_NT)# WindowsRM_CMD = if existRM_END = del /F /QMKDIR_CMD = if not existMKDIR_END = mkdirCP_CMD = xcopy /E /I /Y /HPATH_SEP = \\EXE_EXT = .exeNPM_CMD = npm.cmd# 设置代码页为UTF-8CHCP_CMD = chcp 65001 >nul
else# Linux/macOSRM_CMD = rm -rfMKDIR_CMD = mkdir -pCP_CMD = cp -rPATH_SEP = /EXE_EXT =NPM_CMD = npm
endif# 默认目标
.PHONY: all
all: build# 构建前端
.PHONY: build-frontend
build-frontend:
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "构建前端..."cd web && $(NPM_CMD) install && $(NPM_CMD) run build# 复制静态资源
.PHONY: copy-static
copy-static:
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "复制静态资源..."
ifeq ($(OS),Windows_NT)@if not exist resources\static $(MKDIR_END) resources\static@$(CP_CMD) "web\dist\*" "resources\static\"
else@$(MKDIR_CMD) resources/static@$(CP_CMD) web/dist/* resources/static/
endif# 检查UPX是否可用
.PHONY: check-upx
check-upx:
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "检查UPX..."@upx --version >nul 2>&1 || (echo "UPX未安装,跳过压缩步骤" && exit 0)# 使用UPX压缩
.PHONY: compress-upx
compress-upx: check-upx
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "开始UPX压缩..."@upx --best --verbose $(APP_NAME)$(EXE_EXT)@echo "清理UPX临时文件..."
ifeq ($(OS),Windows_NT)@if exist $(APP_NAME).000 $(RM_END) $(APP_NAME).000@if exist $(APP_NAME).upx $(RM_END) $(APP_NAME).upx
else@$(RM_CMD) $(APP_NAME).000 2>/dev/null || true@$(RM_CMD) $(APP_NAME).upx 2>/dev/null || true
endif# 构建Go应用
.PHONY: build-go
build-go:
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "构建Go应用..."go build -o $(APP_NAME)$(EXE_EXT)# 构建所有内容
.PHONY: build
build: build-frontend copy-static build-go compress-upx# 清理构建文件
.PHONY: clean
clean:
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "清理构建文件..."
ifeq ($(OS),Windows_NT)@if exist web\dist $(RM_END) web\dist@if exist resources\static $(RM_END) resources\static\*@if exist $(APP_NAME)$(EXE_EXT) $(RM_END) $(APP_NAME)$(EXE_EXT)@if exist $(APP_NAME).000 $(RM_END) $(APP_NAME).000@if exist $(APP_NAME).upx $(RM_END) $(APP_NAME).upx
else@$(RM_CMD) web/dist@$(RM_CMD) resources/static/*@$(RM_CMD) $(APP_NAME)$(EXE_EXT)@$(RM_CMD) $(APP_NAME).000 2>/dev/null || true@$(RM_CMD) $(APP_NAME).upx 2>/dev/null || true
endif# 帮助信息
.PHONY: help
help:
ifeq ($(OS),Windows_NT)@$(CHCP_CMD)
endif@echo "可用的命令:"@echo "  make build        - 构建整个应用"@echo "  make build-frontend - 仅构建前端"@echo "  make copy-static  - 仅复制静态资源"@echo "  make build-go     - 仅构建Go应用"@echo "  make compress-upx - 仅压缩可执行文件"@echo "  make clean        - 清理所有构建文件"@echo ""@echo "当前操作系统: $(OS)"@echo "可执行文件扩展名: $(EXE_EXT)" 

如果想要构建,运行

make build

3.2 build.py

from pathlib import Path
import subprocess
import logging as log
import os
import shutil
log.basicConfig(level=log.INFO, format="%(asctime)s - %(levelname)s - %(message)s")def run_npm_build():target_dir = Path(__file__).parent.joinpath("web")target_dir_path = target_dir.resolve()log.info("target_dir_path: " + str(target_dir_path))# 在Windows系统上使用npm.cmdnpm_cmd = "npm.cmd" if os.name == 'nt' else "npm"try:result = subprocess.run([npm_cmd, "run", "build"], cwd=target_dir_path, capture_output=True, text=True,shell=True,encoding='utf-8',  # 显式指定UTF-8编码errors='replace'   # 替换无法解码的字符)if result.returncode == 0:log.info("npm run build 命令执行成功")if result.stdout:log.info(result.stdout)else:log.error("npm run build 命令执行失败")if result.stderr:log.error(result.stderr)raise Exception("npm run build 命令执行失败")except Exception as e:log.error(f"执行npm命令时出错: {str(e)}")raisedef remove_old_build():target_dir = Path(__file__).parent.joinpath("resources").joinpath("static")# 检查并创建目标目录try:target_dir.mkdir(parents=True, exist_ok=True)except Exception as e:log.error(f"创建目录失败: {str(e)}")raise# 删除目录内容(包括子目录)for item in target_dir.glob('*'):try:if item.is_file():item.unlink()elif item.is_dir():shutil.rmtree(item)except Exception as e:log.error(f"删除 {item} 失败: {str(e)}")raisedef copy_new_build():source_dir = Path(__file__).parent.joinpath("web").joinpath("dist")target_dir = Path(__file__).parent.joinpath("resources").joinpath("static")# 确保源目录存在if not source_dir.exists():log.error("源目录不存在: " + str(source_dir))raise FileNotFoundError("源目录不存在")# 复制整个目录结构try:shutil.copytree(source_dir,target_dir,dirs_exist_ok=True  # 允许目标目录已存在)log.info(f"成功复制文件从 {source_dir}{target_dir}")except Exception as e:log.error(f"复制文件失败: {str(e)}")raisedef main():run_npm_build()remove_old_build()copy_new_build()if __name__ == "__main__":main()

打包运行

python build.py

3.3 完整编译流程

梳理完整编译流程

开始
运行 npm run build
构建成功?
删除旧静态资源
报错退出
复制新构建到static目录
复制成功?
编译Go应用
编译成功?
检查UPX可用性
UPX可用?
压缩可执行文件
跳过压缩
清理临时文件
结束

4.编译多平台包 🏗️

推荐使用gox

4.1 gox简介

gox 是一个用于 Go 语言交叉编译的工具,它简化了为多个平台和架构构建 Go 程序的过程。

4.2 主要特点
  1. 交叉编译:可以轻松地为不同操作系统和CPU架构构建二进制文件
  2. 并行构建:能够同时构建多个平台的版本
  3. 简化流程:比直接使用 go build 的交叉编译更简单易用
4.3 基本用法
gox -osarch="linux/amd64 windows/amd64 darwin/amd64"
4.4 与标准Go工具的区别
  • 标准 go build 需要设置 GOOSGOARCH 环境变量
  • gox 自动处理这些设置,并可以一次性构建多个平台版本
  • gox 会自动处理一些依赖问题,如CGO_ENABLED等
4.5 安装
go get github.com/mitchellh/gox
4.6 适用场景
  • 需要为多个平台分发Go应用程序时
  • 自动化构建和发布流程中
  • 开发需要支持多平台的工具时

gox 特别适合需要为Linux、Windows和macOS等多个平台提供二进制文件的开发者。

4.7 build.py改造
def get_version():"""获取版本号优先使用 Git 标签作为版本号,如果没有标签则使用 Git 提交哈希"""try:# 尝试获取最新的 Git 标签tag = subprocess.check_output(["git", "describe", "--tags", "--abbrev=0"],stderr=subprocess.DEVNULL,universal_newlines=True).strip()return tagexcept subprocess.CalledProcessError:try:# 如果没有标签,使用 Git 提交哈希commit = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"],stderr=subprocess.DEVNULL,universal_newlines=True).strip()return f"0.0.0-{commit}"except subprocess.CalledProcessError:# 如果 Git 命令失败,返回默认版本号return "0.0.0-dev"def get_build_info():"""获取构建信息"""try:# 获取 Git 提交哈希commit = subprocess.check_output(["git", "rev-parse", "HEAD"],stderr=subprocess.DEVNULL,universal_newlines=True).strip()# 获取 Git 分支名branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"],stderr=subprocess.DEVNULL,universal_newlines=True).strip()return {"commit": commit,"branch": branch,"build_time": time.strftime("%Y-%m-%d %H:%M:%S")}except subprocess.CalledProcessError:return {"commit": "unknown","branch": "unknown","build_time": time.strftime("%Y-%m-%d %H:%M:%S")}def build_with_gox():"""使用gox进行交叉编译"""if not check_gox():log.error("gox未安装,请先安装gox: go install github.com/mitchellh/gox@v1.0.1")raise Exception("gox未安装")try:# 设置输出目录output_dir = "build"# 清理输出目录if os.path.exists(output_dir):shutil.rmtree(output_dir)os.makedirs(output_dir)# 获取版本信息和构建信息version = get_version()build_info = get_build_info()# 设置编译参数ldflags = (f"-X 'main.Version={version}' "f"-X 'main.BuildTime={build_info['build_time']}' "f"-X 'main.GitCommit={build_info['commit']}' "f"-X 'main.GitBranch={build_info['branch']}'")# 执行gox命令,在项目根目录下执行return_code = run_command(["gox","-os", "windows linux darwin","-arch", "amd64","-ldflags", ldflags,"-output", "%s/aibookmark_{{.OS}}_{{.Arch}}" % output_dir])if return_code == 0:log.info(f"gox交叉编译成功,版本: {version}")# 为Windows版本添加.exe扩展名for file in os.listdir(output_dir):if file.startswith("aibookmark_windows") and not file.endswith(".exe"):old_path = os.path.join(output_dir, file)new_path = old_path + ".exe"os.rename(old_path, new_path)log.info(f"重命名Windows可执行文件: {file} -> {file}.exe")# 只对当前平台的可执行文件进行UPX压缩current_os = "windows" if os.name == 'nt' else "linux" if os.name == 'posix' else "darwin"for file in os.listdir(output_dir):if file.startswith(f"aibookmark_{current_os}"):file_path = os.path.join(output_dir, file)if os.path.isfile(file_path):log.info(f"开始压缩当前平台文件: {file}")compress_with_upx(file_path)else:log.info(f"跳过非可执行文件: {file}")else:log.info(f"跳过其他平台文件: {file}")else:log.error("gox交叉编译失败")raise Exception("gox交叉编译失败")except Exception as e:log.error(f"使用gox编译时出错: {str(e)}")raise

往期系列

  • Ai书签管理工具开发全记录(一):项目总览与技术蓝图
  • Ai书签管理工具开发全记录(二):项目基础框架搭建
  • AI书签管理工具开发全记录(三):配置及数据系统设计
  • AI书签管理工具开发全记录(四):日志系统设计与实现
  • AI书签管理工具开发全记录(五):后端服务搭建与API实现
  • AI书签管理工具开发全记录(六):前端管理基础框框搭建 Vue3+Element Plus
  • AI书签管理工具开发全记录(七):页面编写与接口对接
  • AI书签管理工具开发全记录(八):Ai创建书签功能实现
  • AI书签管理工具开发全记录(九):用户端页面集成与展示
  • AI书签管理工具开发全记录(十):命令行中结合ai高效添加书签
  • AI书签管理工具开发全记录(十一):MCP集成
  • AI书签管理工具开发全记录(十二):MCP集成查询
  • AI书签管理工具开发全记录(十三):TUI基本框架搭建
  • AI书签管理工具开发全记录(十四):TUI基本界面完善
  • AI书签管理工具开发全记录(十五):TUI基本逻辑实现与数据展示
  • AI书签管理工具开发全记录(十六):Sun-Panel接口分析
  • AI书签管理工具开发全记录(十七):Sun-Panel书签同步实现
  • AI书签管理工具开发全记录(十八):书签导入导出
  • 动态API地址配置:利用window.location.origin解决前后端分离部署难题

相关文章:

  • Oracle 数据库对象管理:表空间与表的操作
  • STL 5 适配器
  • leetcode_35.搜索插入位置
  • Vue 模板语法之指令语法详解
  • 如何xml序列化 和反序列化类中包含的类
  • 如何删除导出的xml中的xmlns:xsd=
  • 【无标题新手学习期权从买入看涨期权开始】
  • 分析VSS,VCC和VDD
  • 电路笔记(元器件):并串转换芯片 SN65LV1023A 10:1 LVDS 串行器/解串器变送器 100 至 660Mbps
  • Dispatch PDI(DPDI)kettle调度管理平台稳定版本,正式登场!
  • 突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
  • 【解密LSTM、GRU如何解决传统RNN梯度消失问题】
  • 阳台储能新纪元:ADL200N-CT/D16-WF-1导轨表,家庭能源自由的钥匙
  • 2023年全国研究生数学建模竞赛华为杯D题区域双碳目标与路径规划研究求解全过程文档及程序
  • LangChain Chat History概念指南
  • 案例解读:交安与建安安全员 C 证在实践中的差异
  • 小说系统开发:打造属于你的数字阅读王国
  • [小白]java之复杂JSON解析【超详细】
  • Vue3实现键盘字母筛选功能
  • Day.27
  • 葫芦岛建设工程信息网站/网站优化方法
  • 青岛营销型网站建设/网页制作公司
  • 手机web网站开发软件有/在线外链
  • 南宁网站设计图/百度推广方法
  • 网站开发视频教程百度云/免费b2b网站推广渠道
  • 国外的旅游网站做的如何/桂平seo关键词优化