mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-22 14:22:23 +00:00
195 lines
5.3 KiB
Python
195 lines
5.3 KiB
Python
|
#! /usr/bin/env python3
|
||
|
"""Minimal macro processor. Used for generating VC++ makefiles.
|
||
|
|
||
|
The available template commands are:
|
||
|
|
||
|
Expand a template section for each file in a list of file patterns::
|
||
|
###MAKTEMPLATE:FOREACH my/path*/*.cxx,other*.cxx
|
||
|
...
|
||
|
###MAKTEMPLATE:ENDFOREACH
|
||
|
|
||
|
In the template section, you can use `###BASENAME###` to get the base name
|
||
|
of the file being processed (e.g. "base" for "../base.cxx"), and you can
|
||
|
use `###FILENAME###` to get the full filename.
|
||
|
|
||
|
|
||
|
Copyright (c) 2000-2022, Bart Samwel and Jeroen T. Vermeulen.
|
||
|
"""
|
||
|
|
||
|
from __future__ import (
|
||
|
absolute_import,
|
||
|
print_function,
|
||
|
unicode_literals,
|
||
|
)
|
||
|
|
||
|
from argparse import (
|
||
|
ArgumentError,
|
||
|
ArgumentParser,
|
||
|
RawDescriptionHelpFormatter,
|
||
|
)
|
||
|
from contextlib import contextmanager
|
||
|
from glob import glob
|
||
|
import os
|
||
|
from sys import (
|
||
|
argv,
|
||
|
stdin,
|
||
|
stderr,
|
||
|
stdout,
|
||
|
)
|
||
|
import sys
|
||
|
from textwrap import dedent
|
||
|
|
||
|
|
||
|
def expand_foreach_file(path, block, outfile):
|
||
|
"""Expand a "foreach" block for a single file path.
|
||
|
|
||
|
Write the results to outfile.
|
||
|
"""
|
||
|
basepath, _ = os.path.splitext(os.path.basename(path))
|
||
|
for line in block:
|
||
|
line = line.replace("###FILENAME###", path)
|
||
|
line = line.replace("###BASENAME###", basepath)
|
||
|
outfile.write(line)
|
||
|
|
||
|
|
||
|
def match_globs(globs):
|
||
|
"""List all files matching any item in globs.
|
||
|
|
||
|
Eliminates duplicates.
|
||
|
"""
|
||
|
return sorted({
|
||
|
path
|
||
|
for pattern in globs
|
||
|
for path in glob(pattern)
|
||
|
})
|
||
|
|
||
|
|
||
|
def expand_foreach(globs, block, outfile):
|
||
|
"""Expand a foreach block for each file matching one of globs.
|
||
|
|
||
|
Write the results to outfile.
|
||
|
"""
|
||
|
# We'll be iterating over block a variable number of times. Turn it
|
||
|
# from a generic iterable into an immutable array.
|
||
|
block = tuple(block)
|
||
|
for path in match_globs(globs):
|
||
|
expand_foreach_file(path, block, outfile)
|
||
|
|
||
|
|
||
|
# Header to be prefixed to the generated file.
|
||
|
OUTPUT_HEADER = dedent("""\
|
||
|
# AUTOMATICALLY GENERATED FILE -- DO NOT EDIT.
|
||
|
#
|
||
|
# This file is generated automatically by libpqxx's {script} script, and
|
||
|
# will be rewritten from time to time.
|
||
|
#
|
||
|
# If you modify this file, chances are your modifications will be lost.
|
||
|
#
|
||
|
# The {script} script should be available in the tools directory of the
|
||
|
# libpqxx source archive.
|
||
|
""")
|
||
|
|
||
|
|
||
|
foreach_marker = r"###MAKTEMPLATE:FOREACH "
|
||
|
end_foreach_marker = r"###MAKTEMPLATE:ENDFOREACH"
|
||
|
|
||
|
|
||
|
def parse_foreach(line):
|
||
|
"""Parse FOREACH directive, if line contains one.
|
||
|
|
||
|
:param line: One line of template input.
|
||
|
:return: A list of FOREACH globs, or None if this was not a FOREACH line.
|
||
|
"""
|
||
|
line = line.strip()
|
||
|
if line.startswith(foreach_marker):
|
||
|
return line[len(foreach_marker):].split(',')
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
|
||
|
def read_foreach_block(infile):
|
||
|
"""Read a FOREACH block from infile (not including the FOREACH directive).
|
||
|
|
||
|
Assumes that the FOREACH directive was in the preceding line. Consumes
|
||
|
the line with the ENDFOREACH directive, but does not yield it.
|
||
|
|
||
|
:return: Iterable of lines.
|
||
|
"""
|
||
|
for line in infile:
|
||
|
if line.strip().startswith(end_foreach_marker):
|
||
|
return
|
||
|
yield line
|
||
|
|
||
|
|
||
|
def expand_template(infile, outfile):
|
||
|
"""Expand the template in infile, and write the results to outfile."""
|
||
|
for line in infile:
|
||
|
globs = parse_foreach(line)
|
||
|
if globs is None:
|
||
|
# Not a FOREACH line. Copy to output.
|
||
|
outfile.write(line)
|
||
|
else:
|
||
|
block = read_foreach_block(infile)
|
||
|
expand_foreach(globs, block, outfile)
|
||
|
|
||
|
|
||
|
@contextmanager
|
||
|
def open_stream(path=None, default=None, mode='r'):
|
||
|
"""Open file at given path, or yield default. Close as appropriate.
|
||
|
|
||
|
The default should be a stream, not a path; closing the context will not
|
||
|
close it.
|
||
|
"""
|
||
|
if path is None:
|
||
|
yield default
|
||
|
else:
|
||
|
with open(path, mode) as stream:
|
||
|
yield stream
|
||
|
|
||
|
|
||
|
def parse_args():
|
||
|
"""Parse command-line arguments.
|
||
|
|
||
|
:return: Tuple of: input path (or None for stdin), output path (or None
|
||
|
for stdout).
|
||
|
"""
|
||
|
parser = ArgumentParser(
|
||
|
description=__doc__, formatter_class=RawDescriptionHelpFormatter)
|
||
|
|
||
|
parser.add_argument(
|
||
|
'template', nargs='?',
|
||
|
help="Input template. Defaults to standard input.")
|
||
|
parser.add_argument(
|
||
|
'output', nargs='?',
|
||
|
help="Output file. Defaults to standard output.")
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
return args.template, args.output
|
||
|
|
||
|
|
||
|
def write_header(stream, template_path=None):
|
||
|
"""Write header to stream."""
|
||
|
hr = ('# ' + '#' * 78) + "\n"
|
||
|
script = os.path.basename(argv[0])
|
||
|
|
||
|
outstream.write(hr)
|
||
|
outstream.write(OUTPUT_HEADER.format(script=script))
|
||
|
if template_path is not None:
|
||
|
outstream.write("#\n")
|
||
|
outstream.write("# Generated from template '%s'.\n" % template_path)
|
||
|
outstream.write(hr)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
try:
|
||
|
template_path, output_path = parse_args()
|
||
|
except ArgumentError as error:
|
||
|
stderr.write('%s\n' % error)
|
||
|
sys.exit(2)
|
||
|
|
||
|
input_stream = open_stream(template_path, stdin, 'r')
|
||
|
output_stream = open_stream(output_path, stdout, 'w')
|
||
|
with input_stream as instream, output_stream as outstream:
|
||
|
write_header(outstream, template_path)
|
||
|
expand_template(instream, outstream)
|