summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Repository.mk1
-rw-r--r--l10ntools/Executable_treex.mk36
-rw-r--r--l10ntools/Module_l10ntools.mk1
-rw-r--r--l10ntools/inc/treemerge.hxx38
-rw-r--r--l10ntools/source/localize.cxx3
-rw-r--r--l10ntools/source/treemerge.cxx304
-rw-r--r--l10ntools/source/treex.cxx60
-rw-r--r--solenv/gbuild/ExtensionTarget.mk37
8 files changed, 462 insertions, 18 deletions
diff --git a/Repository.mk b/Repository.mk
index 217d74a19278..5b24259e59c1 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -69,6 +69,7 @@ $(eval $(call gb_Helper_register_executables,NONE, \
svg2odf \
svidl \
transex3 \
+ treex \
typesconfig \
$(if $(filter UCPP,$(BUILD_TYPE)),\
ucpp \
diff --git a/l10ntools/Executable_treex.mk b/l10ntools/Executable_treex.mk
new file mode 100644
index 000000000000..41af0a8bfbc9
--- /dev/null
+++ b/l10ntools/Executable_treex.mk
@@ -0,0 +1,36 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# 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/.
+#
+
+
+$(eval $(call gb_Executable_Executable,treex))
+
+$(eval $(call gb_Executable_set_include,treex,\
+ -I$(SRCDIR)/l10ntools/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_Executable_use_libraries,treex,\
+ sal \
+ i18nregexp \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,treex,\
+ transex \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,treex,\
+ l10ntools/source/treemerge \
+ l10ntools/source/treex \
+))
+
+$(eval $(call gb_Executable_use_externals,treex,\
+ libxml2 \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/l10ntools/Module_l10ntools.mk b/l10ntools/Module_l10ntools.mk
index e6c0cc3a8047..6efcd7f45596 100644
--- a/l10ntools/Module_l10ntools.mk
+++ b/l10ntools/Module_l10ntools.mk
@@ -39,6 +39,7 @@ $(eval $(call gb_Module_add_targets,l10ntools,\
Executable_transex3 \
Executable_renewpo \
Executable_propex \
+ Executable_treex \
StaticLibrary_transex \
Package_inc \
Package_scripts \
diff --git a/l10ntools/inc/treemerge.hxx b/l10ntools/inc/treemerge.hxx
new file mode 100644
index 000000000000..3b681126bfab
--- /dev/null
+++ b/l10ntools/inc/treemerge.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ */
+
+#ifndef _TREEMERGE_INCLUDED
+#define _TREEMERGE_INCLUDED
+
+#include <libxml/tree.h>
+#include <rtl/string.hxx>
+#include <vector>
+
+class TreeParser
+{
+private:
+ xmlDocPtr m_pSource;
+ OString m_sLang;
+ bool m_bIsInitialized;
+
+public:
+ TreeParser(
+ const OString& rInputFile, const OString& rLang );
+ ~TreeParser();
+
+ bool isInitialized() const { return m_bIsInitialized; }
+ void Extract(
+ const OString& rSDFFile, const OString& rPrj, const OString& rRoot );
+ void Merge(
+ const OString &rMergeSrc, const OString &rDestinationFile,
+ const OString &rXhpRoot );
+};
+
+#endif //_TREEMERGE_INCLUDED
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/l10ntools/source/localize.cxx b/l10ntools/source/localize.cxx
index fd1d8ca4b4d0..f808af59d6cd 100644
--- a/l10ntools/source/localize.cxx
+++ b/l10ntools/source/localize.cxx
@@ -324,7 +324,8 @@ void handleFile(
{ RTL_CONSTASCII_STRINGPARAM(".xml"), "xrmex", true },
{ RTL_CONSTASCII_STRINGPARAM(".xhp"), "helpex", false },
{ RTL_CONSTASCII_STRINGPARAM(".properties"), "propex", false },
- { RTL_CONSTASCII_STRINGPARAM(".ui"), "uiex", false } };
+ { RTL_CONSTASCII_STRINGPARAM(".ui"), "uiex", false },
+ { RTL_CONSTASCII_STRINGPARAM(".tree"), "treex", false } };
for (std::size_t i = 0; i != SAL_N_ELEMENTS(commands); ++i) {
if (url.endsWithAsciiL(
commands[i].extension, commands[i].extensionLength))
diff --git a/l10ntools/source/treemerge.cxx b/l10ntools/source/treemerge.cxx
new file mode 100644
index 000000000000..12e931c6d221
--- /dev/null
+++ b/l10ntools/source/treemerge.cxx
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <cstring>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlstring.h>
+
+#include "export.hxx"
+#include "common.hxx"
+#include "treemerge.hxx"
+
+
+namespace
+{
+ //Write out an sdf line
+ static void lcl_WriteSDF(
+ std::ofstream &aSDFStream, const OString& rText, const OString& rPrj,
+ const OString& rActFileName, const OString& rID, const OString& rType )
+ {
+ OString sOutput( rPrj ); sOutput += "\t";
+ sOutput += rActFileName;
+ sOutput += "\t0\t";
+ sOutput += rType; sOutput += "\t";
+ sOutput += rID; sOutput += "\t\t\t\t0\ten-US\t";
+ sOutput += rText; sOutput += "\t\t\t\t";
+ aSDFStream << sOutput.getStr() << std::endl;
+ }
+
+ //Convert xmlChar* to OString
+ static OString lcl_xmlStrToOString( const xmlChar* pString )
+ {
+ xmlChar* pTemp = xmlStrdup( pString );
+ OString sResult =
+ static_cast<OString>(reinterpret_cast<sal_Char*>( pTemp ));
+ xmlFree( pTemp );
+ return sResult;
+ }
+
+ //Extract strings from nodes on all level recursively
+ static void lcl_ExtractLevel(
+ const xmlDocPtr pSource, const xmlNodePtr pRoot,
+ const xmlChar* pNodeName, std::ofstream& rSDFStream,
+ const OString& rPrj, const OString& rRoot )
+ {
+ if( !pRoot->children )
+ {
+ return;
+ }
+ for( xmlNodePtr pCurrent = pRoot->children->next;
+ pCurrent; pCurrent = pCurrent->next)
+ {
+ if (!xmlStrcmp(pCurrent->name, pNodeName))
+ {
+ xmlChar* pID = xmlGetProp(pCurrent, (const xmlChar*)("id"));
+ xmlChar* pText =
+ xmlGetProp(pCurrent, (const xmlChar*)("title"));
+ lcl_WriteSDF(
+ rSDFStream,
+ lcl_xmlStrToOString( pText ),
+ rPrj,
+ common::pathnameToken(
+ pSource->name, rRoot.getStr()),
+ lcl_xmlStrToOString( pID ),
+ lcl_xmlStrToOString( pNodeName ));
+
+ xmlFree( pID );
+ xmlFree( pText );
+
+ lcl_ExtractLevel(
+ pSource, pCurrent, (const xmlChar *)("node"),
+ rSDFStream, rPrj, rRoot );
+ }
+ }
+ }
+
+ //Update id and content of the topic
+ static xmlNodePtr lcl_UpdateTopic(
+ const xmlNodePtr pCurrent, const OString& rXhpRoot )
+ {
+ xmlNodePtr pReturn = pCurrent;
+ xmlChar* pID = xmlGetProp(pReturn, (const xmlChar*)("id"));
+ const OString sID =
+ lcl_xmlStrToOString( pID );
+ xmlFree( pID );
+
+ const sal_Int32 nFirstSlash = sID.indexOf("/");
+ //Update id attribute of topic
+ {
+ OString sNewID =
+ sID.copy( 0, nFirstSlash + 1 ) +
+ rXhpRoot.copy( rXhpRoot.lastIndexOf("/") + 1 ) +
+ sID.copy( sID.indexOf( "/", nFirstSlash + 1 ) );
+ xmlSetProp(
+ pReturn, (const xmlChar*)("id"),
+ reinterpret_cast<const xmlChar*>(sNewID.getStr()));
+ }
+
+ const OString sXhpPath =
+ rXhpRoot +
+ sID.copy(sID.indexOf("/", nFirstSlash + 1));
+ xmlDocPtr pXhpFile = xmlParseFile( sXhpPath.getStr() );
+ //if xhpfile is missing than we put this topic into comment
+ if ( !pXhpFile )
+ {
+ xmlNodePtr pTemp = pReturn;
+ xmlChar* sNewID =
+ xmlGetProp(pReturn, (const xmlChar*)("id"));
+ xmlChar* sComment =
+ xmlStrcat( xmlCharStrdup("removed "), sNewID );
+ pReturn = xmlNewComment( sComment );
+ xmlReplaceNode( pTemp, pReturn );
+ xmlFree( pTemp );
+ xmlFree( sNewID );
+ xmlFree( sComment );
+ }
+ //update topic's content on the basis of xhpfile's title
+ else
+ {
+ xmlNodePtr pXhpNode = xmlDocGetRootElement( pXhpFile );
+ for( pXhpNode = pXhpNode->children;
+ pXhpNode; pXhpNode = pXhpNode->children )
+ {
+ while( pXhpNode->type != XML_ELEMENT_NODE )
+ {
+ pXhpNode = pXhpNode->next;
+ }
+ if(!xmlStrcmp(pXhpNode->name, (const xmlChar *)("title")))
+ {
+ xmlChar* sTitle =
+ xmlNodeListGetString(pXhpFile, pXhpNode->children, 1);
+ OString sNewTitle =
+ lcl_xmlStrToOString( sTitle ).
+ replaceAll("$[officename]","%PRODUCTNAME").
+ replaceAll("$[officeversion]","%PRODUCTVERSION");
+ xmlNodeSetContent(
+ pReturn,
+ reinterpret_cast<const xmlChar*>( sNewTitle.getStr() ));
+ xmlFree( sTitle );
+ break;
+ }
+ }
+ if( !pXhpNode )
+ {
+ std::cerr
+ << "Treex error: Cannot find title in "
+ << sXhpPath.getStr() << std::endl;
+ return 0;
+ }
+ xmlFree( pXhpFile );
+ xmlCleanupParser();
+ }
+ return pReturn;
+ }
+ //Localize title attribute of help_section and node tags
+ static void lcl_MergeLevel(
+ xmlDocPtr io_pSource, const xmlNodePtr pRoot,
+ const xmlChar * pNodeName, MergeDataFile* pMergeDataFile,
+ const OString& rLang, const OString& rXhpRoot )
+ {
+ if( !pRoot->children )
+ {
+ return;
+ }
+ for( xmlNodePtr pCurrent = pRoot->children;
+ pCurrent; pCurrent = pCurrent->next)
+ {
+ if( !xmlStrcmp(pCurrent->name, pNodeName) )
+ {
+ if( pMergeDataFile )
+ {
+ xmlChar* pID = xmlGetProp(pCurrent, (const xmlChar*)("id"));
+ ResData aResData(
+ "", lcl_xmlStrToOString( pID ),
+ static_cast<OString>(io_pSource->name) );
+ xmlFree( pID );
+ aResData.sResTyp = lcl_xmlStrToOString( pNodeName );
+ PFormEntrys* pEntrys =
+ pMergeDataFile->GetPFormEntrys( &aResData );
+ if( pEntrys )
+ {
+ OString sNewText;
+ pEntrys->GetText( sNewText, STRING_TYP_TEXT, rLang );
+ xmlSetProp(
+ pCurrent, (const xmlChar*)("title"),
+ (const xmlChar*)(sNewText.getStr()));
+ }
+ }
+ lcl_MergeLevel(
+ io_pSource, pCurrent, (const xmlChar *)("node"),
+ pMergeDataFile, rLang, rXhpRoot );
+ }
+ else if( !xmlStrcmp(pCurrent->name, (const xmlChar *)("topic")) )
+ {
+ pCurrent = lcl_UpdateTopic( pCurrent, rXhpRoot );
+ }
+ }
+ }
+}
+
+//Parse tree file
+TreeParser::TreeParser(
+ const OString& rInputFile, const OString& rLang )
+ : m_pSource( 0 )
+ , m_sLang( rLang )
+ , m_bIsInitialized( false )
+{
+ m_pSource = xmlParseFile( rInputFile.getStr() );
+ if ( !m_pSource ) {
+ std::cerr
+ << "Treex error: Cannot open source file: "
+ << rInputFile.getStr() << std::endl;
+ return;
+ }
+ if( !m_pSource->name )
+ {
+ m_pSource->name = new char[strlen(rInputFile.getStr())+1];
+ strcpy( m_pSource->name, rInputFile.getStr() );
+ }
+ m_bIsInitialized = true;
+}
+
+TreeParser::~TreeParser()
+{
+}
+
+//Extract strings form source file
+void TreeParser::Extract(
+ const OString& rSDFFile, const OString& rPrj, const OString& rRoot )
+{
+ assert( m_bIsInitialized );
+ std::ofstream aSDFStream(
+ rSDFFile.getStr(), std::ios_base::out | std::ios_base::trunc );
+ if( !aSDFStream.is_open() )
+ {
+ std::cerr
+ << "Treex error: Cannot open sdffile for extract: "
+ << rSDFFile.getStr() << std::endl;
+ return;
+ }
+
+ xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
+ lcl_ExtractLevel(
+ m_pSource, pRootNode, (const xmlChar *)("help_section"),
+ aSDFStream, rPrj, rRoot );
+
+ xmlFreeDoc( m_pSource );
+ xmlCleanupParser();
+ aSDFStream.close();
+ m_bIsInitialized = false;
+}
+
+//Merge strings to tree file and update reference to help files(xhp)
+void TreeParser::Merge(
+ const OString &rMergeSrc, const OString &rDestinationFile,
+ const OString &rXhpRoot )
+{
+ assert( m_bIsInitialized );
+
+ const xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
+ if( m_sLang == "en-US" )
+ {
+ lcl_MergeLevel(
+ m_pSource, pRootNode, (const xmlChar *)("help_section"),
+ 0, m_sLang, rXhpRoot );
+ }
+ else
+ {
+ MergeDataFile aMergeDataFile(
+ rMergeSrc, static_cast<OString>( m_pSource->name ), false );
+
+ if( aMergeDataFile.GetLanguages()[0] != m_sLang )
+ {
+ std::cerr
+ << "Treex error: given language conflicts with "
+ << "language of Mergedata file: "
+ << m_sLang.getStr() << " - " << rMergeSrc.getStr() << std::endl;
+ return;
+ }
+
+ lcl_MergeLevel(
+ m_pSource, pRootNode, (const xmlChar *)("help_section"),
+ &aMergeDataFile, m_sLang, rXhpRoot );
+ }
+
+ xmlSaveFile( rDestinationFile.getStr(), m_pSource );
+ xmlFreeDoc( m_pSource );
+ xmlCleanupParser();
+ m_bIsInitialized = false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/l10ntools/source/treex.cxx b/l10ntools/source/treex.cxx
new file mode 100644
index 000000000000..65807b1a5c0b
--- /dev/null
+++ b/l10ntools/source/treex.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ */
+
+#include <iostream>
+#include "sal/main.h"
+
+#include "export.hxx"
+#include "treemerge.hxx"
+
+void WriteUsage()
+{
+ std::cout
+ << "Syntax: Treex [-p Prj] [-r Root] -i FileIn -o FileOut"
+ << " [-m DataBase] [-l l1,l2,...]\n"
+ << " Prj: Project\n"
+ << " Root: Path to project root (../.. etc.)\n"
+ << " or path to root of localized xhp files\n"
+ << " FileIn: Source files (*.tree)\n"
+ << " FileOut: Destination file (*.*)\n"
+ << " DataBase: Mergedata (*.po)\n"
+ << " -l: Restrict the handled languages; l1, l2, ... are elements of"
+ << " (de, en-US, ...)\n";
+}
+
+
+SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
+{
+ HandledArgs aArgs;
+ if( !Export::handleArguments(argc, argv, aArgs) )
+ {
+ WriteUsage();
+ return 1;
+ }
+
+ TreeParser aParser(aArgs.m_sInputFile, Export::sLanguages);
+ if( !aParser.isInitialized() )
+ {
+ return 1;
+ }
+
+ if( aArgs.m_bMergeMode || aArgs.m_sPrj.isEmpty() )
+ {
+ aParser.Merge(
+ aArgs.m_sMergeSrc, aArgs.m_sOutputFile, aArgs.m_sPrjRoot );
+ }
+ else
+ {
+ aParser.Extract(
+ aArgs.m_sOutputFile, aArgs.m_sPrj, aArgs.m_sPrjRoot );
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/solenv/gbuild/ExtensionTarget.mk b/solenv/gbuild/ExtensionTarget.mk
index 7c94e15d4e3d..bc00c11a8c2f 100644
--- a/solenv/gbuild/ExtensionTarget.mk
+++ b/solenv/gbuild/ExtensionTarget.mk
@@ -39,11 +39,9 @@ gb_ExtensionTarget_PROPMERGETARGET := $(call gb_Executable_get_target_for_build,
gb_ExtensionTarget_PROPMERGECOMMAND := \
$(gb_Helper_set_ld_path) $(gb_ExtensionTarget_PROPMERGETARGET)
-gb_ExtensionTarget_UPDATETREETARGET := $(SRCDIR)/l10ntools/scripts/update_tree.pl
-gb_ExtensionTarget_UPDATETREECOMMAND := \
- $(gb_Helper_set_ld_path) $(PERL) $(gb_ExtensionTarget_UPDATETREETARGET)
- # update_tree.pl calls xmllint, which needs $(gb_Helper_set_ld_path) if it
- # is the internal one
+gb_ExtensionTarget_TREEXTARGET := $(call gb_Executable_get_target_for_build,treex)
+gb_ExtensionTarget_TREEXCOMMAND := \
+ $(gb_Helper_set_ld_path) $(gb_ExtensionTarget_TREEXTARGET)
gb_ExtensionTarget_HELPEXTARGET := $(call gb_Executable_get_target_for_build,helpex)
gb_ExtensionTarget_HELPEXCOMMAND := \
@@ -261,9 +259,8 @@ endef
# suffix
# $(3): relative path of (target) help.tree file (e.g.,
# com.sun.wiki-publisher/help.tree)
-# $(4): optional relative path of source help.tree file, when it differs from $(3)
-# (i.e., if $(4) is empty the en-US source file is $(2)/$(3), otherwise it
-# is $(2)/$(4))
+# $(4): relative path of source help.tree file
+# $(5): relative path of localized xhp files (PlatformID included)
define gb_ExtensionTarget_add_helptreefile
$(foreach lang,$(gb_ExtensionTarget_ALL_LANGS), \
$(call gb_ExtensionTarget__localize_helptreefile_onelang,$(1),$(2),$(3),$(4),$(lang),$(5)) \
@@ -322,38 +319,44 @@ $(call gb_ExtensionTarget_get_workdir,$(1))/help/$(5)/$(3) : \
endef
# localize one help.tree for one language; the result is stored as
-# help/$(4)/$(3) in the extension's workdir; as a special case, if $(4) is
-# "en-US", the source file is just copied, not passed through update_tree.pl
+# help/$(4)/$(3) in the extension's workdir;
# $(1): extension identifier
# $(2): absolute path prefix of en-US source file without $(3) (resp. $(4))
# suffix
# $(3): relative path of (target) help.tree file (see
# gb_ExtensionTarget_add_helptreefile)
-# $(4): optional relative path of source help.tree file (see
+# $(4): relative path of source help.tree file (see
# gb_ExtensionTarget_add_helptreefile)
# $(5): language
+# $(6): relative path of localized xhp files (PlatformID included)
define gb_ExtensionTarget__localize_helptreefile_onelang
$(call gb_ExtensionTarget_get_rootdir,$(1))/help/$(5).done : \
$(call gb_ExtensionTarget_get_rootdir,$(1))/help/$(5)/$(3)
ifneq ($(strip $(gb_WITH_LANG)),)
ifneq ($(filter-out en-US,$(5)),)
$(call gb_ExtensionTarget_get_rootdir,$(1))/help/$(5)/$(3) : \
- POFILE := $(gb_POLOCATION)/$(5)$(subst $(SRCDIR),,$(2))$(patsubst %/,/%.po,$(patsubst ./,.po,$(dir $(or $(4),$(3)))))
+ POFILE := $(gb_POLOCATION)/$(5)$(subst $(SRCDIR),,$(2))$(patsubst %/,/%.po,$(patsubst ./,.po,$(dir $(4))))
$(call gb_ExtensionTarget_get_rootdir,$(1))/help/$(5)/$(3) : \
- $(gb_POLOCATION)/$(5)$(subst $(SRCDIR),,$(2))$(patsubst %/,/%.po,$(patsubst ./,.po,$(dir $(or $(4),$(3)))))
+ $(gb_POLOCATION)/$(5)$(subst $(SRCDIR),,$(2))$(patsubst %/,/%.po,$(patsubst ./,.po,$(dir $(4))))
endif
endif
$(call gb_ExtensionTarget_get_rootdir,$(1))/help/$(5)/$(3) : \
- $(if $(filter-out en-US,$(5)),$(gb_ExtensionTarget_UPDATETREETARGET)) | \
+ $(gb_ExtensionTarget_TREEXTARGET) | \
$(2)/$(4)
$(call gb_ExtensionTarget_get_rootdir,$(1))/help/$(5)/$(3) : \
$(2)/$(or $(4),$(3))
$$(call gb_Output_announce,$(1) $(3) $(5),$(true),TRE,3)
$$(call gb_Helper_abbreviate_dirs, \
- mkdir -p $$(dir $$@) && \
+ mkdir -p $$(dir $$@) && \
$(if $(filter-out en-US,$(5)), \
- cp $$< $$@,\
- cp $$< $$@))
+ MERGEINPUT=`$(gb_MKTEMP)` && \
+ echo $$(POFILE) > $$$${MERGEINPUT} && \
+ $(gb_ExtensionTarget_TREEXCOMMAND) -i $$< -o $$@ -l $(5) \
+ -m $$$${MERGEINPUT} \
+ -r $$(call gb_ExtensionTarget_get_workdir,$(1))/help/$(5)/$(6) && \
+ rm -rf $$$${MERGEINPUT}, \
+ $(gb_ExtensionTarget_TREEXCOMMAND) -i $$< -o $$@ -l $(5) \
+ -r $$(call gb_ExtensionTarget_get_workdir,$(1))/help/$(5)/$(6) ))
endef