Skip to content

Commit

Permalink
docs: Generate validation issue tag documentation from code
Browse files Browse the repository at this point in the history
  • Loading branch information
ximion committed Jan 23, 2024
1 parent 4d97479 commit 5c74769
Show file tree
Hide file tree
Showing 8 changed files with 412 additions and 80 deletions.
35 changes: 28 additions & 7 deletions docs/doc-build-helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@
EXTRA_CSS = [['/usr/share/javascript/highlight.js/styles/routeros.css', 'highlight.css']]


def daps_build(src_dir, project_name, daps_exe):
def daps_build(src_dir, project_name, daps_exe, valsec_gen):
print('Creating HTML with DAPS...')
sys.stdout.flush()

if valsec_gen:
ret = subprocess.call([valsec_gen, src_dir], cwd=src_dir)
if ret != 0:
print('Failed to generated some documentation sections.')
sys.exit(6)

build_dir = os.path.join(src_dir, '_docbuild')
cmd = [daps_exe, 'html', '--clean']
if project_name:
Expand Down Expand Up @@ -77,15 +83,29 @@ def copy_result(build_dir, project_name, dest_dir):
)


def cleanup_build_dir(build_dir):
def cleanup_workspace(src_dir, build_dir):
print('Cleaning up.')
assert src_dir != build_dir

try:
os.remove(os.path.join(src_dir, 'xml', 'validator-compose-hints.xml'))
os.remove(os.path.join(src_dir, 'xml', 'validator-issues.xml'))
except OSError:
pass

if os.path.exists(build_dir):
shutil.rmtree(build_dir)


def daps_validate(src_dir, daps_exe):
def daps_validate(src_dir, daps_exe, valsec_gen):
print('Validating documentation with DAPS...')

if valsec_gen:
ret = subprocess.call([valsec_gen, src_dir], cwd=src_dir)
if ret != 0:
print('Failed to generated some documentation sections.')
sys.exit(6)

build_dir = os.path.join(src_dir, '_docbuild')
if os.path.exists(build_dir):
shutil.rmtree(build_dir)
Expand All @@ -94,7 +114,7 @@ def daps_validate(src_dir, daps_exe):
ret = subprocess.call([daps_exe, 'validate'], cwd=src_dir)
if ret != 0:
print('Validation failed!')
cleanup_build_dir(build_dir)
cleanup_workspace(src_dir, build_dir)
return ret == 0


Expand All @@ -103,6 +123,7 @@ def main(args):
parser.add_argument('--build', action='store_true')
parser.add_argument('--validate', action='store_true')

parser.add_argument('--valsec-gen', action='store')
parser.add_argument('--src', action='store')
parser.add_argument('--builddir', action='store')
parser.add_argument('--daps', action='store', default='daps')
Expand All @@ -122,13 +143,13 @@ def main(args):

if options.build:
# build the HTML files
build_dir = daps_build(options.src, options.project, options.daps)
build_dir = daps_build(options.src, options.project, options.daps, options.valsec_gen)

# copy to output HTML folder, overriding all previous contents
copy_result(build_dir, options.project, os.path.join(options.src, 'html'))

# remove temporary directory
cleanup_build_dir(build_dir)
cleanup_workspace(options.src, build_dir)

# make a dummy file so Meson can rebuild documentation on demand
if options.builddir:
Expand All @@ -138,7 +159,7 @@ def main(args):

elif options.validate:
# validate the XML
ret = daps_validate(options.src, options.daps)
ret = daps_validate(options.src, options.daps, options.valsec_gen)
if not ret:
sys.exit(6)

Expand Down
171 changes: 171 additions & 0 deletions docs/gen-valtag-sections.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2019-2024 Matthias Klumpp <[email protected]>
*
* Licensed under the GNU Lesser General Public License Version 2.1
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the license, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"

#include <appstream.h>

#include "as-validator-issue-tag.h"
#include "asc-hint-tags.h"

static GString *
load_doc_template (const gchar *dir_path, const gchar *tmpl_name)
{
g_autofree gchar *path = NULL;
g_autofree gchar *contents = NULL;
g_autoptr(GError) error = NULL;

path = g_build_filename (dir_path, tmpl_name, NULL);

if (!g_file_get_contents (path, &contents, NULL, &error)) {
g_error ("Failed to load template file '%s': %s", tmpl_name, error->message);
return NULL;
}

return g_string_new (contents);
}

static gchar *
make_valtag_entry (const gchar *ns_prefix,
const gchar *tag,
AsIssueSeverity severity,
const gchar *explanation)
{
GString *entry = NULL;
g_autofree gchar *explanation_xml = NULL;

const gchar *entry_tmpl =
" <varlistentry id=\"{{prefix}}-{{tag}}\">\n"
" <term>{{tag}}</term>\n"
" <listitem>\n"
" <para>Severity: <emphasis>{{severity}}</emphasis></para>\n"
" <para>\n"
" {{explanation}}\n"
" </para>\n"
" </listitem>\n"
" </varlistentry>";

entry = g_string_new (entry_tmpl);

as_gstring_replace (entry, "{{prefix}}", ns_prefix, -1);
as_gstring_replace (entry, "{{tag}}", tag, -1);
as_gstring_replace (entry, "{{severity}}", as_issue_severity_to_string (severity), -1);
explanation_xml = g_markup_escape_text (explanation, -1);
as_gstring_replace (entry, "{{explanation}}", explanation_xml, -1);

return g_string_free (entry, FALSE);
}

static gboolean
process_validator_tag_lists (const gchar *work_dir)
{
g_autoptr(GString) val_contents = NULL;
g_autoptr(GString) coval_contents = NULL;
g_autoptr(GString) valtags = NULL;
g_autofree gchar *val_fname = NULL;
g_autofree gchar *coval_fname = NULL;
g_autoptr(GError) error = NULL;

val_contents = load_doc_template (work_dir, "validator-issues.xml.tmpl");
coval_contents = load_doc_template (work_dir, "validator-compose-hints.xml.tmpl");

/* process validator hint tags */
valtags = g_string_new ("");
for (guint i = 0; as_validator_issue_tag_list[i].tag != NULL; i++) {
g_autofree gchar *entry_str = NULL;

entry_str = make_valtag_entry ("asv",
as_validator_issue_tag_list[i].tag,
as_validator_issue_tag_list[i].severity,
as_validator_issue_tag_list[i].explanation);
g_string_append_printf (valtags, "\n%s\n", entry_str);
}
as_gstring_replace (val_contents, "{{issue_list}}", valtags->str, -1);
g_string_truncate (valtags, 0);

/* process compose hint tags */
for (guint i = 0; asc_hint_tag_list[i].tag != NULL; i++) {
g_autofree gchar *entry_str = NULL;

entry_str = make_valtag_entry ("asc",
asc_hint_tag_list[i].tag,
asc_hint_tag_list[i].severity,
asc_hint_tag_list[i].explanation);
g_string_append_printf (valtags, "\n%s\n", entry_str);
}
as_gstring_replace (coval_contents, "{{hints_list}}", valtags->str, -1);

/* save result */
val_fname = g_build_filename (work_dir, "xml", "validator-issues.xml", NULL);
coval_fname = g_build_filename (work_dir, "xml", "validator-compose-hints.xml", NULL);

if (!g_file_set_contents (val_fname, val_contents->str, -1, &error)) {
g_error ("Failed to save generated documentation: %s", error->message);
return FALSE;
}

if (!g_file_set_contents (coval_fname, coval_contents->str, -1, &error)) {
g_error ("Failed to save generated documentation: %s", error->message);
return FALSE;
}

return TRUE;
}

int
main (int argc, char **argv)
{
g_autoptr(GOptionContext) option_context = NULL;
gboolean ret;
gboolean verbose = FALSE;
g_autoptr(GError) error = NULL;

const GOptionEntry options[] = {
{ "verbose",
'v', 0,
G_OPTION_ARG_NONE, &verbose,
"Show extra debugging information", NULL },
{ NULL }
};

option_context = g_option_context_new (" - DOCDIR");

g_option_context_add_main_entries (option_context, options, NULL);
ret = g_option_context_parse (option_context, &argc, &argv, &error);
if (!ret) {
/* TRANSLATORS: Error message of appstream-compose */
g_print ("%s: %s\n", "Failed to parse arguments", error->message);
return EXIT_FAILURE;
}

if (verbose)
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);

if (argc <= 1) {
g_autofree gchar *tmp = NULL;
tmp = g_option_context_get_help (option_context, TRUE, NULL);
g_print ("%s", tmp);
return EXIT_FAILURE;
}

process_validator_tag_lists (argv[1]);

return EXIT_SUCCESS;
}
29 changes: 26 additions & 3 deletions docs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ endif
# Documentation
#

gen_doc_valsec_src = [
'gen-valtag-sections.c',
'../src/as-validator-issue-tag.h',
'../compose/asc-hint-tags.h',
'../compose/asc-hint-tags.c',
]

gen_doc_valsec_exe = executable('gen-validator-tag-sections',
[gen_doc_valsec_src],
dependencies: [appstream_dep,
gio_dep],
include_directories: [root_inc_dir,
include_directories ('../compose')],
c_args: ['-DASC_COMPILATION'],
install: false,
)

as_doc_src = [
'xml/APIDoc.xml',
'xml/AppStream.xml',
Expand Down Expand Up @@ -100,6 +117,7 @@ as_doc_src = [
'xml/quickstart-desktopapps.xml',
'xml/quickstart-packaging.xml',
'xml/quickstart-translation.xml',
'xml/Validation.xml',
]

hljs_installed_file = '/usr/share/javascript/highlight.js/highlight.min.js'
Expand All @@ -113,19 +131,23 @@ if get_option('docs')
'--build',
'--src', meson.current_source_dir(),
'--builddir', meson.current_build_dir(),
'--valsec-gen', gen_doc_valsec_exe.path(),
'AppStream'
]

make_docs_target = custom_target('make-docs',
input: ['DC-AppStream',
as_doc_src],
output: ['docs_built.stamp'],
depends: [gen_doc_valsec_exe],
build_by_default: true,
command: build_docs_cmd
)

# helper if you only and always want to rebuild the docs
run_target('documentation', command: build_docs_cmd)
run_target('documentation',
command: build_docs_cmd,
depends: [gen_doc_valsec_exe])

if get_option('install-docs')
install_subdir('html', install_dir: as_doc_target_dir)
Expand All @@ -139,11 +161,12 @@ if get_option('docs')
endif

# add an extra testcase for documentation validation
test ('as-validate_docs',
test('as-validate_docs',
python_exe,
args: [join_paths(meson.current_source_dir(), 'doc-build-helper.py'),
'--validate',
'--src', meson.current_source_dir()],
'--src', meson.current_source_dir(),
'--valsec-gen', gen_doc_valsec_exe.path()],
timeout: 60
)
elif get_option('install-docs')
Expand Down
33 changes: 33 additions & 0 deletions docs/validator-compose-hints.xml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "AppStream.ent">
%BOOK_ENTITIES;
]>

<section id="sect-ComposeHints">
<title>Compose Hints</title>

<section id="compose-hints-intro">
<title>Introduction</title>
<para>
The AppStream Compose utility <command>appstreamcli compose</command> can be used to construct
AppStream catalog metadata from a set of directories and other sources containing MetaInfo files.
</para>
<para>
While compositing the final data, a variety of hints can be emitted.
</para>
</section>

<section id="compose-hints-list">
<title>Compose Hints List</title>

<para>
This is a list of all possible validation issues that <code>appstreamcli</code> can detect.
</para>

<variablelist>
{{hints_list}}
</variablelist>

</section>
</section>
Loading

0 comments on commit 5c74769

Please sign in to comment.