如何编写支持拷贝本缓存与调用检测的COBOL SCons构建器
问题解答
A、需求实现方案
1. copybooks扫描缓存实现
直接复用SCons原生的Scanner机制即可,不需要自行实现缓存逻辑:
- SCons的自定义扫描器默认会将扫描结果和源文件的哈希值绑定存储在
.sconsign文件中,只有当源文件内容发生变更时才会重新执行扫描,和C语言头文件的默认处理逻辑完全一致。 - 只需要把原有的
getCopyBooks逻辑封装为扫描器函数,关联到.cbl后缀的源文件即可。
2. 自动检测CALL语句添加构建目标实现
通过SCons的Emitter机制实现:
- Emitter会在构建器执行前处理目标和依赖关系,你可以在Emitter中扫描当前COBOL源文件的CALL语句,自动为每个被调用的程序生成对应的构建目标,并添加到当前目标的依赖链中。
- 新增全局目标缓存字典,避免重复创建相同的构建目标,同时可兼容循环调用的场景。
以下是完整的实现代码:
import re from SCons.Scanner import Scanner from SCons.Builder import Builder Import('env') # 全局配置,可根据项目实际路径调整 COBOL_TARGET_CACHE = {} COBOL_SRC_SEARCH_PATHS = ['src/cbl', 'src/bat'] COPYBOOK_SEARCH_PATHS = ['src/cpy'] TARGET_OUTPUT_DIR = 'lib' # COPYBOOK扫描函数 def scan_copybooks(node, env, path): copybooks = [] regex = r'^......\s*COPY\s*[\'"]?([A-Z0-9]+)[\'"]?\.?$' with open(node.get_abspath(), 'r', encoding='utf-8') as f: for line in f: m = re.match(regex, line.rstrip(), re.I) if m: cb_name = m.group(1).upper() for search_path in path: cb_node = env.FindFile(f"{cb_name}.cpy", search_path) if cb_node: copybooks.append(cb_node) break return copybooks # 定义COPYBOOK扫描器,自动处理缓存 cobol_copybook_scanner = Scanner( function=scan_copybooks, skeys=['.cbl'], path_function=lambda env, dir, target: env['COPYBOOKPATH'], recursive=True ) # Emitter:处理CALL依赖,自动生成被调用程序的构建目标 def cobol_program_emitter(target, source, env): src_path = source[0].get_abspath() regex = r'^......\s*CALL\s*[\'"]?([A-Z0-9]+)[\'"]?\.?$' call_progs = [] with open(src_path, 'r', encoding='utf-8') as f: for line in f: m = re.match(regex, line.rstrip(), re.I) if m: call_progs.append(m.group(1).upper()) for prog_name in call_progs: lib_target = f"{env['TARGET_OUTPUT_DIR']}/{prog_name}.so" if lib_target not in env['COBOL_TARGET_CACHE']: # 查找被调用程序的源文件 prog_src = None for search_path in env['COBOL_SRC_SEARCH_PATHS']: prog_src = env.FindFile(f"{prog_name}.cbl", search_path) if prog_src: break if prog_src: # 自动创建构建目标 env.CobolProgram(lib_target, prog_src) env['COBOL_TARGET_CACHE'][lib_target] = True # 添加依赖保证被调用程序先构建 env.Depends(target[0], lib_target) return target, source # 定义COBOL构建器 cobol_builder = Builder( action='cobc -o $TARGET $COPYBOOK_FLAGS $SOURCE', emitter=cobol_program_emitter, source_scanner=cobol_copybook_scanner ) # 环境变量配置 env.Append( BUILDERS={'CobolProgram': cobol_builder}, COPYBOOKPATH=COPYBOOK_SEARCH_PATHS, COPYBOOK_FLAGS=[f"-I{p}" for p in COPYBOOK_SEARCH_PATHS], COBOL_SRC_SEARCH_PATHS=COBOL_SRC_SEARCH_PATHS, TARGET_OUTPUT_DIR=TARGET_OUTPUT_DIR, COBOL_TARGET_CACHE=COBOL_TARGET_CACHE ) # 使用示例,无需手动添加COPYBOOK依赖和PROG2的构建规则 env.CobolProgram('lib/PROG1.so', 'src/bat/PROG1.cbl')
B、可参考的示例
- SCons官方用户指南中自定义Builder、自定义Scanner、自定义Emitter的官方示例,逻辑完全通用。
- SCons内置的Fortran语言构建器实现,同样需要处理INCLUDE文件扫描和模块依赖,和COBOL的COPYBOOK、CALL依赖的处理逻辑高度相似。
- 开源的第三方SCons扩展构建器(如Rust、Go语言构建器)的实现,可参考依赖扫描和自动目标生成的通用逻辑。
内容的提问来源于stack exchange,提问作者Be Kind To New Users
相关产品推荐
相关产品推荐

