summaryrefslogtreecommitdiffstats
path: root/modules/libpref/init/generate_static_pref_list.py
diff options
context:
space:
mode:
Diffstat (limited to 'modules/libpref/init/generate_static_pref_list.py')
-rw-r--r--modules/libpref/init/generate_static_pref_list.py471
1 files changed, 471 insertions, 0 deletions
diff --git a/modules/libpref/init/generate_static_pref_list.py b/modules/libpref/init/generate_static_pref_list.py
new file mode 100644
index 0000000000..2e9cf9d98f
--- /dev/null
+++ b/modules/libpref/init/generate_static_pref_list.py
@@ -0,0 +1,471 @@
+# 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 os
+import sys
+from collections import defaultdict
+
+import buildconfig
+import yaml
+from mozbuild.preprocessor import Preprocessor
+from mozbuild.util import FileAvoidWrite, ensureParentDir
+from six import StringIO
+
+VALID_KEYS = {
+ "name",
+ "type",
+ "value",
+ "mirror",
+ "do_not_use_directly",
+ "include",
+ "rust",
+}
+
+# Each key is a C++ type; its value is the equivalent non-atomic C++ type.
+VALID_BOOL_TYPES = {
+ "bool": "bool",
+ # These ones are defined in StaticPrefsBase.h.
+ "RelaxedAtomicBool": "bool",
+ "ReleaseAcquireAtomicBool": "bool",
+ "SequentiallyConsistentAtomicBool": "bool",
+}
+
+VALID_TYPES = VALID_BOOL_TYPES.copy()
+VALID_TYPES.update(
+ {
+ "int32_t": "int32_t",
+ "uint32_t": "uint32_t",
+ "float": "float",
+ # These ones are defined in StaticPrefsBase.h.
+ "RelaxedAtomicInt32": "int32_t",
+ "RelaxedAtomicUint32": "uint32_t",
+ "ReleaseAcquireAtomicInt32": "int32_t",
+ "ReleaseAcquireAtomicUint32": "uint32_t",
+ "SequentiallyConsistentAtomicInt32": "int32_t",
+ "SequentiallyConsistentAtomicUint32": "uint32_t",
+ "AtomicFloat": "float",
+ "String": None,
+ "DataMutexString": "nsACString",
+ }
+)
+
+# Map non-atomic C++ types to equivalent Rust types.
+RUST_TYPES = {
+ "bool": "bool",
+ "int32_t": "i32",
+ "uint32_t": "u32",
+ "float": "f32",
+ "DataMutexString": "nsCString",
+}
+
+HEADER_LINE = (
+ "// This file was generated by generate_static_pref_list.py from {input_filename}."
+ " DO NOT EDIT."
+)
+
+MIRROR_TEMPLATES = {
+ "never": """\
+NEVER_PREF("{name}", {typ}, {value})
+""",
+ "once": """\
+ONCE_PREF(
+ "{name}",
+ {base_id},
+ {full_id},
+ {typ}, {value}
+)
+""",
+ "always": """\
+ALWAYS_PREF(
+ "{name}",
+ {base_id},
+ {full_id},
+ {typ}, {value}
+)
+""",
+ "always_datamutex": """\
+ALWAYS_DATAMUTEX_PREF(
+ "{name}",
+ {base_id},
+ {full_id},
+ {typ}, {value}
+)
+""",
+}
+
+STATIC_PREFS_GROUP_H_TEMPLATE1 = """\
+// Include it to gain access to StaticPrefs::{group}_*.
+
+#ifndef mozilla_StaticPrefs_{group}_h
+#define mozilla_StaticPrefs_{group}_h
+"""
+
+STATIC_PREFS_GROUP_H_TEMPLATE2 = """\
+#include "mozilla/StaticPrefListBegin.h"
+#include "mozilla/StaticPrefList_{group}.h"
+#include "mozilla/StaticPrefListEnd.h"
+
+#endif // mozilla_StaticPrefs_{group}_h
+"""
+
+STATIC_PREFS_C_GETTERS_TEMPLATE = """\
+extern "C" {typ} StaticPrefs_{full_id}() {{
+ return mozilla::StaticPrefs::{full_id}();
+}}
+"""
+
+STATIC_PREFS_C_GETTERS_NSSTRING_TEMPLATE = """\
+extern "C" void StaticPrefs_{full_id}(nsACString *result) {{
+ const auto preflock = mozilla::StaticPrefs::{full_id}();
+ result->Append(*preflock);
+}}
+"""
+
+
+def error(msg):
+ raise ValueError(msg)
+
+
+def mk_id(name):
+ "Replace '.' and '-' with '_', e.g. 'foo.bar-baz' becomes 'foo_bar_baz'."
+ return name.replace(".", "_").replace("-", "_")
+
+
+def mk_group(pref):
+ name = pref["name"]
+ return mk_id(name.split(".", 1)[0])
+
+
+def check_pref_list(pref_list):
+ # Pref names seen so far. Used to detect any duplicates.
+ seen_names = set()
+
+ # The previous pref. Used to detect mis-ordered prefs.
+ prev_pref = None
+
+ for pref in pref_list:
+ # Check all given keys are known ones.
+ for key in pref:
+ if key not in VALID_KEYS:
+ error("invalid key `{}`".format(key))
+
+ # 'name' must be present, valid, and in the right section.
+ if "name" not in pref:
+ error("missing `name` key")
+ name = pref["name"]
+ if type(name) != str:
+ error("non-string `name` value `{}`".format(name))
+ if "." not in name:
+ error("`name` value `{}` lacks a '.'".format(name))
+ if name in seen_names:
+ error("`{}` pref is defined more than once".format(name))
+ seen_names.add(name)
+
+ # Prefs must be ordered appropriately.
+ if prev_pref:
+ if mk_group(prev_pref) > mk_group(pref):
+ error(
+ "`{}` pref must come before `{}` pref".format(
+ name, prev_pref["name"]
+ )
+ )
+
+ # 'type' must be present and valid.
+ if "type" not in pref:
+ error("missing `type` key for pref `{}`".format(name))
+ typ = pref["type"]
+ if typ not in VALID_TYPES:
+ error("invalid `type` value `{}` for pref `{}`".format(typ, name))
+
+ # 'value' must be present and valid.
+ if "value" not in pref:
+ error("missing `value` key for pref `{}`".format(name))
+ value = pref["value"]
+ if typ == "String" or typ == "DataMutexString":
+ if type(value) != str:
+ error(
+ "non-string `value` value `{}` for `{}` pref `{}`; "
+ "add double quotes".format(value, typ, name)
+ )
+ elif typ in VALID_BOOL_TYPES:
+ if value not in (True, False):
+ error("invalid boolean value `{}` for pref `{}`".format(value, name))
+
+ # 'mirror' must be present and valid.
+ if "mirror" not in pref:
+ error("missing `mirror` key for pref `{}`".format(name))
+ mirror = pref["mirror"]
+ if typ.startswith("DataMutex"):
+ mirror += "_datamutex"
+ if mirror not in MIRROR_TEMPLATES:
+ error("invalid `mirror` value `{}` for pref `{}`".format(mirror, name))
+
+ # Check 'do_not_use_directly' if present.
+ if "do_not_use_directly" in pref:
+ do_not_use_directly = pref["do_not_use_directly"]
+ if type(do_not_use_directly) != bool:
+ error(
+ "non-boolean `do_not_use_directly` value `{}` for pref "
+ "`{}`".format(do_not_use_directly, name)
+ )
+ if do_not_use_directly and mirror == "never":
+ error(
+ "`do_not_use_directly` uselessly set with `mirror` value "
+ "`never` for pref `{}`".format(pref["name"])
+ )
+
+ # Check 'include' if present.
+ if "include" in pref:
+ include = pref["include"]
+ if type(include) != str:
+ error(
+ "non-string `include` value `{}` for pref `{}`".format(
+ include, name
+ )
+ )
+ if include.startswith("<") and not include.endswith(">"):
+ error(
+ "`include` value `{}` starts with `<` but does not "
+ "end with `>` for pref `{}`".format(include, name)
+ )
+
+ # Check 'rust' if present.
+ if "rust" in pref:
+ rust = pref["rust"]
+ if type(rust) != bool:
+ error("non-boolean `rust` value `{}` for pref `{}`".format(rust, name))
+ if rust and mirror == "never":
+ error(
+ "`rust` uselessly set with `mirror` value `never` for "
+ "pref `{}`".format(pref["name"])
+ )
+
+ prev_pref = pref
+
+
+def generate_code(pref_list, input_filename):
+ check_pref_list(pref_list)
+
+ first_line = HEADER_LINE.format(input_filename=input_filename)
+
+ # The required includes for StaticPrefs_<group>.h.
+ includes = defaultdict(set)
+
+ # StaticPrefList_<group>.h contains all the pref definitions for this
+ # group.
+ static_pref_list_group_h = defaultdict(lambda: [first_line, ""])
+
+ # StaticPrefsCGetters.cpp contains C getters for all the mirrored prefs,
+ # for use by Rust code.
+ static_prefs_c_getters_cpp = [first_line, ""]
+
+ # static_prefs.rs contains C getter declarations and a macro.
+ static_prefs_rs_decls = []
+ static_prefs_rs_macro = []
+
+ # Generate the per-pref code (spread across multiple files).
+ for pref in pref_list:
+ name = pref["name"]
+ typ = pref["type"]
+ value = pref["value"]
+ mirror = pref["mirror"]
+ do_not_use_directly = pref.get("do_not_use_directly")
+ include = pref.get("include")
+ rust = pref.get("rust")
+
+ base_id = mk_id(pref["name"])
+ full_id = base_id
+ if mirror == "once":
+ full_id += "_AtStartup"
+ if do_not_use_directly:
+ full_id += "_DoNotUseDirectly"
+ if typ.startswith("DataMutex"):
+ mirror += "_datamutex"
+
+ group = mk_group(pref)
+
+ if include:
+ if not include.startswith("<"):
+ # It's not a system header. Add double quotes.
+ include = '"{}"'.format(include)
+ includes[group].add(include)
+
+ if typ == "String":
+ # Quote string literals, and escape double-quote chars.
+ value = '"{}"'.format(value.replace('"', '\\"'))
+ elif typ == "DataMutexString":
+ # Quote string literals, and escape double-quote chars.
+ value = '"{}"_ns'.format(value.replace('"', '\\"'))
+ elif typ in VALID_BOOL_TYPES:
+ # Convert Python bools to C++ bools.
+ if value is True:
+ value = "true"
+ elif value is False:
+ value = "false"
+
+ # Append the C++ definition to the relevant output file's code.
+ static_pref_list_group_h[group].append(
+ MIRROR_TEMPLATES[mirror].format(
+ name=name,
+ base_id=base_id,
+ full_id=full_id,
+ typ=typ,
+ value=value,
+ )
+ )
+
+ if rust:
+ passed_type = VALID_TYPES[typ]
+ if passed_type == "nsACString":
+ # Generate the C getter.
+ static_prefs_c_getters_cpp.append(
+ STATIC_PREFS_C_GETTERS_NSSTRING_TEMPLATE.format(full_id=full_id)
+ )
+
+ # Generate the C getter declaration, in Rust.
+ decl = " pub fn StaticPrefs_{full_id}(result: *mut nsstring::nsACString);"
+ static_prefs_rs_decls.append(decl.format(full_id=full_id))
+
+ # Generate the Rust macro entry.
+ macro = ' ("{name}") => (unsafe {{ let mut result = $crate::nsCString::new(); $crate::StaticPrefs_{full_id}(&mut *result); result }});'
+ static_prefs_rs_macro.append(macro.format(name=name, full_id=full_id))
+
+ else:
+ # Generate the C getter.
+ static_prefs_c_getters_cpp.append(
+ STATIC_PREFS_C_GETTERS_TEMPLATE.format(
+ typ=passed_type, full_id=full_id
+ )
+ )
+
+ # Generate the C getter declaration, in Rust.
+ decl = " pub fn StaticPrefs_{full_id}() -> {typ};"
+ static_prefs_rs_decls.append(
+ decl.format(full_id=full_id, typ=RUST_TYPES[passed_type])
+ )
+
+ # Generate the Rust macro entry.
+ macro = (
+ ' ("{name}") => (unsafe {{ $crate::StaticPrefs_{full_id}() }});'
+ )
+ static_prefs_rs_macro.append(macro.format(name=name, full_id=full_id))
+
+ # Delete this so that `group` can be reused below without Flake8
+ # complaining.
+ del group
+
+ # StaticPrefListAll.h contains one `#include "mozilla/StaticPrefList_X.h`
+ # line per pref group.
+ static_pref_list_all_h = [first_line, ""]
+ static_pref_list_all_h.extend(
+ '#include "mozilla/StaticPrefList_{}.h"'.format(group)
+ for group in sorted(static_pref_list_group_h)
+ )
+ static_pref_list_all_h.append("")
+
+ # StaticPrefsAll.h contains one `#include "mozilla/StaticPrefs_X.h` line per
+ # pref group.
+ static_prefs_all_h = [first_line, ""]
+ static_prefs_all_h.extend(
+ '#include "mozilla/StaticPrefs_{}.h"'.format(group)
+ for group in sorted(static_pref_list_group_h)
+ )
+ static_prefs_all_h.append("")
+
+ # StaticPrefs_<group>.h wraps StaticPrefList_<group>.h. It is the header
+ # used directly by application code.
+ static_prefs_group_h = defaultdict(list)
+ for group in sorted(static_pref_list_group_h):
+ static_prefs_group_h[group] = [first_line]
+ static_prefs_group_h[group].append(
+ STATIC_PREFS_GROUP_H_TEMPLATE1.format(group=group)
+ )
+ if group in includes:
+ # Add any necessary includes, from 'h_include' values.
+ for include in sorted(includes[group]):
+ static_prefs_group_h[group].append("#include {}".format(include))
+ static_prefs_group_h[group].append("")
+ static_prefs_group_h[group].append(
+ STATIC_PREFS_GROUP_H_TEMPLATE2.format(group=group)
+ )
+
+ # static_prefs.rs contains the Rust macro getters.
+ static_prefs_rs = [first_line, "", "pub use nsstring::nsCString;", 'extern "C" {']
+ static_prefs_rs.extend(static_prefs_rs_decls)
+ static_prefs_rs.extend(["}", "", "#[macro_export]", "macro_rules! pref {"])
+ static_prefs_rs.extend(static_prefs_rs_macro)
+ static_prefs_rs.extend(["}", ""])
+
+ def fold(lines):
+ return "\n".join(lines)
+
+ return {
+ "static_pref_list_all_h": fold(static_pref_list_all_h),
+ "static_prefs_all_h": fold(static_prefs_all_h),
+ "static_pref_list_group_h": {
+ k: fold(v) for k, v in static_pref_list_group_h.items()
+ },
+ "static_prefs_group_h": {k: fold(v) for k, v in static_prefs_group_h.items()},
+ "static_prefs_c_getters_cpp": fold(static_prefs_c_getters_cpp),
+ "static_prefs_rs": fold(static_prefs_rs),
+ }
+
+
+def emit_code(fd, pref_list_filename):
+ pp = Preprocessor()
+ pp.context.update(buildconfig.defines["ALLDEFINES"])
+
+ # A necessary hack until MOZ_DEBUG_FLAGS are part of buildconfig.defines.
+ if buildconfig.substs.get("MOZ_DEBUG"):
+ pp.context["DEBUG"] = "1"
+
+ if buildconfig.substs.get("CPU_ARCH") == "aarch64":
+ pp.context["MOZ_AARCH64"] = True
+
+ if buildconfig.substs.get("MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS"):
+ pp.context["MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS"] = True
+
+ pp.out = StringIO()
+ pp.do_filter("substitution")
+ pp.do_include(pref_list_filename)
+
+ try:
+ pref_list = yaml.safe_load(pp.out.getvalue())
+ input_file = os.path.relpath(
+ pref_list_filename,
+ os.environ.get("GECKO_PATH", os.environ.get("TOPSRCDIR")),
+ )
+ code = generate_code(pref_list, input_file)
+ except (IOError, ValueError) as e:
+ print("{}: error:\n {}\n".format(pref_list_filename, e))
+ sys.exit(1)
+
+ # When generating multiple files from a script, the build system treats the
+ # first named output file (StaticPrefListAll.h in this case) specially -- it
+ # is created elsewhere, and written to via `fd`.
+ fd.write(code["static_pref_list_all_h"])
+
+ # We must create the remaining output files ourselves. This requires
+ # creating the output directory directly if it doesn't already exist.
+ ensureParentDir(fd.name)
+ init_dirname = os.path.dirname(fd.name)
+
+ with FileAvoidWrite("StaticPrefsAll.h") as fd:
+ fd.write(code["static_prefs_all_h"])
+
+ for group, text in sorted(code["static_pref_list_group_h"].items()):
+ filename = "StaticPrefList_{}.h".format(group)
+ with FileAvoidWrite(os.path.join(init_dirname, filename)) as fd:
+ fd.write(text)
+
+ for group, text in sorted(code["static_prefs_group_h"].items()):
+ filename = "StaticPrefs_{}.h".format(group)
+ with FileAvoidWrite(filename) as fd:
+ fd.write(text)
+
+ with FileAvoidWrite(os.path.join(init_dirname, "StaticPrefsCGetters.cpp")) as fd:
+ fd.write(code["static_prefs_c_getters_cpp"])
+
+ with FileAvoidWrite("static_prefs.rs") as fd:
+ fd.write(code["static_prefs_rs"])