This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
library-web/src/modtypes/mallard.py

366 lines
14 KiB
Python

# libgo - script to build library.gnome.org
# Copyright (C) 2007-2009 Frederic Peters
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
import glob
import logging
import os
import re
import shutil
import stat
import subprocess
import tarfile
import tempfile
try:
import elementtree.ElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
import xml.parsers.expat
import errors
from base import DocModule
def dup_node(e):
return ET.fromstring(ET.tostring(e))
MAL_NS = 'http://projectmallard.org/1.0/'
class NotAMallardPageException(Exception):
pass
class MallardPage:
id = None
link_title = None
sort_title = None
info_nodes = None
sections = None
def __init__(self, filename):
self.info_nodes = []
self.sections = []
tree = ET.parse(filename)
if tree.getroot().tag != '{%s}page' % MAL_NS:
raise NotAMallardPageException()
try:
self.id = tree.getroot().attrib['id']
except KeyError:
pass
if self.id is None:
return
self.page_info(tree.find('{%s}info' % MAL_NS))
self.title_tag = tree.find('{%s}title' % MAL_NS)
if self.title_tag is not None:
if self.link_title is None:
self.link_title = dup_node(self.title_tag)
self.link_title.attrib['type'] = 'link'
if self.sort_title is None:
self.sort_title = dup_node(self.title_tag)
self.sort_title.attrib['type'] = 'sort'
for section in tree.findall('{%s}section' % MAL_NS):
title_tag = section.find('{%s}title' % MAL_NS)
if title_tag is None:
continue
if not section.attrib.get('id'):
continue
self.sections.append({'id': section.attrib['id'],
'title': dup_node(title_tag),
'link_title': dup_node(title_tag),
'sort_title': dup_node(title_tag)})
def page_info(self, element):
if element is None:
return
title_tag = element.find('{%s}title' % MAL_NS)
if title_tag is not None:
type = title_tag.attrib.get('type', 'link')
try:
role = title_tag.attrib['role']
except KeyError:
role = None
if type == 'link' and not role:
self.link_title = dup_node(title_tag)
if type == 'sort':
self.sort_title = dup_node(title_tag)
for desc_tag in element.findall('{%s}desc' % MAL_NS):
self.info_nodes.append(desc_tag)
for link_tag in element.findall('{%s}link' % MAL_NS):
self.info_nodes.append(link_tag)
for link_tag in element.findall('{%s}title' % MAL_NS):
self.info_nodes.append(link_tag)
def toxml(self):
e = ET.Element('{%s}page' % MAL_NS)
e.attrib['id'] = self.id
info = ET.SubElement(e, '{%s}info' % MAL_NS)
for node in self.info_nodes:
info.append(node)
if self.link_title is not None:
info.append(self.link_title)
if self.sort_title is not None:
info.append(self.sort_title)
if self.title_tag:
e.append(dup_node(self.title_tag))
for section in self.sections:
section_node = ET.SubElement(e, '{%s}section' % MAL_NS)
section_node.attrib['id'] = '%s#%s' % (self.id, section.get('id'))
section_info = ET.SubElement(section_node, '{%s}info' % MAL_NS)
section.get('link_title').attrib['type'] = 'link'
section.get('sort_title').attrib['type'] = 'sort'
section_info.append(section.get('link_title'))
section_info.append(section.get('sort_title'))
section_node.append(section.get('title'))
return e
class MallardCache:
def __init__(self):
self.all_pages = []
def add_page(self, filename):
page = MallardPage(filename)
if page.id is None:
return
self.all_pages.append(page)
return page
def toxml(self):
mallard_cache = ET.Element('{%s}cache' % MAL_NS)
for page in self.all_pages:
mallard_cache.append(page.toxml())
return mallard_cache
def dump(self, filename):
tree = ET.ElementTree(self.toxml())
tree.write(filename)
class MallardModule(DocModule):
'''Class for documentation shipped in a tarball and using gnome-doc-utils'''
mal2html_xsl_file = os.path.join(data_dir, 'xslt', 'mal2html.xsl')
category = None
related_xsl_files = ['mal2html.xsl', 'heading.xsl']
def setup_channel(self):
# get category from omf file
ext_dirname = os.path.join(app.config.private_dir, 'extracts')
omf_file = glob.glob(os.path.join(ext_dirname, self.dirname) + '/*.omf.in')
if not omf_file:
omf_file = glob.glob(os.path.join(ext_dirname, self.dirname) + '/C/*.omf.in')
if omf_file:
try:
self.category = ET.parse(omf_file[0]).find('resource/subject').attrib['category']
except (IndexError, KeyError):
pass
channel = 'users'
if self.category and (self.category.startswith('GNOME|Development') or
self.category.startswith('GNOME|Applications|Programming')):
channel = 'devel'
self.channel = app.overlay.get_channel_overlay(self.modulename, channel)
if self.channel == 'misc' and not self.category:
self.category = 'GNOME|Other'
def __str__(self):
return 'mallard module at %s' % self.dirname
def expand_doc_figures(self, doc_figures):
for figure in doc_figures[:]:
if figure == '$(NULL)': # common idiom
doc_figures.remove(figure)
continue
if figure.startswith('$'): # $(blah)
doc_figures.remove(figure)
var_name = figure[2:-1]
var_content = re.findall('%s\s+=\s+(.*)' % var_name,
self.makefile_am)[0].split()
doc_figures.extend(var_content)
def process(self):
doc_module = self.modulename
ext_dirname = os.path.join(app.config.private_dir, 'extracts')
try:
doc_linguas = re.findall(r'(?:DOC_LINGUAS|HELP_LINGUAS)\s+=[\t ](.*)',
self.makefile_am)[0].split()
if not 'en' in doc_linguas:
doc_linguas.append('en')
except IndexError:
doc_linguas = ['en']
try:
doc_pages = re.findall(r'(?:DOC_PAGES|HELP_FILES)\s+=\s+(.*)',
self.makefile_am)[0].split()
except IndexError:
return
try:
doc_figures = re.findall(r'(?:DOC_FIGURES|HELP_FIGURES)\s+=\s+(.*)',
self.makefile_am)[0].split()
figures_dirname = os.path.join(ext_dirname, self.dirname, 'C')
self.expand_doc_figures(doc_figures)
for doc_figure in doc_figures:
if not os.path.exists(os.path.join(figures_dirname, doc_figure)):
logging.warning('figure (%s) listed but not present, going to autodiscover' % \
doc_figure)
raise IndexError()
except IndexError:
figures_dirname = os.path.join(ext_dirname, self.dirname, 'C', 'figures')
images_dirname = os.path.join(ext_dirname, self.dirname, 'C', 'images')
doc_figures = []
if os.path.exists(figures_dirname):
doc_figures += [os.path.join('figures', x) for x in \
os.listdir(figures_dirname) \
if os.path.splitext(x)[1] in ('.png', '.jpg', '.jpeg')]
if os.path.exists(images_dirname):
doc_figures += [os.path.join('images', x) for x in \
os.listdir(images_dirname) \
if os.path.splitext(x)[1] in ('.png', '.jpg', '.jpeg')]
doc_linguas.sort()
if app.config.languages:
for lang in doc_linguas[:]:
if lang not in app.config.languages + ['C']:
doc_linguas.remove(lang)
doc = self.get_libgo_document(doc_linguas)
if not doc:
return
if self.category:
doc.category = self.category
doc.toc_id = app.toc_mapping.get(doc.category)
web_output_dir = app.get_module_web_output_dir(self, versioned=True)
logging.info('generating doc in %s' % web_output_dir[len(app.config.output_dir):])
if not os.path.exists(web_output_dir):
os.makedirs(web_output_dir)
for lang in doc.languages:
if lang == 'en' and not os.path.exists(os.path.join(ext_dirname, self.dirname, 'en')):
lang_dirname = os.path.join(ext_dirname, self.dirname, 'C')
else:
lang_dirname = os.path.join(ext_dirname, self.dirname, lang)
mallard_cache = MallardCache()
for doc_page in doc_pages:
xml_file = os.path.join(lang_dirname, doc_page)
if not os.path.exists(xml_file):
if lang in doc.languages:
doc.languages.remove(lang)
logging.warn('failed to find %s for lang %s' % (xml_file, lang))
continue
try:
page = mallard_cache.add_page(xml_file)
except xml.parsers.expat.ExpatError:
logging.warn('failed to add %s to mallard cache file for lang %s' % (
xml_file, lang))
continue
except NotAMallardPageException:
logging.warn('failed processing %s, not a mallard page' % xml_file)
doc_pages.remove(doc_page)
continue
if doc_page == 'index.page':
def get_plain(x):
# XXX: this should be removed, Mallard provides a text
# version of the title in <info>
if x.text:
t = x.text
else:
t = ''
for element in x.getchildren():
if element.tag == '{%s}media' % MAL_NS:
continue
if not element.text:
continue
t += ' '
t += element.text
if element.tail:
t += element.tail
return t.strip()
doc.title[lang] = get_plain(page.link_title)
if not lang in doc.languages:
continue
temporary = tempfile.NamedTemporaryFile()
mallard_cache.dump(temporary.name)
html_index_file = os.path.join(web_output_dir, 'index.html.%s' % lang)
if not app.rebuild_all and (
app.rebuild_language is None or
lang != app.rebuild_language) and os.path.exists(html_index_file):
mtime = os.stat(html_index_file)[stat.ST_MTIME]
if mtime > max(self.mtime_tarball, self.mtime_xslt_files):
continue
for doc_page in doc_pages:
xml_file = os.path.join(lang_dirname, doc_page)
filename = os.path.splitext(doc_page)[0] + '.html.' + lang
# format docbook into html files
cmd = ['xsltproc',
'--nonet', '--xinclude',
'--stringparam', 'libgo.mallard.html.basedir', web_output_dir,
'--stringparam', 'mal.cache.file', temporary.name,
'--stringparam', 'libgo.lang', lang,
'--stringparam', 'libgo.channel', self.channel,
self.mal2html_xsl_file, xml_file]
if self.nightly:
cmd[2:2] = ['--param', 'libgo.nightly', 'true()']
if app.config.symbols_dbm_filepath:
cmd.insert(-2, '--param')
cmd.insert(-2, 'libgo.dbm_support')
cmd.insert(-2, 'true()')
onepage_cmd = cmd[:]
logging.debug('executing %s' % ' '.join(cmd))
rc = subprocess.call(cmd)
if rc != 0:
logging.warn('%s failed with error %d' % (' '.join(cmd), rc))
if doc_figures:
# and copy images/
logging.debug('copying figures')
for doc_figure in doc_figures:
src = os.path.join(lang_dirname, doc_figure)
if not os.path.exists(src):
# fallback to image from C locale.
src = os.path.join(ext_dirname, self.dirname, 'C', doc_figure)
if not os.path.exists(src):
continue
if os.path.splitext(src)[-1] in ('.catalog',):
# the .catalog file should be unique for all
# translations
dst = os.path.join(web_output_dir, doc_figure)
else:
dst = os.path.join(web_output_dir, doc_figure + '.%s' % lang)
if not os.path.exists(os.path.split(dst)[0]):
os.makedirs(os.path.split(dst)[0])
open(dst, 'w').write(open(src, 'r').read())
self.install_version_symlinks(doc)