author | Alberto Bertogli
<albertito@blitiri.com.ar> 2023-08-23 16:15:53 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2023-08-23 16:15:53 UTC |
parent | b497c222444b1c5470e7ebb85ebd7a4c537eec6d |
pygen | +224 | -214 |
diff --git a/pygen b/pygen index 486d55b..b0fe6be 100755 --- a/pygen +++ b/pygen @@ -7,222 +7,232 @@ from optparse import OptionParser def printd(*args): - for i in args: - print(i, end=' ', file=sys.stderr) - print(file=sys.stderr) + for i in args: + print(i, end=" ", file=sys.stderr) + print(file=sys.stderr) + def is_newer_than(src, dst): - """Determine if "src" is newer than "dst".""" - if not os.path.exists(dst): - # To simplify callers, return as if src is newer when there is - # no dst, so that it will be copied - return True - # We have to use the integer part because copystat() is not accurate - # and loses precision when copying mtimes, causing the mtimes to be - # different and sometimes the new file has an older mtime than the old - # one. Since we do not expect to need sub-second precision when - # regenerating, this should be safe. - return int(os.path.getmtime(src)) > int(os.path.getmtime(dst)) - - -def gen_from(src, dst, environ = None, incpath = None): - "Process src and generate dst." - - # open files if necessary - opened_src = opened_dst = False - if not isinstance(src, file): - src = open(src) - opened_src = True - if not isinstance(dst, file): - dst = open(dst, 'w') - opened_dst = True - - # set up the environment for execution - preout = sys.stdout - if not environ: - sys.stdout = dst - environ = { - 'sys': sys, - 'src_mtime': os.path.getmtime(src.name), - } - - # include path search - if not incpath: - incpath = options.include - base, reduced = os.path.split(src.name) - incpath.append(base) - - # state variables - in_python = 0 - in_python_code = '' - - for line in src: - # state parsing - if in_python and not line.startswith('#endpy'): - in_python_code += line - continue - - if line.startswith('#include '): - path = line.split()[1] - origpath = path - if not os.path.exists(path): - for p in incpath: - newpath = p + '/' + path - if os.path.exists(newpath): - path = newpath - break - else: - printd("Unable to find file to include") - printd("\tsrc: %s" % src.name) - printd("\tfile: %s" % origpath) - printd("\tincpath: %s" % str(incpath)) - os.unlink(dst.name) - return - - gen_from(path, dst, environ) - - elif line.startswith('#py '): - code = line.split(' ', 1)[1] - exec(code, environ) - - elif line.startswith('#py:'): - in_python = 1 - in_python_code = '' - - elif line.startswith('#endpy'): - exec(in_python_code, environ) - in_python = 0 - in_python_code = '' - - elif line.startswith('#print'): - s = line.split(' ', 1)[1] - s = 'print ' + s - exec(s, environ) - - else: - dst.write(line) - - # restore environment - sys.stdout = preout - - if opened_src: src.close() - if opened_dst: dst.close() - - # preserve mtime - shutil.copystat(src.name, dst.name) + """Determine if "src" is newer than "dst".""" + if not os.path.exists(dst): + # To simplify callers, return as if src is newer when there is + # no dst, so that it will be copied + return True + # We have to use the integer part because copystat() is not accurate + # and loses precision when copying mtimes, causing the mtimes to be + # different and sometimes the new file has an older mtime than the old + # one. Since we do not expect to need sub-second precision when + # regenerating, this should be safe. + return int(os.path.getmtime(src)) > int(os.path.getmtime(dst)) + + +def gen_from(src, dst, environ=None, incpath=None): + "Process src and generate dst." + + # open files if necessary + opened_src = opened_dst = False + if not isinstance(src, file): + src = open(src) + opened_src = True + if not isinstance(dst, file): + dst = open(dst, "w") + opened_dst = True + + # set up the environment for execution + preout = sys.stdout + if not environ: + sys.stdout = dst + environ = { + "sys": sys, + "src_mtime": os.path.getmtime(src.name), + } + + # include path search + if not incpath: + incpath = options.include + base, reduced = os.path.split(src.name) + incpath.append(base) + + # state variables + in_python = 0 + in_python_code = "" + + for line in src: + # state parsing + if in_python and not line.startswith("#endpy"): + in_python_code += line + continue + + if line.startswith("#include "): + path = line.split()[1] + origpath = path + if not os.path.exists(path): + for p in incpath: + newpath = p + "/" + path + if os.path.exists(newpath): + path = newpath + break + else: + printd("Unable to find file to include") + printd("\tsrc: %s" % src.name) + printd("\tfile: %s" % origpath) + printd("\tincpath: %s" % str(incpath)) + os.unlink(dst.name) + return + + gen_from(path, dst, environ) + + elif line.startswith("#py "): + code = line.split(" ", 1)[1] + exec(code, environ) + + elif line.startswith("#py:"): + in_python = 1 + in_python_code = "" + + elif line.startswith("#endpy"): + exec(in_python_code, environ) + in_python = 0 + in_python_code = "" + + elif line.startswith("#print"): + s = line.split(" ", 1)[1] + s = "print " + s + exec(s, environ) + + else: + dst.write(line) + + # restore environment + sys.stdout = preout + + if opened_src: + src.close() + if opened_dst: + dst.close() + + # preserve mtime + shutil.copystat(src.name, dst.name) def autogen(src, dst): - "Generate automatically by walking src and storing results in dst." - - srcroot = os.path.abspath(src) - base, reduced = os.path.split(srcroot) - tree = os.walk(srcroot) - - if not os.path.isdir(dst): - print('mkdir', dst) - os.mkdir(dst) - - # directories' stats must be updated after we do all the generation, - # so we append them to the list as we go - to_update_stat = [] - - for path, dirs, files in tree: - for d in dirs: - fulld = path + '/' + d - diff = fulld[len(srcroot):] - - if [ x for x in options.exclude if diff[1:].startswith(x)]: - continue - - p = os.path.normpath(dst + '/' + diff) - if os.path.islink(fulld): - files.append(d) - continue - if not os.path.isdir(p): - print('mkdir', p) - os.mkdir(p) - to_update_stat.append((fulld, p)) - - for f in files: - fullf = path + '/' + f - diff = fullf[len(srcroot):] - - if [ x for x in options.exclude if diff[1:].startswith(x)]: - continue - - if diff.endswith('.pgh'): - # header files are ignored - print('ignored', diff) - continue - elif diff.endswith('.pg'): - t = os.path.splitext(diff)[0] - t = os.path.normpath(dst + '/' + t) - - if not is_newer_than(fullf, t): - print('skipped', diff) - continue - print(diff, '->', t) - gen_from(fullf, t) - else: - t = os.path.normpath(dst + '/' + diff) - if not is_newer_than(fullf, t): - print('skipped', diff) - continue - if os.path.islink(fullf): - dlink = os.readlink(fullf) - print('symlink', diff, '->', dlink) - if os.path.exists(t): - os.unlink(t) - os.symlink(dlink, t) - elif os.path.isfile(fullf): - print('copy', diff) - shutil.copy2(fullf, t) - - # second pass to preserve directories' modification time - for s, d in to_update_stat: - print('copystat', d) - shutil.copystat(s, d) - - -if __name__ == '__main__': - - usage = "usage: %prog [options] {gen|autogen} [action_parameters]" - - parser = OptionParser(usage) - parser.add_option("-I", "--include", action = "append", default = [], - help = "Include directory for .pgh files") - parser.add_option("-x", "--exclude", action = "append", default = [], - help = "Paths to exclude from the generation") - #parser.add_option("-v", "--verbose", action = "store_true", - # help = "Show additional information") - - options, args = parser.parse_args() - - if len(args) < 1: - parser.error("Incorrect number of arguments") - sys.exit(1) - - action = args[0] - - if action == 'gen': - if len(args) < 2: - parser.error("Incorrect number of arguments") - sys.exit(1) - for src in args[1:]: - dst = os.path.splitext(src)[0] - print(src, '->', dst) - gen_from(src, dst) - - elif action == 'autogen': - if len(args) < 3: - parser.error("Incorrect number of arguments") - sys.exit(1) - srcroot, dstroot = args[1:3] - autogen(srcroot, dstroot) - - else: - parser.error("Unknown action") - sys.exit(1) - - + "Generate automatically by walking src and storing results in dst." + + srcroot = os.path.abspath(src) + base, reduced = os.path.split(srcroot) + tree = os.walk(srcroot) + + if not os.path.isdir(dst): + print("mkdir", dst) + os.mkdir(dst) + + # directories' stats must be updated after we do all the generation, + # so we append them to the list as we go + to_update_stat = [] + + for path, dirs, files in tree: + for d in dirs: + fulld = path + "/" + d + diff = fulld[len(srcroot) :] + + if [x for x in options.exclude if diff[1:].startswith(x)]: + continue + + p = os.path.normpath(dst + "/" + diff) + if os.path.islink(fulld): + files.append(d) + continue + if not os.path.isdir(p): + print("mkdir", p) + os.mkdir(p) + to_update_stat.append((fulld, p)) + + for f in files: + fullf = path + "/" + f + diff = fullf[len(srcroot) :] + + if [x for x in options.exclude if diff[1:].startswith(x)]: + continue + + if diff.endswith(".pgh"): + # header files are ignored + print("ignored", diff) + continue + elif diff.endswith(".pg"): + t = os.path.splitext(diff)[0] + t = os.path.normpath(dst + "/" + t) + + if not is_newer_than(fullf, t): + print("skipped", diff) + continue + print(diff, "->", t) + gen_from(fullf, t) + else: + t = os.path.normpath(dst + "/" + diff) + if not is_newer_than(fullf, t): + print("skipped", diff) + continue + if os.path.islink(fullf): + dlink = os.readlink(fullf) + print("symlink", diff, "->", dlink) + if os.path.exists(t): + os.unlink(t) + os.symlink(dlink, t) + elif os.path.isfile(fullf): + print("copy", diff) + shutil.copy2(fullf, t) + + # second pass to preserve directories' modification time + for s, d in to_update_stat: + print("copystat", d) + shutil.copystat(s, d) + + +if __name__ == "__main__": + usage = "usage: %prog [options] {gen|autogen} [action_parameters]" + + parser = OptionParser(usage) + parser.add_option( + "-I", + "--include", + action="append", + default=[], + help="Include directory for .pgh files", + ) + parser.add_option( + "-x", + "--exclude", + action="append", + default=[], + help="Paths to exclude from the generation", + ) + # parser.add_option("-v", "--verbose", action = "store_true", + # help = "Show additional information") + + options, args = parser.parse_args() + + if len(args) < 1: + parser.error("Incorrect number of arguments") + sys.exit(1) + + action = args[0] + + if action == "gen": + if len(args) < 2: + parser.error("Incorrect number of arguments") + sys.exit(1) + for src in args[1:]: + dst = os.path.splitext(src)[0] + print(src, "->", dst) + gen_from(src, dst) + + elif action == "autogen": + if len(args) < 3: + parser.error("Incorrect number of arguments") + sys.exit(1) + srcroot, dstroot = args[1:3] + autogen(srcroot, dstroot) + + else: + parser.error("Unknown action") + sys.exit(1)