stylo: Allow regenerating atoms as part of the normal generation of bindings.

This configures the regeneration of atoms as part of the normal generation of
bindings, so it stops being a whole different process.

This also adds a generated file to components/style/generated with a convenience
macro invocation for pseudo-elements, which comes handy in order to avoid
duplication.
This commit is contained in:
Emilio Cobos Álvarez 2016-08-09 21:30:17 -07:00
parent 54b92015cb
commit 12fcb7a2e7
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
3 changed files with 226 additions and 79 deletions

View file

@ -13,6 +13,8 @@ import copy
import subprocess import subprocess
import tempfile import tempfile
import regen_atoms
DESCRIPTION = 'Regenerate the rust version of the structs or the bindings file.' DESCRIPTION = 'Regenerate the rust version of the structs or the bindings file.'
TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
COMMON_BUILD_KEY = "__common__" COMMON_BUILD_KEY = "__common__"
@ -38,6 +40,7 @@ COMPILATION_TARGETS = {
}, },
# Generation of style structs. # Generation of style structs.
"structs": { "structs": {
"target_dir": "../gecko_bindings",
"test": True, "test": True,
"flags": [ "flags": [
"-ignore-functions", "-ignore-functions",
@ -108,6 +111,7 @@ COMPILATION_TARGETS = {
}, },
# Generation of the ffi bindings. # Generation of the ffi bindings.
"bindings": { "bindings": {
"target_dir": "../gecko_bindings",
"raw_lines": [ "raw_lines": [
"use heapsize::HeapSizeOf;", "use heapsize::HeapSizeOf;",
], ],
@ -140,6 +144,10 @@ COMPILATION_TARGETS = {
"void_types": [ "void_types": [
"nsINode", "nsIDocument", "nsIPrincipal", "nsIURI", "nsINode", "nsIDocument", "nsIPrincipal", "nsIURI",
], ],
},
"atoms": {
"custom_build": regen_atoms.build,
} }
} }
@ -212,6 +220,17 @@ def build(objdir, target_name, kind_name=None,
assert ((kind_name is None and "build_kinds" not in current_target) or assert ((kind_name is None and "build_kinds" not in current_target) or
(kind_name in current_target["build_kinds"])) (kind_name in current_target["build_kinds"]))
if "custom_build" in current_target:
print("[CUSTOM] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='')
sys.stdout.flush()
ret = current_target["custom_build"](objdir, verbose=True)
if ret != 0:
print("FAIL")
else:
print("OK")
return ret
if bindgen is None: if bindgen is None:
bindgen = os.path.join(TOOLS_DIR, "rust-bindgen") bindgen = os.path.join(TOOLS_DIR, "rust-bindgen")
@ -221,18 +240,23 @@ def build(objdir, target_name, kind_name=None,
else: else:
bindgen = [bindgen] bindgen = [bindgen]
if output_filename is None:
filename = "{}.rs".format(target_name)
if kind_name is not None:
filename = "{}_{}.rs".format(target_name, kind_name)
output_filename = "{}/../{}".format(TOOLS_DIR, filename)
if kind_name is not None: if kind_name is not None:
current_target = copy.deepcopy(current_target) current_target = copy.deepcopy(current_target)
extend_object(current_target, current_target["build_kinds"][kind_name]) extend_object(current_target, current_target["build_kinds"][kind_name])
target_dir = None
if output_filename is None and "target_dir" in current_target:
target_dir = current_target["target_dir"]
if output_filename is None:
output_filename = "{}.rs".format(target_name)
if kind_name is not None:
output_filename = "{}_{}.rs".format(target_name, kind_name)
if target_dir:
output_filename = "{}/{}".format(target_dir, output_filename)
print("[BINDGEN] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='') print("[BINDGEN] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='')
sys.stdout.flush() sys.stdout.flush()

View file

@ -0,0 +1,194 @@
#!/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 http://mozilla.org/MPL/2.0/.
import re
import os
def gnu_symbolify(source, ident):
return "_ZN" + str(len(source.CLASS)) + source.CLASS + str(len(ident)) + ident + "E"
def msvc64_symbolify(source, ident):
return "?" + ident + "@" + source.CLASS + "@@2PEAV" + source.TYPE + "@@EA"
def msvc32_symbolify(source, ident):
return "?" + ident + "@" + source.CLASS + "@@2PAV" + source.TYPE + "@@A"
class GkAtomSource:
PATTERN = re.compile('^GK_ATOM\((.+),\s*"(.*)"\)')
FILE = "dist/include/nsGkAtomList.h"
CLASS = "nsGkAtoms"
TYPE = "nsIAtom"
class CSSPseudoElementsAtomSource:
PATTERN = re.compile('^CSS_PSEUDO_ELEMENT\((.+),\s*"(.*)",')
FILE = "dist/include/nsCSSPseudoElementList.h"
CLASS = "nsCSSPseudoElements"
# NB: nsICSSPseudoElement is effectively the same as a nsIAtom, but we need
# this for MSVC name mangling.
TYPE = "nsICSSPseudoElement"
class CSSAnonBoxesAtomSource:
PATTERN = re.compile('^CSS_ANON_BOX\((.+),\s*"(.*)"\)')
FILE = "dist/include/nsCSSAnonBoxList.h"
CLASS = "nsCSSAnonBoxes"
TYPE = "nsICSSAnonBoxPseudo"
SOURCES = [
GkAtomSource,
CSSPseudoElementsAtomSource,
CSSAnonBoxesAtomSource,
]
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, source, ident, value):
self.ident = "{}_{}".format(source.CLASS, ident)
self._original_ident = ident
self.value = value
self.source = source
def cpp_class(self):
return self.source.CLASS
def gnu_symbol(self):
return gnu_symbolify(self.source, self._original_ident)
def msvc32_symbol(self):
return msvc32_symbolify(self.source, self._original_ident)
def msvc64_symbol(self):
return msvc64_symbolify(self.source, self._original_ident)
def type(self):
return self.source.TYPE
def collect_atoms(objdir):
atoms = []
for source in SOURCES:
with open(os.path.join(objdir, source.FILE)) as f:
for line in f.readlines():
result = re.match(source.PATTERN, line)
if result:
atoms.append(Atom(source, result.group(1), result.group(2)))
return atoms
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 http://mozilla.org/MPL/2.0/. */
/* Autogenerated file, DO NOT EDIT DIRECTLY */
"""[1:]
def write_atom_macro(atoms, file_name):
ATOM_TEMPLATE = """
#[link_name = "{link_name}"]
pub static {name}: *mut {type};
"""[1:]
def write_items(f, func):
f.write(" extern {\n")
for atom in atoms:
f.write(ATOM_TEMPLATE.format(name=atom.ident,
link_name=func(atom),
type=atom.type()))
f.write(" }\n")
with open(file_name, "wb") as f:
f.write(PRELUDE)
f.write("use gecko_bindings::structs::nsIAtom;\n\n")
f.write("use Atom;\n\n")
for source in SOURCES:
if source.TYPE != "nsIAtom":
f.write("pub enum {} {{}}\n\n".format(source.TYPE))
f.write("""
#[inline(always)] pub fn unsafe_atom_from_static(ptr: *mut nsIAtom) -> Atom {
unsafe { Atom::from_static(ptr) }
}\n\n
""")
f.write("cfg_if! {\n")
f.write(" if #[cfg(not(target_env = \"msvc\"))] {\n")
write_items(f, Atom.gnu_symbol)
f.write(" } else if #[cfg(target_pointer_width = \"64\")] {\n")
write_items(f, Atom.msvc64_symbol)
f.write(" } else {\n")
write_items(f, Atom.msvc32_symbol)
f.write(" }\n")
f.write("}\n\n")
f.write("#[macro_export]\n")
f.write("macro_rules! atom {\n")
f.writelines(['("%s") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::%s as *mut _) };\n'
% (atom.value, atom.ident) for atom in atoms])
f.write("}\n")
PSEUDO_ELEMENT_HEADER = """
/*
* This file contains a helper macro invocation to aid Gecko's style system
* pseudo-element integration.
*
* This file is NOT INTENDED to be compiled as a standalone module.
*
* Also, it guarantees the property that normal pseudo-elements are processed
* before anonymous boxes.
*
* Expected usage is as follows:
*
* ```
* fn have_to_use_pseudo_elements() {
* macro_rules pseudo_element! {
* ($pseudo_str_with_colon:expr, $pseudo_atom:expr, $is_anon_box:true) => {{
* // Stuff stuff stuff.
* }}
* }
* include!("path/to/helper.rs")
* }
* ```
*
*/
"""
PSEUDO_ELEMENT_MACRO_INVOCATION = """
pseudo_element!(\"{}\",
atom!(\"{}\"),
{});
"""[1:]
def write_pseudo_element_helper(atoms, target_filename):
with open(target_filename, "wb") as f:
f.write(PRELUDE)
f.write(PSEUDO_ELEMENT_HEADER)
f.write("{\n")
for atom in atoms:
if atom.type() == "nsICSSPseudoElement":
f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "false"))
elif atom.type() == "nsICSSAnonBoxPseudo":
f.write(PSEUDO_ELEMENT_MACRO_INVOCATION.format(atom.value, atom.value, "true"))
f.write("}\n")
def build(objdir, verbose=False):
atoms = collect_atoms(objdir)
write_atom_macro(atoms, "../string_cache/atom_macro.rs")
write_pseudo_element_helper(atoms, "../../../components/style/generated/gecko_pseudo_element_helper.rs")
return 0

View file

@ -1,71 +0,0 @@
# 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 http://mozilla.org/MPL/2.0/.
import re
import sys
if len(sys.argv) != 2:
print "usage: ./%s PATH/TO/OBJDIR" % sys.argv[0]
objdir_path = sys.argv[1]
def line_to_atom(line):
result = re.match('^GK_ATOM\((.+),\s*"(.*)"\)', line)
return (result.group(1), result.group(2))
def map_atom(ident):
if ident in {"box", "loop", "match", "mod", "ref",
"self", "type", "use", "where", "in"}:
return ident + "_"
return ident
def gnu_symbolify(ident):
return "_ZN9nsGkAtoms" + str(len(ident)) + ident + "E"
def msvc64_symbolify(ident):
return "?" + ident + "@nsGkAtoms@@2PEAVnsIAtom@@EA"
def msvc32_symbolify(ident):
return "?" + ident + "@nsGkAtoms@@2PAVnsIAtom@@A"
def write_items(f, func):
f.write(" extern {\n")
for atom in atoms:
f.write(TEMPLATE.format(name=map_atom(atom[0]),
link_name=func(atom[0])))
f.write(" }\n")
with open(objdir_path + "/dist/include/nsGkAtomList.h") as f:
lines = [line for line in f.readlines() if line.startswith("GK_ATOM")]
atoms = [line_to_atom(line) for line in lines]
TEMPLATE = """
#[link_name = "{link_name}"]
pub static {name}: *mut nsIAtom;
"""[1:]
with open("atom_macro.rs", "wb") as f:
f.write("use gecko_bindings::structs::nsIAtom;\n\n")
f.write("use Atom;\n\n")
f.write("pub fn unsafe_atom_from_static(ptr: *mut nsIAtom) -> Atom { unsafe { Atom::from_static(ptr) } }\n\n")
f.write("cfg_if! {\n")
f.write(" if #[cfg(not(target_env = \"msvc\"))] {\n")
write_items(f, gnu_symbolify)
f.write(" } else if #[cfg(target_pointer_width = \"64\")] {\n")
write_items(f, msvc64_symbolify)
f.write(" } else {\n")
write_items(f, msvc32_symbolify)
f.write(" }\n")
f.write("}\n\n")
f.write("#[macro_export]\n")
f.write("macro_rules! atom {\n")
f.writelines(['("%s") => { $crate::atom_macro::unsafe_atom_from_static($crate::atom_macro::%s) };\n'
% (atom[1], map_atom(atom[0])) for atom in atoms])
f.write("}\n")