如何让setuptools记录文件修改时间以避免重复编译Go共享库?
如何让setuptools的自定义构建命令基于文件修改时间跳过重复编译?
我想给setuptools添加一个自定义构建命令,用来编译Go共享库。这个编译过程耗时不短,但Go源码很少改动。所以我想知道:能不能让setuptools记录上次运行时文件的最后修改时间,以此判断是否需要重新编译?还是说必须用SCons这类更专业的构建工具?我不介意用时间戳或者其他简单策略来判断文件更新情况。
我已经写了部分代码,如下:
class BuildGo(build_py): ''' Build Go bindings before building Python extension ''' def run(self): # What should go into go_sources_modified? if self.go_sources_modified(): self.compile_go() build_py.run(self) def compile_go(self): version = None refusal = 'Will not compile Go bindings' try: version = subprocess.check_output(['go', 'version']) except subprocess.CalledProcessError: print('Didn\'t find Go compiler.') print(refusal) return match = re.search(br'go(\d+)\.(\d+)\.(\d+)', version) if match: version = tuple(map(int, match.group(1, 2, 3))) else: print('Unrecognized version of Go compiler: {}'.format(version)) print(refusal) return req_version = 1, 9, 1 if version < req_version: print('You need Go compiler version higher than: {}'.format( req_version, )) print(refusal) return try: result = subprocess.check_output([ 'go', 'build', '-v', '-buildmode=c-shared', '-o', SHLIB_LOCATION, SHLIB_PACKAGE, ]) print(result) except subprocess.CalledProcessError as e: print(e.stderr) raise
不需要用SCons,自己实现时间戳判断完全可行
你完全不需要依赖SCons这类工具,用简单的文件修改时间对比就能实现需求。下面给你两种实用的方案:
方案1:对比源文件和目标库的修改时间
这是最直接的方法——检查所有Go源文件的最后修改时间,只要有任何一个比生成的共享库新,就触发重新编译。
把go_sources_modified方法实现如下:
import os from pathlib import Path def go_sources_modified(self): # 替换成你实际的Go源码目录和目标共享库路径 go_source_dir = Path("go") target_lib = Path(SHLIB_LOCATION) # 如果目标库还不存在,必须编译 if not target_lib.exists(): return True # 获取目标库的最后修改时间 target_mtime = target_lib.stat().st_mtime # 遍历所有.go源文件,检查是否有更新的文件 for go_file in go_source_dir.rglob("*.go"): # 别忘了把go.mod、go.sum也加入检查,这些文件变化也需要重新编译 if go_file.name in ("go.mod", "go.sum") or go_file.suffix == ".go": if go_file.stat().st_mtime > target_mtime: return True # 所有源文件都比目标库旧,不需要编译 return False
方案2:用状态文件记录编译快照
如果你的场景更复杂(比如依赖其他非Go文件),可以创建一个状态文件,记录上次编译时所有源文件的修改时间哈希,下次编译前对比快照判断是否需要更新。
实现示例:
import os import json from pathlib import Path def go_sources_modified(self): state_file = Path(".go_build_state") go_source_dir = Path("go") target_lib = Path(SHLIB_LOCATION) # 目标库不存在则直接编译 if not target_lib.exists(): return True # 收集当前所有相关文件的修改时间 current_state = {} for file_path in go_source_dir.rglob("*"): if file_path.name in ("go.mod", "go.sum") or file_path.suffix == ".go": current_state[str(file_path)] = file_path.stat().st_mtime # 对比保存的状态 if state_file.exists(): with open(state_file, "r") as f: saved_state = json.load(f) if saved_state == current_state: return False # 状态不匹配,需要编译 return True # 记得在compile_go方法最后添加更新状态的代码 def compile_go(self): # ... 原有编译代码 ... # 编译成功后,更新状态文件 state_file = Path(".go_build_state") go_source_dir = Path("go") current_state = {} for file_path in go_source_dir.rglob("*"): if file_path.name in ("go.mod", "go.sum") or file_path.suffix == ".go": current_state[str(file_path)] = file_path.stat().st_mtime with open(state_file, "w") as f: json.dump(current_state, f)
额外小提示
- 把状态文件
.go_build_state加入到.gitignore里,避免提交到版本控制。 - 如果你的Go源码分散在多个目录,调整
go_source_dir的路径或者添加多个目录遍历即可。
这样你就能在setuptools里实现轻量的增量编译,完全不需要引入复杂的第三方构建工具。
内容的提问来源于stack exchange,提问作者wvxvw
相关产品推荐
相关产品推荐

