import argparse
import os
import shutil
import sys
from shutil import copy2
from loguru import logger
class GoBuild:
"""
一个用于构建跨平台执行文件的类。
初始化函数,设置构建的主文件、生成的执行文件名称以及目标平台。
:param f: 需要构建的主文件(例如: main.go)
:param n: 生成的执行文件主名称(例如: install)
"""
def __init__(self, f, n=None):
self.darwin = darwin
self.go = "go"
self.name = n
self.arch_list = []
self.os_list = []
self.amd64 = False
self.mips64 = False
self.arm64 = False
self.arm32 = False
self.linux = False
self.windows = False
self.file = f
self.basename = ""
self.archs = "X86_64"
self.os_type = ""
self.exe = ""
self.tmp = ""
self.logger = logger
def init(self):
"""
初始化函数,根据不同的架构和操作系统生成相应的架构和操作系统列表,
并设置可执行文件的基础名称。
"""
if self.arm64:
self.arch_list.append("arm64")
if self.mips64:
self.arch_list.append("mips64le")
if self.amd64:
self.arch_list.append("amd64")
if self.arm32:
self.arch_list.append("arm")
if self.linux:
self.os_list.append("linux")
if self.windows:
self.os_list.append("windows")
if self.darwin:
self.os_list.append("darwin")
if self.name is None:
self.basename = str(os.path.basename(self.file)).replace(".go", "")
else:
self.basename = self.name
def delete(self):
"""
开始删除生成的临时文件
:return: None
"""
tmp = os.path.join(os.getcwd(), self.tmp)
try:
os.remove(path=self.tmp)
self.logger.debug(f"删除成功: {tmp}")
except Exception as e:
self.logger.error(f"删除出错 - [{tmp} ] : {str(e)}")
def copy(self):
"""
复制执行文件到目标目录,并根据当前环境调整文件路径。
此方法首先构建目标文件路径,然后尝试从临时文件路径复制文件到目标路径,
如果临时文件存在的话。如果复制成功,它将调用delete方法删除临时文件。
如果临时文件不存在,它将记录一个警告消息。
"""
dst = os.path.join("client", self.exe)
dst = os.path.join(os.getcwd(), dst)
dst = str(dst).replace("amd64", "X86_64")
self.logger.debug(f"开始复制: {dst}")
if os.path.isfile(self.tmp):
try:
copy2(src=self.tmp, dst=dst)
self.delete()
except Exception as e:
self.logger.error(f"复制失败: {str(e)}")
else:
self.logger.warning(f"文件不存在: {self.tmp}")
def build(self):
"""
构建指定的Go文件,根据操作系统类型和架构进行编译,并处理构建结果。
1. 根据架构类型转换变量`self.archs`,确保其符合预期的架构命名规范。
2. 记录构建系统的操作系统类型和架构,用于调试和追踪。
3. 根据操作系统和架构生成可执行文件名`self.exe`,并调整Windows系统下的文件扩展名。
4. 构建Go文件,如果构建成功则调用`self.copy()`方法处理构建结果,否则记录错误并退出程序。
"""
if self.archs == "amd64":
self.archs = "X86_64"
self.logger.debug(f"构建系统: {self.os_type}", )
self.logger.debug(f"构建架构: {self.archs}")
self.exe = self.basename + "_" + self.os_type + "-" + self.archs
self.tmp = str(os.path.basename(self.file)).replace(".go", "")
if self.os_type == "windows":
self.exe = self.exe + ".exe"
self.tmp = self.tmp + ".exe"
c = f"{self.go} build {self.file}"
if os.system(c) == 0:
self.logger.info(f"构建成功,正在生成: {self.exe}")
self.copy()
else:
self.logger.error(f"构建失败: {self.exe}")
print(c)
sys.exit(2)
def ost(self, o):
"""
设置操作系统类型
该方法主要用于设置GOOS环境变量,以模拟不同的操作系统环境这对于后续的编译过程特别重要,
因为GOOS环境变量决定了编译输出的目标操作系统。
参数:
o (str): 要模拟的操作系统类型,例如"linux"、"windows"等。
返回:
无
"""
os.environ['GOOS'] = o
self.os_type = o
def arch(self, arch):
"""
设置架构并触发构建过程
该方法接收一个架构名称,根据特定规则转换后设置环境变量GOARCH,
并将该架构名称保存以供后续使用。最后,调用build方法进行构建。
参数:
arch (str): 构架名称,可能需要转换以适配特定的命名约定。
返回:
无
"""
if arch == "X86_64":
arch = "amd64"
os.environ['GOARCH'] = arch
self.archs = arch
self.build()
def start(self, save):
"""
启动初始化和操作系统处理流程
在这个方法中,首先进行初始化操作,然后根据`save`参数检查目录是否存在,
如果不存在则创建目录.随后,遍历操作系统列表,对每个操作系统进行处理.
对于Linux操作系统,进一步遍历其架构列表并进行处理;对于其他操作系统,
则默认使用X86_64架构进行处理.
参数:
save (str): 保存路径字符串,用于检查是否存在以及创建目录
"""
self.init()
if not os.path.isdir(save):
os.mkdir("./client")
for i in self.os_list:
self.ost(i)
if i == "linux":
self.logger.debug(f"架构列表: {self.arch_list}")
for a in self.arch_list:
self.arch(arch=a)
else:
self.arch(arch="X86_64")
if __name__ == "__main__":
cwd = os.getcwd()
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--file", help="源代码文件名", type=str, default="ssl.go")
parser.add_argument("-n", "--name", help="项目名称", type=str, default="ssl")
parser.add_argument("-l", "--linux", help="是否启用Linux平台", action='store_true', default=True)
parser.add_argument("--no-linux", help="禁用Linux平台", action='store_false', dest='linux')
parser.add_argument("-d", "--darwin", help="是否启用Darwin平台", action='store_true', default=True)
parser.add_argument("--no-darwin", help="禁用Darwin平台", action='store_false', dest='darwin')
parser.add_argument("-w", "--windows", help="是否启用Windows平台", action='store_true', default=True)
parser.add_argument("--no-windows", help="禁用Windows平台", action='store_false', dest='windows')
parser.add_argument("-a", "--arm64", help="是否启用arm64平台", action='store_true', default=True)
parser.add_argument("--no-arm64", help="禁用arm64平台", action='store_false', dest='arm64')
parser.add_argument("-r32", "--arm32", help="是否启用arm32平台", action='store_true', default=True)
parser.add_argument("--no-arm32", help="禁用arm32平台", action='store_false', dest='arm32')
parser.add_argument("-m", "--mips64", help="是否启用mips64平台", action='store_true', default=True)
parser.add_argument("--no-mips64", help="禁用mips64平台", action='store_false', dest='mips64')
parser.add_argument("-x", "--x86", help="是否启用amd64/x86平台", action='store_true', default=True)
parser.add_argument("--no-x86", help="禁用amd64/x86平台", action='store_false', dest='x86')
client_ = os.path.join(cwd, 'client')
parser.add_argument("-o", "--output", help=f"保存目录->默认: {client_}", type=str, default=client_)
parser.add_argument("-v", "--version", action='version', version='%(prog)s 1.0')
args = parser.parse_args()
file = args.file
name = args.name
mips64 = args.mips64
linux = args.linux
darwin = args.darwin
arm64 = args.arm64
x86 = args.x86
arm32 = args.arm32
windows = args.windows
print(args)
print(f"文件名: {file}")
print(f"Linux: {args.linux}")
print(f"Darwin: {args.darwin}")
print(f"Windows: {args.windows}")
print(f"ARM64: {args.arm64}")
print(f"ARM32: {args.arm32}")
print(f"MIPS64: {args.mips64}")
print(f"X86: {args.x86}")
up = GoBuild(f=file, n=name)
up.arm32 = arm32
up.windows = windows
up.arm64 = arm64
up.mips64 = mips64
up.x86 = x86
up.linux = linux
up.darwin = darwin
up.start(save=args.output)
效果