-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
87 changed files
with
14,029 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,3 +68,6 @@ lockfiles/ | |
|
||
# ruff cache | ||
.ruff_cache/ | ||
|
||
# generated files in tests | ||
tests/data/*.template |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,86 @@ | ||
from argparse import ArgumentParser | ||
from pathlib import Path | ||
from typing import Optional | ||
|
||
import typer | ||
|
||
from . import __version__ | ||
from .convert import convert | ||
|
||
__all__ = ["main"] | ||
|
||
|
||
def main(args=None): | ||
parser = ArgumentParser() | ||
parser.add_argument("-v", "--version", action="version", version=__version__) | ||
args = parser.parse_args(args) | ||
cli = typer.Typer(rich_markup_mode="markdown") | ||
|
||
|
||
def version_callback(value: bool): | ||
if value: | ||
typer.echo(__version__) | ||
raise typer.Exit() | ||
|
||
|
||
@cli.command() | ||
def main( | ||
version: Optional[bool] = typer.Option( | ||
None, | ||
"--version", | ||
callback=version_callback, | ||
is_eager=True, | ||
help="Print the version and exit", | ||
), | ||
folder: Path = typer.Argument( | ||
..., | ||
help="folder of vdb files to convert to template files.", | ||
exists=True, | ||
file_okay=False, | ||
resolve_path=True, | ||
), | ||
use_builder: bool = typer.Option( | ||
True, | ||
help="Use the builder.py file to look for direct references to template files.", | ||
), | ||
builder: Optional[Path] = typer.Option( | ||
None, | ||
help="Path to the builder file.", | ||
exists=True, | ||
dir_okay=False, | ||
resolve_path=True, | ||
), | ||
): | ||
""" | ||
### VDCT to template conversion function. | ||
- This function assumes that all referenced VDCT files in the expand() blocks | ||
will be in the same folder. | ||
- We can use the builder.py file to check for direct references to template files | ||
Use --no-use-builder to disable this feature. Direct references to a template | ||
file is an error because we need to modify all macro names to add a _ prefix | ||
in templated files. | ||
- Files referenced in expand() blocks will have their macro names updated to | ||
all have a _ prefix, because MSI does not support substituting a macro with | ||
it's own name and passing a default. This is a workaround to that limitation. | ||
- The original expands() block is replaced with a series of substitute | ||
MSI directives and an include MSI directive. | ||
- The resulting set of templates can be expanded natively by MSI without the | ||
need for VDCT. | ||
- The DB files created by such an expansion should be equivalent to the | ||
original VDCT generated ones. | ||
""" | ||
|
||
if use_builder: | ||
builder = builder or Path(folder.parent.parent / "etc" / "builder.py") | ||
builder_txt = builder.read_text() | ||
else: | ||
builder_txt = "" | ||
|
||
convert(folder, builder_txt) | ||
|
||
|
||
# test with: python -m vdct2template | ||
# test with: | ||
# python -m vdct2template --version | ||
if __name__ == "__main__": | ||
main() | ||
typer.run(main) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from pathlib import Path | ||
|
||
from .expansion import Expansion | ||
from .regex import DROP | ||
|
||
|
||
def convert(folder: Path, builder_txt: str): | ||
""" | ||
function to oversee conversion of a set of VDB files to template files. | ||
""" | ||
warning = False | ||
targets = list(folder.glob("*.vdb")) | ||
|
||
print(f"converting vdb files in {folder}\n ...") | ||
|
||
for target in targets: | ||
expansion = Expansion(target, folder) | ||
if expansion.parse_expands() > 0: | ||
print(f"writing expansion {expansion.template_path.name}") | ||
expansion.template_path.write_text(expansion.text) | ||
|
||
for file, text in expansion.process_includes(): | ||
print(f"writing template {file.name}") | ||
file.write_text(text) | ||
if file.name in builder_txt: | ||
warning = True | ||
print(f" WARNING: direct reference from builder.py to {file.name}") | ||
|
||
# process the remaining (flat) vdbs | ||
all_vdb_files = {target.name for target in targets} | ||
unprocessed = all_vdb_files - set(Expansion.processed) | ||
|
||
for file in unprocessed: | ||
path = folder / file | ||
template_path = path.with_suffix(".template") | ||
|
||
text = path.read_text() | ||
text = DROP.sub("", text) | ||
|
||
print(f"writing flat {file}") | ||
template_path.write_text(text) | ||
|
||
# give warnings if there are inconsistent macro substitutions | ||
warning |= Expansion.validate_includes() | ||
|
||
if warning: | ||
print("\n WARNINGS DETECTED: check above for details.") | ||
exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from pathlib import Path | ||
from typing import Dict, List | ||
|
||
from vdct2template.macros import Macros | ||
|
||
from .regex import DROP, EXPAND | ||
|
||
|
||
class Expansion: | ||
""" | ||
A class to represent a VDB file that contains expands() blocks. | ||
Provides the necessary conversion to MSI include/substitute statements. | ||
""" | ||
|
||
# class level list of all Expansion instances created | ||
expansions: List["Expansion"] = [] | ||
# class level list of all vdb files processed so far | ||
processed: List[str] = [] | ||
|
||
def __init__(self, filename: Path, folder: Path) -> None: | ||
""" | ||
Constructor: set up properties | ||
""" | ||
self.vdb_path = filename.resolve() | ||
self.folder = folder | ||
self.template_path = filename.with_suffix(".template") | ||
self.includes = [] | ||
self.text = filename.read_text() | ||
|
||
Expansion.expansions.append(self) | ||
|
||
def parse_expands(self) -> int: | ||
""" | ||
Parse the expands() blocks in the VDB file. | ||
Updates the class attribute substitutions with the macro substitutions parsed. | ||
Updates the class attribute text with the VDB file text with the expands() | ||
blocks processed into MSI substitute/include statements. | ||
returns the number of expands() blocks found. | ||
""" | ||
|
||
expands = EXPAND.findall(self.text) | ||
if not expands: | ||
return 0 | ||
|
||
for match in expands: | ||
# match: 0=include path, 1=name, 2=macro text | ||
include_path = self.folder / match[0] | ||
macros = Macros(self.template_path, include_path, match[2]) | ||
self.includes.append(macros) | ||
|
||
# replace the expands() block with the MSI directives | ||
self.text = EXPAND.sub(macros.render_include(), self.text, 1) | ||
|
||
# remove other extraneous VDB things | ||
self.text = DROP.sub("", self.text) | ||
|
||
self.processed.append(self.vdb_path.name) | ||
|
||
return len(expands) | ||
|
||
def process_includes(self): | ||
""" | ||
Process the included files for this VDB file. | ||
""" | ||
for include in self.includes: | ||
if include.vdb_path.name not in Expansion.processed: | ||
yield include.process() | ||
Expansion.processed.append(include.vdb_path.name) | ||
|
||
@classmethod | ||
def validate_includes(cls) -> bool: | ||
""" | ||
Check that all included files are always using the same substitutions | ||
every time they are included. If not then the the replacing of macro | ||
names with _ prefix will be inconsistent between uses of the included | ||
templates and this approach will fail. | ||
""" | ||
warning = False | ||
index: Dict[str, Macros] = {} | ||
|
||
print() | ||
for expansion in cls.expansions: | ||
for include in expansion.includes: | ||
if include.template_path.name in index: | ||
original = index[include.template_path.name] | ||
if include.compare(original): | ||
warning = True | ||
print( | ||
f" WARNING: inconsistent macros for " | ||
f"{include.template_path.name}" | ||
) | ||
print( | ||
f" {include.parent.name} missing:" | ||
f"{original.missing_str(include)}" | ||
) | ||
print( | ||
f" {original.parent.name} missing: " | ||
f"{include.missing_str(original)}" | ||
) | ||
else: | ||
index[include.template_path.name] = include | ||
|
||
return warning |
Oops, something went wrong.