|
@@ -2,24 +2,25 @@
|
|
|
|
|
|
############################################################################
|
|
|
#
|
|
|
-# MODULE: g.extension.add
|
|
|
+# MODULE: g.extension
|
|
|
# AUTHOR(S): Markus Neteler
|
|
|
# Pythonized by Martin Landa
|
|
|
-# PURPOSE: tool to download and install extensions from GRASS Addons SVN into
|
|
|
+# PURPOSE: Tool to download and install extensions from GRASS Addons SVN into
|
|
|
# local GRASS installation
|
|
|
-# COPYRIGHT: (C) 2009 by the Markus Neteler, GRASS Development Team
|
|
|
+# COPYRIGHT: (C) 2009-2010 by Markus Neteler, and the GRASS Development Team
|
|
|
#
|
|
|
-# This program is free software under the GNU General Public
|
|
|
-# License (>=v2). Read the file COPYING that comes with GRASS
|
|
|
-# for details.
|
|
|
+# This program is free software under the GNU General
|
|
|
+# Public License (>=v2). Read the file COPYING that
|
|
|
+# comes with GRASS for details.
|
|
|
#
|
|
|
# TODO: add sudo support where needed (i.e. check first permission to write into
|
|
|
# $GISBASE directory)
|
|
|
#############################################################################
|
|
|
|
|
|
#%module
|
|
|
-#% label: Tool to maintain GRASS extensions in local GRASS installation.
|
|
|
+#% label: Tool to maintain the extensions in local GRASS installation.
|
|
|
#% description: Downloads, installs extensions from GRASS Addons SVN repository into local GRASS installation or removes installed extensions.
|
|
|
+#% keywords: general
|
|
|
#% keywords: installation
|
|
|
#% keywords: extensions
|
|
|
#%end
|
|
@@ -34,7 +35,6 @@
|
|
|
#%option
|
|
|
#% key: operation
|
|
|
#% type: string
|
|
|
-#% key_desc: name
|
|
|
#% description: Operation to be performed
|
|
|
#% required: no
|
|
|
#% options: add,remove
|
|
@@ -46,7 +46,7 @@
|
|
|
#% key_desc: url
|
|
|
#% description: SVN Addons repository URL
|
|
|
#% required: yes
|
|
|
-#% answer: https://svn.osgeo.org/grass/grass-addons/grass7
|
|
|
+#% answer: https://svn.osgeo.org/grass/grass-addons
|
|
|
#%end
|
|
|
#%option
|
|
|
#% key: prefix
|
|
@@ -59,7 +59,6 @@
|
|
|
#%option
|
|
|
#% key: menuitem
|
|
|
#% type: string
|
|
|
-#% key_desc: name
|
|
|
#% label: Menu item in wxGUI
|
|
|
#% description: Given as string, e.g. 'Imagery;Filter image'
|
|
|
#% required: no
|
|
@@ -67,7 +66,17 @@
|
|
|
|
|
|
#%flag
|
|
|
#% key: l
|
|
|
-#% description: List available modules in the GRASS Addons SVN repository
|
|
|
+#% description: List available modules in the add-ons repository
|
|
|
+#% guisection: Print
|
|
|
+#%end
|
|
|
+#%flag
|
|
|
+#% key: f
|
|
|
+#% description: List available modules in the add-ons repository including modules description
|
|
|
+#% guisection: Print
|
|
|
+#%end
|
|
|
+#%flag
|
|
|
+#% key: g
|
|
|
+#% description: List available modules in the add-ons repository in shell script style
|
|
|
#% guisection: Print
|
|
|
#%end
|
|
|
|
|
@@ -108,21 +117,128 @@ def expand_module_class_name(c):
|
|
|
|
|
|
return c
|
|
|
|
|
|
-def list_available_modules(svnurl):
|
|
|
+def list_available_modules(svnurl, full = False, shell = False):
|
|
|
grass.message(_('Fetching list of modules from GRASS-Addons SVN (be patient)...'))
|
|
|
pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
|
|
|
- for d in ['d', 'db', 'g', 'i', 'ps',
|
|
|
- 'p', 'r', 'r3', 'v']:
|
|
|
+ i = 0
|
|
|
+ prefix = ['d', 'db', 'g', 'i', 'ps',
|
|
|
+ 'p', 'r', 'r3', 'v']
|
|
|
+ nprefix = len(prefix)
|
|
|
+ for d in prefix:
|
|
|
+ if shell:
|
|
|
+ grass.percent(i, nprefix, 1)
|
|
|
+ i += 1
|
|
|
+
|
|
|
modclass = expand_module_class_name(d)
|
|
|
url = svnurl + '/' + modclass
|
|
|
f = urllib.urlopen(url)
|
|
|
if not f:
|
|
|
grass.warning(_("Unable to fetch '%s'") % url)
|
|
|
continue
|
|
|
+
|
|
|
for line in f.readlines():
|
|
|
+ # list modules
|
|
|
sline = pattern.search(line)
|
|
|
if sline and sline.group(2).split('.', 1)[0] == d:
|
|
|
- print sline.group(2).rstrip('/')
|
|
|
+ name = sline.group(2).rstrip('/')
|
|
|
+ print_module_desc(name, url, full, shell)
|
|
|
+
|
|
|
+ if shell:
|
|
|
+ grass.percent(1, 1, 1)
|
|
|
+
|
|
|
+def print_module_desc(name, url, full = False, shell = False):
|
|
|
+ if not full and not shell:
|
|
|
+ print name
|
|
|
+ return
|
|
|
+
|
|
|
+ if shell:
|
|
|
+ print 'name=' + name
|
|
|
+
|
|
|
+ # check main.c first
|
|
|
+ desc = get_module_desc(url + '/' + name + '/' + name)
|
|
|
+ if not desc:
|
|
|
+ desc = get_module_desc(url + '/' + name + '/main.c', script = False)
|
|
|
+ if not desc:
|
|
|
+ if not shell:
|
|
|
+ print name + '-'
|
|
|
+ return
|
|
|
+
|
|
|
+ if shell:
|
|
|
+ print 'description=' + desc.get('description', '')
|
|
|
+ print 'keywords=' + ','.join(desc.get('keywords', list()))
|
|
|
+ else:
|
|
|
+ print name + ' - ' + desc.get('description', '')
|
|
|
+
|
|
|
+def get_module_desc(url, script = True):
|
|
|
+ grass.debug('url=%s' % url)
|
|
|
+ f = urllib.urlopen(url)
|
|
|
+ if script:
|
|
|
+ ret = get_module_script(f)
|
|
|
+ else:
|
|
|
+ ret = get_module_main(f)
|
|
|
+
|
|
|
+ return ret
|
|
|
+
|
|
|
+def get_module_main(f):
|
|
|
+ if not f:
|
|
|
+ return dict()
|
|
|
+
|
|
|
+ ret = { 'keyword' : list() }
|
|
|
+
|
|
|
+ pattern = re.compile(r'(module.*->)(.+)(=)(.*)', re.IGNORECASE)
|
|
|
+ keyword = re.compile(r'(G_add_keyword\()(.+)(\);)', re.IGNORECASE)
|
|
|
+
|
|
|
+ key = ''
|
|
|
+ value = ''
|
|
|
+ for line in f.readlines():
|
|
|
+ line = line.strip()
|
|
|
+ find = pattern.search(line)
|
|
|
+ if find:
|
|
|
+ key = find.group(2).strip()
|
|
|
+ line = find.group(4).strip()
|
|
|
+ else:
|
|
|
+ find = keyword.search(line)
|
|
|
+ if find:
|
|
|
+ ret['keyword'].append(find.group(2).replace('"', '').replace('_(', '').replace(')', ''))
|
|
|
+ if key:
|
|
|
+ value += line
|
|
|
+ if line[-2:] == ');':
|
|
|
+ value = value.replace('"', '').replace('_(', '').replace(');', '')
|
|
|
+ if key == 'keywords':
|
|
|
+ ret[key] = map(lambda x: x.strip(), value.split(','))
|
|
|
+ else:
|
|
|
+ ret[key] = value
|
|
|
+
|
|
|
+ key = value = ''
|
|
|
+
|
|
|
+ return ret
|
|
|
+
|
|
|
+def get_module_script(f):
|
|
|
+ ret = dict()
|
|
|
+ if not f:
|
|
|
+ return ret
|
|
|
+
|
|
|
+ begin = re.compile(r'#%.*module', re.IGNORECASE)
|
|
|
+ end = re.compile(r'#%.*end', re.IGNORECASE)
|
|
|
+ mline = None
|
|
|
+ for line in f.readlines():
|
|
|
+ if not mline:
|
|
|
+ mline = begin.search(line)
|
|
|
+ if mline:
|
|
|
+ if end.search(line):
|
|
|
+ break
|
|
|
+ try:
|
|
|
+ key, value = line.split(':', 1)
|
|
|
+ key = key.replace('#%', '').strip()
|
|
|
+ value = value.strip()
|
|
|
+ if key == 'keywords':
|
|
|
+ ret[key] = map(lambda x: x.strip(), value.split(','))
|
|
|
+ else:
|
|
|
+ ret[key] = value
|
|
|
+ except ValueError:
|
|
|
+ pass
|
|
|
+
|
|
|
+ return ret
|
|
|
|
|
|
def cleanup():
|
|
|
global tmpdir
|
|
@@ -202,8 +318,8 @@ def main():
|
|
|
check()
|
|
|
|
|
|
# list available modules
|
|
|
- if flags['l']:
|
|
|
- list_available_modules(options['svnurl'])
|
|
|
+ if flags['l'] or flags['f'] or flags['g']:
|
|
|
+ list_available_modules(options['svnurl'], full = flags['f'], shell = flags['g'])
|
|
|
return 0
|
|
|
else:
|
|
|
if not options['extension']:
|