#!/usr/bin/env python # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. import re import os import sys from io import BytesIO from pseudo_elements import PseudoElementData import build # Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true)`. PATTERN = re.compile( r'^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*\)', re.MULTILINE, ) FILE = "include/nsGkAtomList.h" def map_atom(ident): if ident in { "box", "loop", "match", "mod", "ref", "self", "type", "use", "where", "in", }: return ident + "_" return ident class Atom: def __init__(self, ident, value, hash): self.ident = "nsGkAtoms_{}".format(ident) self.original_ident = ident self.value = value self.hash = hash def collect_atoms(objdir): atoms = [] path = os.path.abspath(os.path.join(objdir, FILE)) print("cargo:rerun-if-changed={}".format(path)) with open(path) as f: content = f.read() for result in PATTERN.finditer(content): atoms.append( Atom( result.group(1), result.group(2), result.group(3), ) ) return atoms class FileAvoidWrite(BytesIO): """File-like object that buffers output and only writes if content changed.""" def __init__(self, filename): BytesIO.__init__(self) self.name = filename def write(self, buf): if isinstance(buf, str): buf = buf.encode("utf-8") BytesIO.write(self, buf) def close(self): buf = self.getvalue() BytesIO.close(self) try: with open(self.name, "rb") as f: old_content = f.read() if old_content == buf: print("{} is not changed, skip".format(self.name)) return except IOError: pass with open(self.name, "wb") as f: f.write(buf) def __enter__(self): return self def __exit__(self, type, value, traceback): if not self.closed: self.close() PRELUDE = """ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // Autogenerated file created by components/style/gecko/regen_atoms.py. // DO NOT EDIT DIRECTLY """[ 1: ] RULE_TEMPLATE = """ ("{atom}") => {{{{ #[allow(unsafe_code)] #[allow(unused_unsafe)] unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }} }}}}; """[ 1: ] MACRO_TEMPLATE = """ /// Returns a static atom by passing the literal string it represents. #[macro_export] macro_rules! atom {{ {body}\ }} """ def write_atom_macro(atoms, file_name): with FileAvoidWrite(file_name) as f: f.write(PRELUDE) macro_rules = [ RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i) for (i, atom) in enumerate(atoms) ] f.write(MACRO_TEMPLATE.format(body="".join(macro_rules))) def generate_atoms(dist, out): atoms = collect_atoms(dist) write_atom_macro(atoms, os.path.join(out, "atom_macro.rs")) def generate_pseudo_elements(dist, out): data = PseudoElementData() pseudo_definition_template = os.path.join( os.path.dirname(__file__), "pseudo_element_definition.mako.rs" ) print(f"cargo:rerun-if-changed={pseudo_definition_template}") for f in data.path_dependencies: print(f"cargo:rerun-if-changed={f}") target = os.path.join(out, "pseudo_element_definition.rs") with FileAvoidWrite(target) as f: f.write(build.render(pseudo_definition_template, PSEUDOS=data.all_pseudos())) if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: {} dist out".format(sys.argv[0])) exit(2) dist = sys.argv[1] out = sys.argv[2] generate_atoms(dist, out) generate_pseudo_elements(dist, out)