Generate apis.html and css-properties.json for docs as part of crates’ build scripts

… rather than as an extra step after `cargo doc`.
This helps always using the correct set of CSS properties
(for layout 2013 v.s. 2020).
This commit is contained in:
Simon Sapin 2019-07-29 18:57:20 +02:00
parent ddb4e369dd
commit 0215d09ccb
8 changed files with 67 additions and 173 deletions

View file

@ -41,7 +41,6 @@ set(globalgen_deps
${bindings_src}/Configuration.py ${bindings_src}/Configuration.py
${bindings_src}/CodegenRust.py ${bindings_src}/CodegenRust.py
${bindings_src}/parser/WebIDL.py ${bindings_src}/parser/WebIDL.py
${PROJECT_BINARY_DIR}/css_properties.json
) )
set(bindinggen_deps set(bindinggen_deps
${globalgen_deps} ${globalgen_deps}
@ -69,28 +68,12 @@ add_custom_command(
${bindings_src}/Bindings.conf ${bindings_src}/Bindings.conf
. .
${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}/css_properties.json ${PROJECT_BINARY_DIR}/../css-properties.json
DEPENDS Bindings _cache ${globalgen_deps} ${webidls} ${PROJECT_SOURCE_DIR}/../../target/doc/servo
DEPENDS Bindings _cache ${globalgen_deps} ${webidls} ${PROJECT_BINARY_DIR}/../css-properties.json
VERBATIM VERBATIM
) )
add_custom_command(
OUTPUT apis.html
COMMAND ${PYTHON_EXECUTABLE} -B ${bindings_src}/pythonpath.py -I ${bindings_src}/parser -I ${bindings_src}/ply
${bindings_src}/GlobalGen.py
--cachedir=_cache
--filelist=webidls.list
--only-html
${bindings_src}/Bindings.conf
.
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR}/css_properties.json
DEPENDS _cache ${globalgen_deps} ${webidls}
VERBATIM
)
add_custom_target(supported-apis DEPENDS apis.html)
# We need an intermediate custom target for this, due to this misfeature: # We need an intermediate custom target for this, due to this misfeature:
# > If any dependency is an OUTPUT of another custom command in the same # > If any dependency is an OUTPUT of another custom command in the same
# > directory CMake automatically brings the other custom command into the # > directory CMake automatically brings the other custom command into the

View file

@ -17,10 +17,8 @@ fn main() {
let style_out_dir = PathBuf::from(env::var_os("DEP_SERVO_STYLE_CRATE_OUT_DIR").unwrap()); let style_out_dir = PathBuf::from(env::var_os("DEP_SERVO_STYLE_CRATE_OUT_DIR").unwrap());
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let build_dir = out_dir.join("build"); let json = "css-properties.json";
let json = "css_properties.json"; std::fs::copy(style_out_dir.join(json), out_dir.join(json)).unwrap();
let _ = std::fs::create_dir(&build_dir); // Ignore errors: they most likely indicate it already exists
std::fs::copy(style_out_dir.join(json), build_dir.join(json)).unwrap();
// This must use the Ninja generator -- it's the only one that // This must use the Ninja generator -- it's the only one that
// parallelizes cmake's output properly. (Cmake generates // parallelizes cmake's output properly. (Cmake generates

View file

@ -7550,7 +7550,7 @@ impl %(base)s {
def SupportedDomApis(config): def SupportedDomApis(config):
descriptors = config.getDescriptors(isExposedConditionally=False) descriptors = config.getDescriptors(isExposedConditionally=False)
base_path = os.path.join('dom', 'bindings', 'codegen') base_path = os.path.dirname(__file__)
with open(os.path.join(base_path, 'apis.html.template')) as f: with open(os.path.join(base_path, 'apis.html.template')) as f:
base_template = f.read() base_template = f.read()
with open(os.path.join(base_path, 'api.html.template')) as f: with open(os.path.join(base_path, 'api.html.template')) as f:

View file

@ -29,12 +29,10 @@ def generate_file(config, name, filename):
def main(): def main():
# Parse arguments. # Parse arguments.
from optparse import OptionParser from optparse import OptionParser
usageString = "usage: %prog [options] configFile outputdir webidldir cssProperties.json [files]" usageString = "usage: %prog [options] configFile outputdir webidldir cssProperties.json docServoDir [files]"
o = OptionParser(usage=usageString) o = OptionParser(usage=usageString)
o.add_option("--cachedir", dest='cachedir', default=None, o.add_option("--cachedir", dest='cachedir', default=None,
help="Directory in which to cache lex/parse tables.") help="Directory in which to cache lex/parse tables.")
o.add_option("--only-html", dest='only_html', action="store_true",
help="Only generate HTML from WebIDL inputs")
o.add_option("--filelist", dest='filelist', default=None, o.add_option("--filelist", dest='filelist', default=None,
help="A file containing the list (one per line) of webidl files to process.") help="A file containing the list (one per line) of webidl files to process.")
(options, args) = o.parse_args() (options, args) = o.parse_args()
@ -46,6 +44,7 @@ def main():
outputdir = args[1] outputdir = args[1]
baseDir = args[2] baseDir = args[2]
css_properties_json = args[3] css_properties_json = args[3]
doc_servo = args[4]
if options.filelist is not None: if options.filelist is not None:
fileList = [l.strip() for l in open(options.filelist).xreadlines()] fileList = [l.strip() for l in open(options.filelist).xreadlines()]
else: else:
@ -63,7 +62,6 @@ def main():
parserResults = parser.finish() parserResults = parser.finish()
if not options.only_html:
# Write the parser results out to a pickle. # Write the parser results out to a pickle.
resultsPath = os.path.join(outputdir, 'ParserResults.pkl') resultsPath = os.path.join(outputdir, 'ParserResults.pkl')
with open(resultsPath, 'wb') as resultsFile: with open(resultsPath, 'wb') as resultsFile:
@ -72,11 +70,6 @@ def main():
# Load the configuration. # Load the configuration.
config = Configuration(configFile, parserResults) config = Configuration(configFile, parserResults)
to_generate = [
('SupportedDomApis', 'apis.html'),
]
if not options.only_html:
to_generate = [ to_generate = [
('PrototypeList', 'PrototypeList.rs'), ('PrototypeList', 'PrototypeList.rs'),
('RegisterBindings', 'RegisterBindings.rs'), ('RegisterBindings', 'RegisterBindings.rs'),
@ -91,6 +84,8 @@ def main():
for name, filename in to_generate: for name, filename in to_generate:
generate_file(config, name, os.path.join(outputdir, filename)) generate_file(config, name, os.path.join(outputdir, filename))
generate_file(config, 'SupportedDomApis', os.path.join(doc_servo, 'apis.html'))
def add_css_properties_attributes(webidl_files, css_properties_json, parser): def add_css_properties_attributes(webidl_files, css_properties_json, parser):
for filename in webidl_files: for filename in webidl_files:
@ -102,10 +97,11 @@ def add_css_properties_attributes(webidl_files, css_properties_json, parser):
css_properties = json.load(open(css_properties_json, "rb")) css_properties = json.load(open(css_properties_json, "rb"))
idl = "partial interface CSSStyleDeclaration {\n%s\n};\n" % "\n".join( idl = "partial interface CSSStyleDeclaration {\n%s\n};\n" % "\n".join(
" [%sCEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString %s;" % ( " [%sCEReactions, SetterThrows] attribute [TreatNullAs=EmptyString] DOMString %s;" % (
('Pref="%s", ' % pref if pref else ""), ('Pref="%s", ' % data["pref"] if data["pref"] else ""),
attribute_name attribute_name
) )
for (property_name, pref) in css_properties for (kind, properties_list) in sorted(css_properties.items())
for (property_name, data) in sorted(properties_list.items())
for attribute_name in attribute_names(property_name) for attribute_name in attribute_names(property_name)
) )
parser.parse(idl.encode("utf-8"), "CSSStyleDeclaration_generated.webidl") parser.parse(idl.encode("utf-8"), "CSSStyleDeclaration_generated.webidl")

View file

@ -101,21 +101,32 @@ def main():
pref_attr = "servo_2013_pref" pref_attr = "servo_2013_pref"
if engine == "servo-2020": if engine == "servo-2020":
pref_attr = "servo_2020_pref" pref_attr = "servo_2020_pref"
names_and_prefs = [ properties_dict = {
(prop.name, getattr(prop, pref_attr)) kind: {
for p in properties.longhands + properties.shorthands p.name: {
if p.enabled_in_content() "pref": getattr(p, pref_attr)
for prop in [p] + p.alias }
for prop in properties_list
if prop.enabled_in_content()
for p in [prop] + prop.alias
}
for kind, properties_list in [
("longhands", properties.longhands),
("shorthands", properties.shorthands)
] ]
write(OUT_DIR, "css_properties.json", json.dumps(names_and_prefs, indent=4)) }
as_html = render(os.path.join(BASE, "properties.html.mako"), properties=properties_dict)
as_json = json.dumps(properties_dict, indent=4, sort_keys=True)
doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
write(doc_servo, "css-properties.html", as_html)
write(doc_servo, "css-properties.json", as_json)
write(OUT_DIR, "css-properties.json", as_json)
elif output == "geckolib": elif output == "geckolib":
if len(sys.argv) < 4: if len(sys.argv) < 4:
abort(usage) abort(usage)
template = sys.argv[3] template = sys.argv[3]
header = render(template, data=properties) header = render(template, data=properties)
sys.stdout.write(header) sys.stdout.write(header)
elif output == "html":
write_html(properties)
def abort(message): def abort(message):
@ -153,19 +164,5 @@ def write(directory, filename, content):
abort("Found \"{}\" in {} ({})".format(python_addr.group(0), filename, full_path)) abort("Found \"{}\" in {} ({})".format(python_addr.group(0), filename, full_path))
def write_html(properties):
properties = dict(
(p.name, {
"flag": p.servo_2013_pref,
"shorthand": hasattr(p, "sub_properties")
})
for p in properties.longhands + properties.shorthands
)
doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
html = render(os.path.join(BASE, "properties.html.mako"), properties=properties)
write(doc_servo, "css-properties.html", html)
write(doc_servo, "css-properties.json", json.dumps(properties, indent=4))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -3,38 +3,29 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc"> <title>Supported CSS properties in Servo</title>
<meta name="description" content="API documentation for the Rust `servo` crate."> <link rel="stylesheet" type="text/css" href="../normalize.css">
<meta name="keywords" content="rust, rustlang, rust-lang, servo">
<title>Supported CSS properties - servo - Rust</title>
<link rel="stylesheet" type="text/css" href="../rustdoc.css"> <link rel="stylesheet" type="text/css" href="../rustdoc.css">
<link rel="stylesheet" type="text/css" href="../main.css"> <link rel="stylesheet" type="text/css" href="../light.css">
</head> </head>
<body class="rustdoc"> <body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<section id='main' class="content mod"> <section id='main' class="content mod">
<h1 class='fqn'><span class='in-band'>CSS properties currently supported in <a class='mod' href=''>Servo</a></span></h1> <h1 class='fqn'><span class='in-band'>CSS properties currently supported in Servo</span></h1>
<div id='properties' class='docblock'> % for kind, props in sorted(properties.items()):
<h2>${kind.capitalize()}</h2>
<table> <table>
<tr> <tr>
<th>Property</th> <th>Name</th>
<th>Flag</th> <th>Pref</th>
<th>Shorthand</th>
</tr> </tr>
% for prop in properties: % for name, data in sorted(props.items()):
<tr> <tr>
<td>${prop}</td> <td><code>${name}</code></td>
<td>${properties[prop]['flag']}</td> <td><code>${data['pref'] or ''}</code></td>
<td>${properties[prop]['shorthand']}</td>
</tr> </tr>
% endfor % endfor
</table> </table>
</div> % endfor
</section> </section>
</body> </body>
</html> </html>

View file

@ -13,7 +13,6 @@ import json
import os import os
import os.path as path import os.path as path
import subprocess import subprocess
import sys
from shutil import copytree, rmtree, copy2 from shutil import copytree, rmtree, copy2
from mach.decorators import ( from mach.decorators import (
@ -274,18 +273,6 @@ class PostBuildCommands(CommandBase):
for name in os.listdir(static): for name in os.listdir(static):
copy2(path.join(static, name), path.join(docs, name)) copy2(path.join(static, name), path.join(docs, name))
build = path.join(self.context.topdir, "components", "style", "properties", "build.py")
if "layout-2020" in features:
engine = "servo-2020"
if "layout-2013" in features:
engine = "servo-2013"
subprocess.check_call([sys.executable, build, engine, "html"])
script = path.join(self.context.topdir, "components", "script")
subprocess.check_call(["cmake", "."], cwd=script)
subprocess.check_call(["cmake", "--build", ".", "--target", "supported-apis"], cwd=script)
copy2(path.join(script, "apis.html"), path.join(docs, "servo", "apis.html"))
@Command('browse-doc', @Command('browse-doc',
description='Generate documentation and open it in a web browser', description='Generate documentation and open it in a web browser',
category='post-build') category='post-build')

View file

@ -4,9 +4,8 @@
use serde_json::{self, Value}; use serde_json::{self, Value};
use std::env; use std::env;
use std::fs::{remove_file, File}; use std::fs::File;
use std::path::Path; use std::path::Path;
use std::process::Command;
#[test] #[test]
fn properties_list_json() { fn properties_list_json() {
@ -19,67 +18,10 @@ fn properties_list_json() {
.join("doc") .join("doc")
.join("servo") .join("servo")
.join("css-properties.json"); .join("css-properties.json");
if json.exists() {
remove_file(&json).unwrap()
}
let python = env::var("PYTHON").ok().unwrap_or_else(find_python);
let script = top
.join("components")
.join("style")
.join("properties")
.join("build.py");
let status = Command::new(python)
.arg(&script)
.arg("servo-2013")
.arg("html")
.arg("regular")
.status()
.unwrap();
assert!(status.success(), "{:?}", status);
let properties: Value = serde_json::from_reader(File::open(json).unwrap()).unwrap(); let properties: Value = serde_json::from_reader(File::open(json).unwrap()).unwrap();
assert!(properties.as_object().unwrap().len() > 100); let longhands = properties["longhands"].as_object().unwrap();
assert!(properties.as_object().unwrap().contains_key("margin")); assert!(longhands.len() > 100);
assert!(properties.as_object().unwrap().contains_key("margin-top")); assert!(longhands.get("margin-top").is_some());
} assert!(properties["shorthands"].get("margin").is_some());
#[cfg(windows)]
fn find_python() -> String {
if Command::new("python2.7.exe")
.arg("--version")
.output()
.is_ok()
{
return "python2.7.exe".to_owned();
}
if Command::new("python27.exe")
.arg("--version")
.output()
.is_ok()
{
return "python27.exe".to_owned();
}
if Command::new("python.exe").arg("--version").output().is_ok() {
return "python.exe".to_owned();
}
panic!("Can't find python (tried python27.exe and python.exe)! Try fixing PATH or setting the PYTHON env var");
}
#[cfg(not(windows))]
fn find_python() -> String {
if Command::new("python2.7")
.arg("--version")
.output()
.unwrap()
.status
.success()
{
"python2.7"
} else {
"python"
}
.to_owned()
} }