Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ruifontes committed Mar 24, 2020
0 parents commit 8eb49d4
Show file tree
Hide file tree
Showing 10 changed files with 748 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto

# Try to ensure that po files in the repo does not include
# source code line numbers.
# Every person expected to commit po files should change their personal config file as described here:
# https://mail.gnome.org/archives/kupfer-list/2010-June/msg00002.html
*.po filter=cleanpo
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
addon/doc/*.css
addon/doc/en/
*_docHandler.py
*.html
*.ini
*.mo
*.pot
*.py[co]
*.nvda-addon
.sconsign.dblite
340 changes: 340 additions & 0 deletions COPYING.txt

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions buildVars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: UTF-8 -*-

# Build customizations
# Change this file instead of sconstruct or manifest files, whenever possible.

# Full getext (please don't change)
_ = lambda x : x

# Add-on information variables
addon_info = {
# for previously unpublished addons, please follow the community guidelines at:
# https://bitbucket.org/nvdaaddonteam/todo/raw/master/guidelines.txt
# add-on Name, internal for nvda
"addon_name" : "addonTemplate",
# Add-on summary, usually the user visible name of the addon.
# Translators: Summary for this add-on to be shown on installation and add-on information.
"addon_summary" : _("Add-on user visible name"),
# Add-on description
# Translators: Long description to be shown for this add-on on add-on information from add-ons manager
"addon_description" : _("""Description for the add-on.
It can span multiple lines."""),
# version
"addon_version" : "x.y",
# Author(s)
"addon_author" : u"name <[email protected]>",
# URL for the add-on documentation support
"addon_url" : None,
# Documentation file name
"addon_docFileName" : "readme.html",
# Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional)
"addon_minimumNVDAVersion" : None,
# Last NVDA version supported/tested (e.g. "2018.4.0", ideally more recent than minimum version)
"addon_lastTestedNVDAVersion" : None,
# Add-on update channel (default is None, denoting stable releases, and for development releases, use "dev"; do not change unless you know what you are doing)
"addon_updateChannel" : None,
}


import os.path

# Define the python files that are the sources of your add-on.
# You can use glob expressions here, they will be expanded.
pythonSources = []

# Files that contain strings for translation. Usually your python sources
i18nSources = pythonSources + ["buildVars.py"]

# Files that will be ignored when building the nvda-addon file
# Paths are relative to the addon directory, not to the root directory of your addon sources.
excludedFiles = []
2 changes: 2 additions & 0 deletions manifest-translated.ini.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
summary = "{addon_summary}"
description = """{addon_description}"""
10 changes: 10 additions & 0 deletions manifest.ini.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name = {addon_name}
summary = "{addon_summary}"
description = """{addon_description}"""
author = "{addon_author}"
url = {addon_url}
version = {addon_version}
docFileName = {addon_docFileName}
minimumNVDAVersion = {addon_minimumNVDAVersion}
lastTestedNVDAVersion = {addon_lastTestedNVDAVersion}
updateChannel = {addon_updateChannel}
54 changes: 54 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# NVDA Add-on Scons Template #

This package contains a basic template structure for NVDA add-on development, building, distribution and localization.
For details about NVDA add-on development please see the [NVDA Developer Guide](http://www.nvda-project.org/documentation/developerGuide.html).
The NVDA addon development/discussion list [is here](https://nvda-addons.groups.io/g/nvda-addons)

Copyright (C) 2012-2019 nvda addon team contributors.

This package is distributed under the terms of the GNU General Public License, version 2 or later. Please see the file COPYING.txt for further details.

## Features

This template provides the following features you can use to help NVDA add-on development:

* Automatic add-on package creation, with naming and version loaded from a centralized build variables file (buildVars.py).
* Manifest file creation using a template (manifest.ini.tpl). Build variables are replaced on this template.
* Compilation of gettext mo files before distribution, when needed.
* To generate a gettext pot file, please run scons pot. A **addon-name.pot** file will be created with all gettext messages for your add-on. You need to check the buildVars.i18nSources variable to comply with your requirements.
* Automatic generation of manifest localization files directly from gettext po files. Please make sure buildVars.py is included in i18nFiles.
* Automatic generation of HTML documents from markdown (.md) files, to manage documentation in different languages.

## Requirements

You need the following software to use this code for your NVDA add-ons development:

* a Python distribution (2.7 or greater is recommended). Check the [Python Website](http://www.python.org) for Windows Installers.
* Scons - [Website](http://www.scons.org/) - version 2.1.0 or greater. Install it using **easy_install** or grab an windows installer from the website.
* GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](http://gnuwin32.sourceforge.net/downlinks/gettext.php).
* Markdown-2.0.1 or greater, if you want to convert documentation files to HTML documents. You can [Download Markdown-2.0.1 installer for Windows](https://pypi.python.org/pypi/Markdown/2.0.1) or get it using `easy_install markdown`.

## Usage

### To create a new NVDA add-on, taking advantage of this template:

1. Create an empty folder to hold the files for your add-on.
2. Copy the **site_scons** folder, and the following files, into your new empty folder: **buildVars.py**, **manifest.ini.tpl**, **manifest-translated.ini.tpl**, **sconstruct**, **.gitignore**, and **.gitattributes**
3. Create an **addon** folder inside your new folder. Inside the **addon* folder, create needed folders for the add-on modules (e.g. appModules, synthDrivers, etc.). An add-on may have one or more module folders.
4. In the **buildVars.py** file, change variable **addon_info** with your add-on's information (name, summary, description, version, author and url).
5. Put your code in the usual folders for NVDA extension, under the **addon** folder. For instance: globalPlugins, synthDrivers, etc.
6. Gettext translations must be placed into addon\locale\<lang>/LC_MESSAGES\nvda.po.

### To manage documentation files for your addon:

1. Copy the **readme.md** file for your add-on to the first created folder, where you copied **buildVars.py**. You can also copy **style.css** to improve the presentation of HTML documents.
2. Documentation files (named **readme.md**) must be placed into addon\doc\<lang>/.

+### To package the add-on for distribution:

1. Open a command line, change to the folder that has the **sconstruct** file (usually the root of your add-on development folder) and run the **scons** command. The created add-on, if there were no errors, is placed in the current directory.
2. You can further customize variables in the **buildVars.py** file.

Note that this template only provides a basic add-on structure and build infrastructure. You may need to adapt it for your specific needs.

If you have any issues please use the NVDA addon list mentioned above.
195 changes: 195 additions & 0 deletions sconstruct
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# NVDA add-on template SCONSTRUCT file
#Copyright (C) 2012, 2014 Rui Batista <[email protected]>
#This file is covered by the GNU General Public License.
#See the file COPYING.txt for more details.

import codecs
import gettext
import os
import os.path
import zipfile
import sys
sys.dont_write_bytecode = True

import buildVars

def md2html(source, dest):
import markdown
lang = os.path.basename(os.path.dirname(source)).replace('_', '-')
title="{addonSummary} {addonVersion}".format(addonSummary=buildVars.addon_info["addon_summary"], addonVersion=buildVars.addon_info["addon_version"])
headerDic = {
"[[!meta title=\"": "# ",
"\"]]": " #",
}
with codecs.open(source, "r", "utf-8") as f:
mdText = f.read()
for k, v in headerDic.items():
mdText = mdText.replace(k, v, 1)
htmlText = markdown.markdown(mdText)
with codecs.open(dest, "w", "utf-8") as f:
f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%s\" lang=\"%s\">\n" % (lang, lang) +
"<head>\n" +
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"../style.css\" media=\"screen\"/>\n" +
"<title>%s</title>\n" % title +
"</head>\n<body>\n"
)
f.write(htmlText)
f.write("\n</body>\n</html>")

def mdTool(env):
mdAction=env.Action(
lambda target,source,env: md2html(source[0].path, target[0].path),
lambda target,source,env: 'Generating %s'%target[0],
)
mdBuilder=env.Builder(
action=mdAction,
suffix='.html',
src_suffix='.md',
)
env['BUILDERS']['markdown']=mdBuilder

vars = Variables()
vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"])
vars.Add(BoolVariable("dev", "Whether this is a daily development version", False))
vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"])

env = Environment(variables=vars, ENV=os.environ, tools=['gettexttool', mdTool])
env.Append(**buildVars.addon_info)

if env["dev"]:
import datetime
buildDate = datetime.datetime.now()
year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day)
env["addon_version"] = "".join([year, month.zfill(2), day.zfill(2), "-dev"])
env["channel"] = "dev"
elif env["version"] is not None:
env["addon_version"] = env["version"]
if "channel" in env and env["channel"] is not None:
env["addon_updateChannel"] = env["channel"]

addonFile = env.File("${addon_name}-${addon_version}.nvda-addon")

def addonGenerator(target, source, env, for_signature):
action = env.Action(lambda target, source, env : createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None,
lambda target, source, env : "Generating Addon %s" % target[0])
return action

def manifestGenerator(target, source, env, for_signature):
action = env.Action(lambda target, source, env : generateManifest(source[0].abspath, target[0].abspath) and None,
lambda target, source, env : "Generating manifest %s" % target[0])
return action

def translatedManifestGenerator(target, source, env, for_signature):
dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), ".."))
lang = os.path.basename(dir)
action = env.Action(lambda target, source, env : generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) and None,
lambda target, source, env : "Generating translated manifest %s" % target[0])
return action

env['BUILDERS']['NVDAAddon'] = Builder(generator=addonGenerator)
env['BUILDERS']['NVDAManifest'] = Builder(generator=manifestGenerator)
env['BUILDERS']['NVDATranslatedManifest'] = Builder(generator=translatedManifestGenerator)

def createAddonHelp(dir):
docsDir = os.path.join(dir, "doc")
if os.path.isfile("style.css"):
cssPath = os.path.join(docsDir, "style.css")
cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE"))
env.Depends(addon, cssTarget)
if os.path.isfile("readme.md"):
readmePath = os.path.join(docsDir, "en", "readme.md")
readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE"))
env.Depends(addon, readmeTarget)

def createAddonBundleFromPath(path, dest):
""" Creates a bundle from a directory that contains an addon manifest file."""
basedir = os.path.abspath(path)
with zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED) as z:
# FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled.
for dir, dirnames, filenames in os.walk(basedir):
relativePath = os.path.relpath(dir, basedir)
for filename in filenames:
pathInBundle = os.path.join(relativePath, filename)
absPath = os.path.join(dir, filename)
if pathInBundle not in buildVars.excludedFiles: z.write(absPath, pathInBundle)
return dest

def generateManifest(source, dest):
addon_info = buildVars.addon_info
addon_info["addon_version"] = env["addon_version"]
addon_info["addon_updateChannel"] = env["addon_updateChannel"]
with codecs.open(source, "r", "utf-8") as f:
manifest_template = f.read()
manifest = manifest_template.format(**addon_info)
with codecs.open(dest, "w", "utf-8") as f:
f.write(manifest)

def generateTranslatedManifest(source, language, out):
# No ugettext in Python 3.
if sys.version_info.major == 2:
_ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).ugettext
else:
_ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).gettext
vars = {}
for var in ("addon_summary", "addon_description"):
vars[var] = _(buildVars.addon_info[var])
with codecs.open(source, "r", "utf-8") as f:
manifest_template = f.read()
result = manifest_template.format(**vars)
with codecs.open(out, "w", "utf-8") as f:
f.write(result)

def expandGlobs(files):
return [f for pattern in files for f in env.Glob(pattern)]

addon = env.NVDAAddon(addonFile, env.Dir('addon'))

langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))]

#Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated
for dir in langDirs:
poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po"))
moFile=env.gettextMoFile(poFile)
env.Depends(moFile, poFile)
translatedManifest = env.NVDATranslatedManifest(dir.File("manifest.ini"), [moFile, os.path.join("manifest-translated.ini.tpl")])
env.Depends(translatedManifest, ["buildVars.py"])
env.Depends(addon, [translatedManifest, moFile])

pythonFiles = expandGlobs(buildVars.pythonSources)
for file in pythonFiles:
env.Depends(addon, file)

#Convert markdown files to html
createAddonHelp("addon") # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager
for mdFile in env.Glob(os.path.join('addon', 'doc', '*', '*.md')):
htmlFile = env.markdown(mdFile)
env.Depends(htmlFile, mdFile)
env.Depends(addon, htmlFile)

# Pot target
i18nFiles = expandGlobs(buildVars.i18nSources)
gettextvars={
'gettext_package_bugs_address' : '[email protected]',
'gettext_package_name' : buildVars.addon_info['addon_name'],
'gettext_package_version' : buildVars.addon_info['addon_version']
}

pot = env.gettextPotFile("${addon_name}.pot", i18nFiles, **gettextvars)
env.Alias('pot', pot)
env.Depends(pot, i18nFiles)
mergePot = env.gettextMergePotFile("${addon_name}-merge.pot", i18nFiles, **gettextvars)
env.Alias('mergePot', mergePot)
env.Depends(mergePot, i18nFiles)

# Generate Manifest path
manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl"))
# Ensure manifest is rebuilt if buildVars is updated.
env.Depends(manifest, "buildVars.py")

env.Depends(addon, manifest)
env.Default(addon)
env.Clean (addon, ['.sconsign.dblite', 'addon/doc/en/'])
49 changes: 49 additions & 0 deletions site_scons/site_tools/gettexttool/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
""" This tool allows generation of gettext .mo compiled files, pot files from source code files
and pot files for merging.
Three new builders are added into the constructed environment:
- gettextMoFile: generates .mo file from .pot file using msgfmt.
- gettextPotFile: Generates .pot file from source code files.
- gettextMergePotFile: Creates a .pot file appropriate for merging into existing .po files.
To properly configure get text, define the following variables:
- gettext_package_bugs_address
- gettext_package_name
- gettext_package_version
"""
from SCons.Action import Action

def exists(env):
return True

XGETTEXT_COMMON_ARGS = (
"--msgid-bugs-address='$gettext_package_bugs_address' "
"--package-name='$gettext_package_name' "
"--package-version='$gettext_package_version' "
"-c -o $TARGET $SOURCES"
)

def generate(env):
env.SetDefault(gettext_package_bugs_address="[email protected]")
env.SetDefault(gettext_package_name="")
env.SetDefault(gettext_package_version="")

env['BUILDERS']['gettextMoFile']=env.Builder(
action=Action("msgfmt -o $TARGET $SOURCE", "Compiling translation $SOURCE"),
suffix=".mo",
src_suffix=".po"
)

env['BUILDERS']['gettextPotFile']=env.Builder(
action=Action("xgettext " + XGETTEXT_COMMON_ARGS, "Generating pot file $TARGET"),
suffix=".pot")

env['BUILDERS']['gettextMergePotFile']=env.Builder(
action=Action("xgettext " + "--omit-header --no-location " + XGETTEXT_COMMON_ARGS,
"Generating pot file $TARGET"),
suffix=".pot")

Loading

0 comments on commit 8eb49d4

Please sign in to comment.