mirror of
https://github.com/servo/servo.git
synced 2025-07-13 18:33:40 +01:00
Auto merge of #12212 - emilio:stylo-regen-script, r=bholley
stylo: Move all binding-generator logic code to a python script. <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors <!-- Either: --> - [x] These changes do not require tests because tooling <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> --- This not only makes us more consistent with the rest of the codebase but also: * Makes us repeat less code like common flags and all that stuff. * Reduces the noise of the build: You only get the output of the commands on failure or when you pass the -v flag. * Makes you able to select a single kind of build or multiple in the same place. I've basically kept the regen.sh script because of the LIBCLANG_PATH checks, but at least from Linux I don't need them anymore. Also, that logic could be moved to the new script. The whole point of this isn't only making it prettier and easier to use, but also allowing me to write more complex logic in the binding generator scripts, that I will probably need to integrate the DOM enum types we need for animations and such easily (can't be just an include, because that pulls in another header with the same name bringing a lot of DOM and IDL churn). For reference, here's a successful regen round with the script: ``` ./regen.py --target all /home/emilio/projects/moz/stylo/gecko/obj-x86_64-pc-linux-gnu/ [BINDGEN] structs::release in "/home/emilio/projects/moz/stylo/gecko/obj-x86_64-pc-linux-gnu/"... OK [RUSTC]... OK [RUSTC_TEST]... OK test result: ok. 168 passed; 0 failed; 0 ignored; 0 measured [BINDGEN] structs::debug in "/home/emilio/projects/moz/stylo/gecko/obj-x86_64-pc-linux-gnu/"... OK [RUSTC]... OK [RUSTC_TEST]... OK test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured [BINDGEN] bindings::None in "/home/emilio/projects/moz/stylo/gecko/obj-x86_64-pc-linux-gnu/"... OK ``` --- r? @bholley Maybe @jgraham and/or @Wafflespeanut want to take a look to criticize my (lack of) pythonism ;-) <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12212) <!-- Reviewable:end -->
This commit is contained in:
commit
e78459e667
8 changed files with 1660 additions and 1635 deletions
|
@ -1,33 +1,118 @@
|
|||
/* automatically generated by rust-bindgen */
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use structs::nsStyleFont;
|
||||
unsafe impl Send for nsStyleFont {}
|
||||
unsafe impl Sync for nsStyleFont {}
|
||||
impl HeapSizeOf for nsStyleFont { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleColor;
|
||||
unsafe impl Send for nsStyleColor {}
|
||||
unsafe impl Sync for nsStyleColor {}
|
||||
impl HeapSizeOf for nsStyleColor { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleList;
|
||||
unsafe impl Send for nsStyleList {}
|
||||
unsafe impl Sync for nsStyleList {}
|
||||
impl HeapSizeOf for nsStyleList { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleText;
|
||||
unsafe impl Send for nsStyleText {}
|
||||
unsafe impl Sync for nsStyleText {}
|
||||
impl HeapSizeOf for nsStyleText { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleVisibility;
|
||||
unsafe impl Send for nsStyleVisibility {}
|
||||
unsafe impl Sync for nsStyleVisibility {}
|
||||
impl HeapSizeOf for nsStyleVisibility { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleUserInterface;
|
||||
unsafe impl Send for nsStyleUserInterface {}
|
||||
unsafe impl Sync for nsStyleUserInterface {}
|
||||
impl HeapSizeOf for nsStyleUserInterface { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleTableBorder;
|
||||
unsafe impl Send for nsStyleTableBorder {}
|
||||
unsafe impl Sync for nsStyleTableBorder {}
|
||||
impl HeapSizeOf for nsStyleTableBorder { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleSVG;
|
||||
unsafe impl Send for nsStyleSVG {}
|
||||
unsafe impl Sync for nsStyleSVG {}
|
||||
impl HeapSizeOf for nsStyleSVG { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleVariables;
|
||||
unsafe impl Send for nsStyleVariables {}
|
||||
unsafe impl Sync for nsStyleVariables {}
|
||||
impl HeapSizeOf for nsStyleVariables { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleBackground;
|
||||
unsafe impl Send for nsStyleBackground {}
|
||||
unsafe impl Sync for nsStyleBackground {}
|
||||
impl HeapSizeOf for nsStyleBackground { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStylePosition;
|
||||
unsafe impl Send for nsStylePosition {}
|
||||
unsafe impl Sync for nsStylePosition {}
|
||||
impl HeapSizeOf for nsStylePosition { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleTextReset;
|
||||
unsafe impl Send for nsStyleTextReset {}
|
||||
unsafe impl Sync for nsStyleTextReset {}
|
||||
impl HeapSizeOf for nsStyleTextReset { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleDisplay;
|
||||
unsafe impl Send for nsStyleDisplay {}
|
||||
unsafe impl Sync for nsStyleDisplay {}
|
||||
impl HeapSizeOf for nsStyleDisplay { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleContent;
|
||||
unsafe impl Send for nsStyleContent {}
|
||||
unsafe impl Sync for nsStyleContent {}
|
||||
impl HeapSizeOf for nsStyleContent { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleUIReset;
|
||||
unsafe impl Send for nsStyleUIReset {}
|
||||
unsafe impl Sync for nsStyleUIReset {}
|
||||
impl HeapSizeOf for nsStyleUIReset { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleTable;
|
||||
unsafe impl Send for nsStyleTable {}
|
||||
unsafe impl Sync for nsStyleTable {}
|
||||
impl HeapSizeOf for nsStyleTable { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleMargin;
|
||||
unsafe impl Send for nsStyleMargin {}
|
||||
unsafe impl Sync for nsStyleMargin {}
|
||||
impl HeapSizeOf for nsStyleMargin { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStylePadding;
|
||||
unsafe impl Send for nsStylePadding {}
|
||||
unsafe impl Sync for nsStylePadding {}
|
||||
impl HeapSizeOf for nsStylePadding { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleBorder;
|
||||
unsafe impl Send for nsStyleBorder {}
|
||||
unsafe impl Sync for nsStyleBorder {}
|
||||
impl HeapSizeOf for nsStyleBorder { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleOutline;
|
||||
unsafe impl Send for nsStyleOutline {}
|
||||
unsafe impl Sync for nsStyleOutline {}
|
||||
impl HeapSizeOf for nsStyleOutline { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleXUL;
|
||||
unsafe impl Send for nsStyleXUL {}
|
||||
unsafe impl Sync for nsStyleXUL {}
|
||||
impl HeapSizeOf for nsStyleXUL { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleSVGReset;
|
||||
unsafe impl Send for nsStyleSVGReset {}
|
||||
unsafe impl Sync for nsStyleSVGReset {}
|
||||
impl HeapSizeOf for nsStyleSVGReset { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleColumn;
|
||||
unsafe impl Send for nsStyleColumn {}
|
||||
unsafe impl Sync for nsStyleColumn {}
|
||||
impl HeapSizeOf for nsStyleColumn { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleEffects;
|
||||
unsafe impl Send for nsStyleEffects {}
|
||||
unsafe impl Sync for nsStyleEffects {}
|
||||
impl HeapSizeOf for nsStyleEffects { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleImage;
|
||||
unsafe impl Send for nsStyleImage {}
|
||||
unsafe impl Sync for nsStyleImage {}
|
||||
impl HeapSizeOf for nsStyleImage { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleGradient;
|
||||
unsafe impl Send for nsStyleGradient {}
|
||||
unsafe impl Sync for nsStyleGradient {}
|
||||
impl HeapSizeOf for nsStyleGradient { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleCoord;
|
||||
unsafe impl Send for nsStyleCoord {}
|
||||
unsafe impl Sync for nsStyleCoord {}
|
||||
impl HeapSizeOf for nsStyleCoord { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::nsStyleGradientStop;
|
||||
unsafe impl Send for nsStyleGradientStop {}
|
||||
unsafe impl Sync for nsStyleGradientStop {}
|
||||
impl HeapSizeOf for nsStyleGradientStop { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
use structs::SheetParsingMode;
|
||||
use structs::nsMainThreadPtrHandle;
|
||||
use structs::nsMainThreadPtrHolder;
|
||||
|
@ -36,91 +121,6 @@ use structs::nsFont;
|
|||
use structs::FontFamilyList;
|
||||
use structs::FontFamilyType;
|
||||
use structs::nsIAtom;
|
||||
use heapsize::HeapSizeOf;
|
||||
unsafe impl Send for nsStyleFont {}
|
||||
unsafe impl Sync for nsStyleFont {}
|
||||
impl HeapSizeOf for nsStyleFont { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleColor {}
|
||||
unsafe impl Sync for nsStyleColor {}
|
||||
impl HeapSizeOf for nsStyleColor { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleList {}
|
||||
unsafe impl Sync for nsStyleList {}
|
||||
impl HeapSizeOf for nsStyleList { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleText {}
|
||||
unsafe impl Sync for nsStyleText {}
|
||||
impl HeapSizeOf for nsStyleText { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleVisibility {}
|
||||
unsafe impl Sync for nsStyleVisibility {}
|
||||
impl HeapSizeOf for nsStyleVisibility { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleUserInterface {}
|
||||
unsafe impl Sync for nsStyleUserInterface {}
|
||||
impl HeapSizeOf for nsStyleUserInterface { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleTableBorder {}
|
||||
unsafe impl Sync for nsStyleTableBorder {}
|
||||
impl HeapSizeOf for nsStyleTableBorder { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleSVG {}
|
||||
unsafe impl Sync for nsStyleSVG {}
|
||||
impl HeapSizeOf for nsStyleSVG { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleVariables {}
|
||||
unsafe impl Sync for nsStyleVariables {}
|
||||
impl HeapSizeOf for nsStyleVariables { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleBackground {}
|
||||
unsafe impl Sync for nsStyleBackground {}
|
||||
impl HeapSizeOf for nsStyleBackground { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStylePosition {}
|
||||
unsafe impl Sync for nsStylePosition {}
|
||||
impl HeapSizeOf for nsStylePosition { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleTextReset {}
|
||||
unsafe impl Sync for nsStyleTextReset {}
|
||||
impl HeapSizeOf for nsStyleTextReset { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleDisplay {}
|
||||
unsafe impl Sync for nsStyleDisplay {}
|
||||
impl HeapSizeOf for nsStyleDisplay { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleContent {}
|
||||
unsafe impl Sync for nsStyleContent {}
|
||||
impl HeapSizeOf for nsStyleContent { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleUIReset {}
|
||||
unsafe impl Sync for nsStyleUIReset {}
|
||||
impl HeapSizeOf for nsStyleUIReset { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleTable {}
|
||||
unsafe impl Sync for nsStyleTable {}
|
||||
impl HeapSizeOf for nsStyleTable { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleMargin {}
|
||||
unsafe impl Sync for nsStyleMargin {}
|
||||
impl HeapSizeOf for nsStyleMargin { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStylePadding {}
|
||||
unsafe impl Sync for nsStylePadding {}
|
||||
impl HeapSizeOf for nsStylePadding { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleBorder {}
|
||||
unsafe impl Sync for nsStyleBorder {}
|
||||
impl HeapSizeOf for nsStyleBorder { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleOutline {}
|
||||
unsafe impl Sync for nsStyleOutline {}
|
||||
impl HeapSizeOf for nsStyleOutline { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleXUL {}
|
||||
unsafe impl Sync for nsStyleXUL {}
|
||||
impl HeapSizeOf for nsStyleXUL { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleSVGReset {}
|
||||
unsafe impl Sync for nsStyleSVGReset {}
|
||||
impl HeapSizeOf for nsStyleSVGReset { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleColumn {}
|
||||
unsafe impl Sync for nsStyleColumn {}
|
||||
impl HeapSizeOf for nsStyleColumn { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleEffects {}
|
||||
unsafe impl Sync for nsStyleEffects {}
|
||||
impl HeapSizeOf for nsStyleEffects { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleImage {}
|
||||
unsafe impl Sync for nsStyleImage {}
|
||||
impl HeapSizeOf for nsStyleImage { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleGradient {}
|
||||
unsafe impl Sync for nsStyleGradient {}
|
||||
impl HeapSizeOf for nsStyleGradient { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleCoord {}
|
||||
unsafe impl Sync for nsStyleCoord {}
|
||||
impl HeapSizeOf for nsStyleCoord { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
unsafe impl Send for nsStyleGradientStop {}
|
||||
unsafe impl Sync for nsStyleGradientStop {}
|
||||
impl HeapSizeOf for nsStyleGradientStop { fn heap_size_of_children(&self) -> usize { 0 } }
|
||||
|
||||
pub enum nsINode { }
|
||||
pub type RawGeckoNode = nsINode;
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
385
ports/geckolib/gecko_bindings/tools/regen.py
Executable file
385
ports/geckolib/gecko_bindings/tools/regen.py
Executable file
|
@ -0,0 +1,385 @@
|
|||
#!/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/.
|
||||
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import platform
|
||||
import copy
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
DESCRIPTION = 'Regenerate the rust version of the structs or the bindings file.'
|
||||
TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
COMMON_BUILD_KEY = "__common__"
|
||||
|
||||
COMPILATION_TARGETS = {
|
||||
# Flags common for all the targets.
|
||||
COMMON_BUILD_KEY: {
|
||||
"flags": [
|
||||
"-x", "c++", "-std=gnu++0x",
|
||||
"-allow-unknown-types", "-no-bitfield-methods",
|
||||
"-no-type-renaming", "-no-namespaced-constants",
|
||||
"-DTRACING=1", "-DIMPL_LIBXUL", "-DMOZ_STYLO_BINDINGS=1",
|
||||
"-DMOZILLA_INTERNAL_API",
|
||||
],
|
||||
"search_dirs": [
|
||||
"{}/dist/include",
|
||||
"{}/dist/include/nspr",
|
||||
"{}/../nsprpub/pr/include"
|
||||
],
|
||||
"includes": [
|
||||
"{}/mozilla-config.h",
|
||||
],
|
||||
},
|
||||
# Generation of style structs.
|
||||
"structs": {
|
||||
"test": True,
|
||||
"flags": [
|
||||
"-ignore-functions",
|
||||
],
|
||||
"includes": [
|
||||
"{}/dist/include/nsThemeConstants.h",
|
||||
"{}/dist/include/mozilla/dom/AnimationEffectReadOnlyBinding.h",
|
||||
],
|
||||
"files": [
|
||||
"{}/dist/include/nsStyleStruct.h",
|
||||
],
|
||||
"build_kinds": {
|
||||
"debug": {
|
||||
"flags": [
|
||||
"-DDEBUG=1",
|
||||
"-DJS_DEBUG=1",
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
}
|
||||
},
|
||||
"match_headers": [
|
||||
"RefCountType.h", "nscore.h", "nsError.h", "nsID.h", "nsString",
|
||||
"nsAString", "nsSubstring", "nsTSubstring", "nsTString",
|
||||
"nsISupportsBase.h", "nsCOMPtr.h", "nsIAtom.h", "nsIURI.h",
|
||||
"nsAutoPtr.h", "nsColor.h", "nsCoord.h", "nsPoint.h", "nsRect.h",
|
||||
"nsMargin.h", "nsThemeConstants.h", "nsCSSProperty.h",
|
||||
"CSSVariableValues.h", "nsFont.h", "nsTHashtable.h",
|
||||
"PLDHashTable.h", "nsColor.h", "nsStyleStruct.h", "nsStyleCoord.h",
|
||||
"RefPtr.h", "nsISupportsImpl.h", "gfxFontConstants.h",
|
||||
"gfxFontFamilyList.h", "gfxFontFeatures.h", "imgRequestProxy.h",
|
||||
"nsIRequest.h", "imgIRequest.h", "CounterStyleManager.h",
|
||||
"nsStyleConsts.h", "nsCSSValue.h", "SheetType.h", "nsIPrincipal.h",
|
||||
"nsDataHashtable.h", "nsCSSScanner.h", "utility", "nsTArray",
|
||||
"pair", "SheetParsingMode.h", "StaticPtr.h", "nsProxyRelease.h",
|
||||
"mozilla/dom/AnimationEffectReadOnlyBinding.h",
|
||||
"/Types.h", # <- Disallow UnionTypes.h
|
||||
],
|
||||
"blacklist": [
|
||||
"IsDestructibleFallbackImpl", "IsDestructibleFallback",
|
||||
"nsProxyReleaseEvent", "FallibleTArray", "nsTArray_Impl",
|
||||
"__is_tuple_like_impl", "tuple_size", "tuple",
|
||||
"__make_pair_return_impl", "__make_pair_return", "tuple_element",
|
||||
"_Itup_cat", "AnimationEffectTimingProperties",
|
||||
"FastAnimationEffectTimingProperties", "ComputedTimingProperties",
|
||||
"FastComputedTimingProperties",
|
||||
],
|
||||
"opaque_types": [
|
||||
"nsIntMargin", "nsIntPoint", "nsIntRect", "nsCOMArray",
|
||||
"nsDependentString", "EntryStore", "gfxFontFeatureValueSet",
|
||||
"imgRequestProxy", "imgRequestProxyStatic", "CounterStyleManager",
|
||||
"ImageValue", "URLValue", "URLValueData", "nsIPrincipal",
|
||||
"nsDataHashtable", "imgIRequest"
|
||||
]
|
||||
},
|
||||
# Generation of the ffi bindings.
|
||||
"bindings": {
|
||||
"raw_lines": [
|
||||
"use heapsize::HeapSizeOf;",
|
||||
],
|
||||
"match_headers": [
|
||||
"ServoBindings.h",
|
||||
"nsStyleStructList.h",
|
||||
],
|
||||
"files": [
|
||||
"{}/dist/include/mozilla/ServoBindings.h",
|
||||
],
|
||||
|
||||
# Types to just use from the `structs` target.
|
||||
"structs_types": [
|
||||
"nsStyleFont", "nsStyleColor", "nsStyleList", "nsStyleText",
|
||||
"nsStyleVisibility", "nsStyleUserInterface", "nsStyleTableBorder",
|
||||
"nsStyleSVG", "nsStyleVariables", "nsStyleBackground",
|
||||
"nsStylePosition", "nsStyleTextReset", "nsStyleDisplay",
|
||||
"nsStyleContent", "nsStyleUIReset", "nsStyleTable",
|
||||
"nsStyleMargin", "nsStylePadding", "nsStyleBorder",
|
||||
"nsStyleOutline", "nsStyleXUL", "nsStyleSVGReset", "nsStyleColumn",
|
||||
"nsStyleEffects", "nsStyleImage", "nsStyleGradient",
|
||||
"nsStyleCoord", "nsStyleGradientStop",
|
||||
|
||||
"SheetParsingMode", "nsMainThreadPtrHandle",
|
||||
"nsMainThreadPtrHolder", "nscolor", "nsFont", "FontFamilyList",
|
||||
"FontFamilyType", "nsIAtom",
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def platform_dependent_defines():
|
||||
ret = []
|
||||
|
||||
if os.name == "posix":
|
||||
ret.append("-DOS_POSIX=1")
|
||||
|
||||
ret.append({
|
||||
"Linux": "-DOS_LINUX=1",
|
||||
"Darwin": "-DOS_MACOSX=1",
|
||||
# TODO: Windows?
|
||||
}[platform.system()])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def extend_object(obj, other):
|
||||
if not obj or not other:
|
||||
return obj
|
||||
|
||||
if isinstance(obj, list) and isinstance(other, list):
|
||||
obj.extend(other)
|
||||
return
|
||||
|
||||
assert isinstance(obj, dict) and isinstance(other, dict)
|
||||
|
||||
for key in other.keys():
|
||||
if key in obj:
|
||||
extend_object(obj[key], other[key])
|
||||
else:
|
||||
obj[key] = copy.deepcopy(other[key])
|
||||
|
||||
|
||||
def build(objdir, target_name, kind_name=None,
|
||||
output_filename=None, bindgen=None, skip_test=False,
|
||||
verbose=False):
|
||||
assert target_name in COMPILATION_TARGETS
|
||||
|
||||
current_target = COMPILATION_TARGETS[target_name]
|
||||
if COMMON_BUILD_KEY in COMPILATION_TARGETS:
|
||||
current_target = copy.deepcopy(COMPILATION_TARGETS[COMMON_BUILD_KEY])
|
||||
extend_object(current_target, COMPILATION_TARGETS[target_name])
|
||||
|
||||
assert ((kind_name is None and "build_kinds" not in current_target) or
|
||||
(kind_name in current_target["build_kinds"]))
|
||||
|
||||
if bindgen is None:
|
||||
bindgen = "{}/rust-bindgen/target/debug/bindgen".format(TOOLS_DIR)
|
||||
|
||||
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:
|
||||
current_target = copy.deepcopy(current_target)
|
||||
extend_object(current_target, current_target["build_kinds"][kind_name])
|
||||
|
||||
print("[BINDGEN] {}::{} in \"{}\"... ".format(target_name, kind_name, objdir), end='')
|
||||
sys.stdout.flush()
|
||||
|
||||
flags = []
|
||||
flags.extend(platform_dependent_defines())
|
||||
|
||||
if "flags" in current_target:
|
||||
flags.extend(current_target["flags"])
|
||||
|
||||
if "raw_lines" in current_target:
|
||||
for raw_line in current_target["raw_lines"]:
|
||||
flags.append("-raw-line")
|
||||
flags.append(raw_line)
|
||||
|
||||
if "search_dirs" in current_target:
|
||||
for dir_name in current_target["search_dirs"]:
|
||||
flags.append("-I")
|
||||
flags.append(dir_name.format(objdir))
|
||||
|
||||
if "includes" in current_target:
|
||||
for file_name in current_target["includes"]:
|
||||
flags.append("-include")
|
||||
flags.append(file_name.format(objdir))
|
||||
|
||||
if "match_headers" in current_target:
|
||||
for header in current_target["match_headers"]:
|
||||
flags.append("-match")
|
||||
flags.append(header.format(objdir))
|
||||
|
||||
if "blacklist" in current_target:
|
||||
for ty in current_target["blacklist"]:
|
||||
flags.append("-blacklist-type")
|
||||
flags.append(ty)
|
||||
|
||||
if "opaque_types" in current_target:
|
||||
for ty in current_target["opaque_types"]:
|
||||
flags.append("-opaque-type")
|
||||
flags.append(ty)
|
||||
|
||||
if "structs_types" in current_target:
|
||||
for ty in current_target["structs_types"]:
|
||||
flags.append("-blacklist-type")
|
||||
flags.append(ty)
|
||||
flags.append("-raw-line")
|
||||
flags.append("use structs::{};".format(ty))
|
||||
# TODO: this is hacky, figure out a better way to do it without
|
||||
# hardcoding everything...
|
||||
if ty.startswith("nsStyle"):
|
||||
flags.extend([
|
||||
"-raw-line",
|
||||
"unsafe impl Send for {} {{}}".format(ty),
|
||||
"-raw-line",
|
||||
"unsafe impl Sync for {} {{}}".format(ty),
|
||||
"-raw-line",
|
||||
"impl HeapSizeOf for {} {{ fn heap_size_of_children(&self) -> usize {{ 0 }} }}".format(ty)
|
||||
])
|
||||
|
||||
flags.append("-o")
|
||||
flags.append(output_filename)
|
||||
|
||||
# TODO: support more files, that's the whole point of this.
|
||||
assert len(current_target["files"]) == 1
|
||||
flags.append(current_target["files"][0].format(objdir))
|
||||
|
||||
flags.insert(0, bindgen)
|
||||
output = None
|
||||
try:
|
||||
output = subprocess.check_output(flags, stderr=subprocess.STDOUT)
|
||||
output = output.decode('utf8')
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("FAIL\n", e.output.decode('utf8'))
|
||||
return 1
|
||||
|
||||
print("OK")
|
||||
|
||||
if verbose:
|
||||
print(output)
|
||||
|
||||
if current_target.get("test", False) and not skip_test:
|
||||
print("[RUSTC]... ", end='')
|
||||
sys.stdout.flush()
|
||||
|
||||
tests_file = tempfile.NamedTemporaryFile()
|
||||
output = None
|
||||
try:
|
||||
rustc_command = ["rustc", output_filename, "--test", "-o", tests_file.name]
|
||||
output = subprocess.check_output(rustc_command, stderr=subprocess.STDOUT)
|
||||
output = output.decode('utf8')
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("FAIL\n", e.output.decode('utf8'))
|
||||
return 1
|
||||
|
||||
print("OK")
|
||||
|
||||
if verbose:
|
||||
print(output)
|
||||
|
||||
tests_file.file.close()
|
||||
print("[RUSTC_TEST]... ", end='')
|
||||
sys.stdout.flush()
|
||||
|
||||
try:
|
||||
output = subprocess.check_output([tests_file.name], stderr=subprocess.STDOUT)
|
||||
output = output.decode('utf8')
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("tests failed: ", e.output.decode('utf8'))
|
||||
return 1
|
||||
|
||||
print("OK")
|
||||
|
||||
# TODO: this -3 is hacky as heck
|
||||
print(output.split('\n')[-3])
|
||||
|
||||
if verbose:
|
||||
print(output)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def builds_for(target_name, kind):
|
||||
if target_name == "all":
|
||||
for target in COMPILATION_TARGETS.keys():
|
||||
if target == COMMON_BUILD_KEY:
|
||||
continue
|
||||
|
||||
if "build_kinds" in COMPILATION_TARGETS[target]:
|
||||
for kind in COMPILATION_TARGETS[target]["build_kinds"].keys():
|
||||
yield (target, kind)
|
||||
else:
|
||||
yield (target, None)
|
||||
return
|
||||
|
||||
target = COMPILATION_TARGETS[target_name]
|
||||
if "build_kinds" in target:
|
||||
if kind is None:
|
||||
for kind in target["build_kinds"].keys():
|
||||
yield(target_name, kind)
|
||||
else:
|
||||
yield (target_name, kind)
|
||||
return
|
||||
|
||||
yield (target_name, None)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=DESCRIPTION)
|
||||
parser.add_argument('--target',
|
||||
help='The target to build, either "structs" or "bindings"')
|
||||
parser.add_argument('--kind',
|
||||
help='Kind of build')
|
||||
parser.add_argument('--bindgen',
|
||||
help='Override bindgen binary')
|
||||
parser.add_argument('--output', '-o',
|
||||
help='Output of the script')
|
||||
parser.add_argument('--skip-test',
|
||||
action='store_true',
|
||||
help='Skip automatic tests, useful for debugging')
|
||||
parser.add_argument('--verbose', '-v',
|
||||
action='store_true',
|
||||
help='Be... verbose')
|
||||
parser.add_argument('objdir')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isdir(args.objdir):
|
||||
print("\"{}\" doesn't seem to be a directory".format(args.objdir))
|
||||
return 1
|
||||
|
||||
if args.target != COMMON_BUILD_KEY and args.target != "all" and args.target not in COMPILATION_TARGETS:
|
||||
print("{} is not a valid compilation target.".format(args.target))
|
||||
print("Valid compilation targets are:")
|
||||
for target in COMPILATION_TARGETS.keys():
|
||||
if target != COMMON_BUILD_KEY:
|
||||
print("\t * {}".format(target))
|
||||
return 1
|
||||
|
||||
current_target = COMPILATION_TARGETS.get(args.target, {})
|
||||
if args.kind and "build_kinds" in current_target and args.kind not in current_target["build_kinds"]:
|
||||
print("{} is not a valid build kind.".format(args.kind))
|
||||
print("Valid build kinds are:")
|
||||
for kind in current_target["build_kinds"].keys():
|
||||
print("\t * {}".format(kind))
|
||||
return 1
|
||||
|
||||
for target, kind in builds_for(args.target, args.kind):
|
||||
ret = build(args.objdir, target, kind,
|
||||
bindgen=args.bindgen, skip_test=args.skip_test,
|
||||
output_filename=args.output,
|
||||
verbose=args.verbose)
|
||||
if ret != 0:
|
||||
print("{}::{} failed".format(target, kind))
|
||||
return ret
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
26
ports/geckolib/gecko_bindings/tools/regen.sh
Executable file
26
ports/geckolib/gecko_bindings/tools/regen.sh
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 /path/to/gecko/objdir [other-regen.py-flags]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for rust-bindgen
|
||||
if [ ! -d rust-bindgen ]; then
|
||||
echo "rust-bindgen not found. Run setup_bindgen.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for /usr/include
|
||||
if [ ! -d /usr/include ]; then
|
||||
echo "/usr/include doesn't exist. Mac users may need to run xcode-select --install."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(uname)" == "Linux" ]; then
|
||||
LIBCLANG_PATH=/usr/lib/llvm-3.8/lib;
|
||||
else
|
||||
LIBCLANG_PATH=`brew --prefix llvm38`/lib/llvm-3.8/lib;
|
||||
fi
|
||||
|
||||
./regen.py --target all "$@"
|
|
@ -1,94 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Run in the tools directory.
|
||||
cd "$(dirname $0)"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 /path/to/gecko/objdir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for rust-bindgen
|
||||
if [ ! -d rust-bindgen ]; then
|
||||
echo "rust-bindgen not found. Run setup_bindgen.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for /usr/include
|
||||
if [ ! -d /usr/include ]; then
|
||||
echo "/usr/include doesn't exist. Mac users may need to run xcode-select --install."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(uname)" == "Linux" ]; then
|
||||
PLATFORM_DEPENDENT_DEFINES+="-DOS_LINUX";
|
||||
LIBCLANG_PATH=/usr/lib/llvm-3.8/lib;
|
||||
else
|
||||
PLATFORM_DEPENDENT_DEFINES+="-DOS_MACOSX";
|
||||
LIBCLANG_PATH=`brew --prefix llvm38`/lib/llvm-3.8/lib;
|
||||
fi
|
||||
|
||||
# Prevent bindgen from generating opaque types for common gecko types.
|
||||
export MAP_GECKO_TYPES=""
|
||||
|
||||
# Extra code we want to generate.
|
||||
export EXTRA_CODE="-raw-line 'use heapsize::HeapSizeOf;' "
|
||||
|
||||
# Style structs.
|
||||
for STRUCT in nsStyleFont nsStyleColor nsStyleList nsStyleText \
|
||||
nsStyleVisibility nsStyleUserInterface nsStyleTableBorder \
|
||||
nsStyleSVG nsStyleVariables nsStyleBackground nsStylePosition \
|
||||
nsStyleTextReset nsStyleDisplay nsStyleContent nsStyleUIReset \
|
||||
nsStyleTable nsStyleMargin nsStylePadding nsStyleBorder \
|
||||
nsStyleOutline nsStyleXUL nsStyleSVGReset nsStyleColumn nsStyleEffects \
|
||||
nsStyleImage nsStyleGradient nsStyleCoord nsStyleGradientStop
|
||||
do
|
||||
MAP_GECKO_TYPES=$MAP_GECKO_TYPES"-blacklist-type $STRUCT "
|
||||
MAP_GECKO_TYPES=$MAP_GECKO_TYPES"-raw-line 'use structs::$STRUCT;' "
|
||||
EXTRA_CODE=$EXTRA_CODE"-raw-line 'unsafe impl Send for $STRUCT {}' "
|
||||
EXTRA_CODE=$EXTRA_CODE"-raw-line 'unsafe impl Sync for $STRUCT {}' "
|
||||
EXTRA_CODE=$EXTRA_CODE"-raw-line 'impl HeapSizeOf for $STRUCT { fn heap_size_of_children(&self) -> usize { 0 } }' "
|
||||
done
|
||||
|
||||
# Other mapped types.
|
||||
for TYPE in SheetParsingMode nsMainThreadPtrHandle nsMainThreadPtrHolder nscolor nsFont \
|
||||
FontFamilyList FontFamilyType nsIAtom
|
||||
do
|
||||
MAP_GECKO_TYPES=$MAP_GECKO_TYPES"-blacklist-type $TYPE "
|
||||
MAP_GECKO_TYPES=$MAP_GECKO_TYPES"-raw-line 'use structs::$TYPE;' "
|
||||
done
|
||||
|
||||
|
||||
|
||||
# Check for the include directory.
|
||||
export OBJDIR="$1"
|
||||
export SRCDIR="$1/.." # Not necessarily true, but let's assume.
|
||||
export DIST_INCLUDE="$1/dist/include"
|
||||
if [ ! -d "$DIST_INCLUDE" ]; then
|
||||
echo "$DIST_INCLUDE: directory not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# We need to use 'eval' here to make MAP_GECKO_TYPES evaluate properly as
|
||||
# multiple arguments.
|
||||
eval ./rust-bindgen/target/debug/bindgen \
|
||||
-x c++ -std=gnu++0x \
|
||||
"-I$DIST_INCLUDE" \
|
||||
"-I$DIST_INCLUDE/nspr/" \
|
||||
"-I$1/nsprpub/pr/include/" \
|
||||
$PLATFORM_DEPENDENT_DEFINES \
|
||||
-DMOZILLA_INTERNAL_API \
|
||||
-DMOZ_STYLO_BINDINGS=1 \
|
||||
-DJS_DEBUG=1 \
|
||||
-DDEBUG=1 -DTRACING=1 -DOS_POSIX=1 \
|
||||
-DIMPL_LIBXUL \
|
||||
-o ../bindings.rs \
|
||||
-no-type-renaming \
|
||||
-include "$1/mozilla-config.h" \
|
||||
"$DIST_INCLUDE/mozilla/ServoBindings.h" \
|
||||
-match "ServoBindings.h" \
|
||||
-match "nsStyleStructList.h" \
|
||||
$MAP_GECKO_TYPES \
|
||||
$EXTRA_CODE
|
|
@ -1,148 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Run in the tools directory.
|
||||
cd "$(dirname $0)"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 /path/to/gecko/objdir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for rust-bindgen
|
||||
if [ ! -d rust-bindgen ]; then
|
||||
echo "rust-bindgen not found. Run setup_bindgen.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for /usr/include
|
||||
if [ ! -d /usr/include ]; then
|
||||
echo "/usr/include doesn't exist. Mac users may need to run xcode-select --install."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(uname)" == "Linux" ]; then
|
||||
PLATFORM_DEPENDENT_DEFINES+="-DOS_LINUX";
|
||||
LIBCLANG_PATH=/usr/lib/llvm-3.8/lib;
|
||||
else
|
||||
PLATFORM_DEPENDENT_DEFINES+="-DOS_MACOSX";
|
||||
LIBCLANG_PATH=`brew --prefix llvm38`/lib/llvm-3.8/lib;
|
||||
fi
|
||||
|
||||
|
||||
# Check for the include directory.
|
||||
export DIST_INCLUDE="$1/dist/include"
|
||||
if [ ! -d "$DIST_INCLUDE" ]; then
|
||||
echo "$DIST_INCLUDE: directory not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
for target in debug release; do
|
||||
./rust-bindgen/target/debug/bindgen \
|
||||
-o ../structs_${target}.rs \
|
||||
-x c++ -std=gnu++0x \
|
||||
-allow-unknown-types \
|
||||
-no-bitfield-methods \
|
||||
"-I$DIST_INCLUDE" "-I$DIST_INCLUDE/nspr" \
|
||||
"-I$1/../nsprpub/pr/include" \
|
||||
$PLATFORM_DEPENDENT_DEFINES \
|
||||
-ignore-functions \
|
||||
-no-type-renaming \
|
||||
-DMOZILLA_INTERNAL_API \
|
||||
-DMOZ_STYLO_BINDINGS=1 \
|
||||
`[ "$target" = debug ] && echo "-DDEBUG=1 -DJS_DEBUG=1"` \
|
||||
-DTRACING=1 -DOS_POSIX=1 \
|
||||
-DIMPL_LIBXUL \
|
||||
-include "nsThemeConstants.h" \
|
||||
-match "RefCountType.h" \
|
||||
-match "nscore.h" \
|
||||
-match "nsError.h" \
|
||||
-match "nsID.h" \
|
||||
-match "nsString" \
|
||||
-match "nsAString" \
|
||||
-match "nsSubstring" \
|
||||
-match "nsTSubstring" \
|
||||
-match "nsTString" \
|
||||
-match "nsISupportsBase.h" \
|
||||
-match "nsCOMPtr.h" \
|
||||
-match "nsIAtom.h" \
|
||||
-match "nsIURI.h" \
|
||||
-match "nsAutoPtr.h" \
|
||||
-match "nsColor.h" \
|
||||
-match "nsCoord.h" \
|
||||
-match "nsPoint.h" \
|
||||
-match "nsRect.h" \
|
||||
-match "nsMargin.h" \
|
||||
-match "nsThemeConstants.h" \
|
||||
-match "nsCSSProperty.h" \
|
||||
-match "CSSVariableValues.h" \
|
||||
-match "nsFont.h" \
|
||||
-match "nsTHashtable.h" \
|
||||
-match "PLDHashTable.h" \
|
||||
-match "nsColor.h" \
|
||||
-match "nsStyleStruct.h" \
|
||||
-match "nsStyleCoord.h" \
|
||||
-match "RefPtr.h" \
|
||||
-match "nsISupportsImpl.h" \
|
||||
-match "gfxFontConstants.h" \
|
||||
-match "gfxFontFamilyList.h" \
|
||||
-match "gfxFontFeatures.h" \
|
||||
-match "imgRequestProxy.h" \
|
||||
-match "nsIRequest.h" \
|
||||
-match "imgIRequest.h" \
|
||||
-match "CounterStyleManager.h" \
|
||||
-match "nsStyleConsts.h" \
|
||||
-match "nsCSSValue.h" \
|
||||
-match "SheetType.h" \
|
||||
-match "nsIPrincipal.h" \
|
||||
-match "nsDataHashtable.h" \
|
||||
-match "nsCSSScanner.h" \
|
||||
-match "Types.h" \
|
||||
-match "utility" \
|
||||
-match "nsTArray" \
|
||||
-match "pair" \
|
||||
-match "SheetParsingMode.h" \
|
||||
-match "StaticPtr.h" \
|
||||
-match "nsProxyRelease.h" \
|
||||
-blacklist-type "IsDestructibleFallbackImpl" \
|
||||
-blacklist-type "IsDestructibleFallback" \
|
||||
-blacklist-type "nsProxyReleaseEvent" \
|
||||
-blacklist-type "FallibleTArray" \
|
||||
-blacklist-type "nsTArray_Impl" \
|
||||
-blacklist-type "__is_tuple_like_impl" \
|
||||
-opaque-type "nsIntMargin" \
|
||||
-opaque-type "nsIntPoint" \
|
||||
-opaque-type "nsIntRect" \
|
||||
-opaque-type "nsCOMArray" \
|
||||
-opaque-type "nsDependentString" \
|
||||
-opaque-type "EntryStore" \
|
||||
-opaque-type "gfxFontFeatureValueSet" \
|
||||
-opaque-type "imgRequestProxy" \
|
||||
-opaque-type "imgRequestProxyStatic" \
|
||||
-opaque-type "CounterStyleManager" \
|
||||
-opaque-type "ImageValue" \
|
||||
-opaque-type "URLValue" \
|
||||
-opaque-type "URLValueData" \
|
||||
-opaque-type "nsIPrincipal" \
|
||||
-opaque-type "nsDataHashtable" \
|
||||
-opaque-type "imgIRequest" \
|
||||
-include "$1/mozilla-config.h" \
|
||||
"$DIST_INCLUDE/nsStyleStruct.h"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[91mwarning:\e[0m bindgen exited with nonzero exit status"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "\e[34minfo:\e[0m bindgen exited successfully, running tests"
|
||||
TESTS_FILE=$(mktemp)
|
||||
TESTS_SRC=$(mktemp)
|
||||
for target in debug release; do
|
||||
echo "#![feature(const_fn)]" > $TESTS_SRC
|
||||
cat ../structs_${target}.rs >> $TESTS_SRC
|
||||
multirust run nightly rustc $TESTS_SRC --test -o $TESTS_FILE
|
||||
$TESTS_FILE
|
||||
done
|
||||
rm $TESTS_FILE
|
||||
rm $TESTS_SRC
|
|
@ -70,8 +70,6 @@ IGNORED_DIRS = [
|
|||
# Generated and upstream code combined with our own. Could use cleanup
|
||||
os.path.join(".", "target"),
|
||||
os.path.join(".", "ports", "cef"),
|
||||
# Tooling, generated locally from external repos.
|
||||
os.path.join(".", "ports", "geckolib", "gecko_bindings", "tools"),
|
||||
# Hidden directories
|
||||
os.path.join(".", "."),
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue