109 from urllib2
import urlopen, HTTPError
112 import xml.etree.ElementTree
as etree
114 import elementtree.ElementTree
as etree
123 for prog
in (
'svn',
'make',
'gcc'):
124 if not grass.find_program(prog, [
'--help']):
125 grass.fatal(_(
"'%s' required. Please install '%s' first.") % (prog, prog))
129 name = {
'd' :
'display',
140 'gui' :
'gui/wxpython' }
150 fXML = os.path.join(options[
'prefix'],
'modules.xml')
151 if not os.path.exists(fXML):
155 grass.warning(_(
"No metadata file available"))
161 tree = etree.fromstring(fo.read())
169 for tnode
in tree.findall(
'task'):
170 ret.append(tnode.get(
'name').strip())
179 url =
"http://grass.osgeo.org/addons/grass%s/modules.xml" % grass.version()[
'version'].
split(
'.')[0]
180 grass.debug(
"url=%s" % url, 1)
184 tree = etree.fromstring(f.read())
186 grass.fatal(_(
"Unable to parse '%s'") % url)
188 for mnode
in tree.findall(
'task'):
189 name = mnode.get(
'name')
190 if flags[
'c']
or flags[
'g']:
191 desc = mnode.find(
'description').text
194 keyw = mnode.find(
'keywords').text
200 print 'description=' + desc
201 print 'keywords=' + keyw
203 print name +
' - ' + desc
214 grass.message(_(
'Fetching list of extensions from GRASS-Addons SVN (be patient)...'))
215 pattern = re.compile(
r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
218 grass.warning(_(
"Flag 'c' ignored, metadata file not available"))
220 grass.warning(_(
"Flag 'g' ignored, metadata file not available"))
222 prefix = [
'd',
'db',
'g',
'i',
'm',
'ps',
223 'p',
'r', 'r3', 's', 'v']
224 nprefix = len(prefix)
227 grass.verbose(_(
"Checking for '%s' modules...") % modclass)
229 url =
'%s/%s' % (options[
'svnurl'], modclass)
230 grass.debug(
"url = %s" % url, debug = 2)
234 grass.debug(_(
"Unable to fetch '%s'") % url, debug = 1)
237 for line
in f.readlines():
239 sline = pattern.search(line)
242 name = sline.group(2).rstrip(
'/')
243 if name.split(
'.', 1)[0] == d:
254 grass.debug(
'Fetching list of wxGUI extensions from GRASS-Addons SVN (be patient)...')
255 pattern = re.compile(
r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
256 grass.verbose(_(
"Checking for '%s' modules...") %
'gui/wxpython')
258 url =
'%s/%s' % (options[
'svnurl'],
'gui/wxpython')
259 grass.debug(
"url = %s" % url, debug = 2)
262 grass.warning(_(
"Unable to fetch '%s'") % url)
265 for line
in f.readlines():
267 sline = pattern.search(line)
270 name = sline.group(2).rstrip(
'/')
271 if name
not in (
'..',
'Makefile'):
280 grass.try_rmdir(tmpdir)
282 grass.message(_(
"Path to the source code:"))
283 sys.stderr.write(
'%s\n' % os.path.join(tmpdir, options[
'extension']))
288 version = grass.version()[
'version'].
split(
'.')[0]
289 fo.write(
'<?xml version="1.0" encoding="UTF-8"?>\n')
290 fo.write(
'<!DOCTYPE task SYSTEM "grass-addons.dtd">\n')
291 fo.write(
'<addons version="%s">\n' % version)
294 for tnode
in tree.findall(
'task'):
296 fo.write(
'%s<task name="%s">\n' % (
' ' * indent, tnode.get(
'name')))
298 fo.write(
'%s<description>%s</description>\n' % \
299 (
' ' * indent, tnode.find(
'description').text))
300 fo.write(
'%s<keywords>%s</keywords>\n' % \
301 (
' ' * indent, tnode.find(
'keywords').text))
302 bnode = tnode.find(
'binary')
303 if bnode
is not None:
304 fo.write(
'%s<binary>\n' % (
' ' * indent))
306 for fnode
in bnode.findall(
'file'):
307 fpath = fnode.text.split(os.path.sep)
309 if fpath[0]
in (
'bin',
'scripts'):
311 if fpath[0] ==
'man':
312 fpath.insert(0,
'docs')
314 fo.write(
'%s<file>%s</file>\n' % \
315 (
' ' * indent, os.path.join(options[
'prefix'],
316 os.path.sep.join(fpath))))
318 fo.write(
'%s</binary>\n' % (
' ' * indent))
319 libgisRev = grass.version()[
'libgis_revision']
320 fo.write(
'%s<libgis revision="%s" />\n' % \
321 (
' ' * indent, libgisRev))
323 fo.write(
'%s</task>\n' % (
' ' * indent))
325 fo.write(
'</addons>\n')
331 url =
"http://grass.osgeo.org/addons/grass%s/modules.xml" % grass.version()[
'version'].
split(
'.')[0]
335 tree = etree.fromstring(f.read())
336 for mnode
in tree.findall(
'task'):
337 name = mnode.get(
'name')
338 if name != options[
'extension']:
342 bnode = mnode.find(
'binary')
343 windows = sys.platform ==
'win32'
344 if bnode
is not None:
345 for fnode
in bnode.findall(
'file'):
346 path = fnode.text.split(
'/')
350 if path[0] ==
'scripts':
352 fList.append(os.path.sep.join(path))
354 desc = mnode.find(
'description').text
357 keyw = mnode.find(
'keywords').text
361 data = {
'name' : name,
367 grass.error(_(
"Unable to read metadata file from the remote server"))
370 grass.warning(_(
"No metadata available"))
373 fXML = os.path.join(options[
'prefix'],
'modules.xml')
375 if not os.path.exists(fXML):
380 tree = etree.fromstring(fo.read())
385 for node
in tree.findall(
'task'):
386 if node.get(
'name') == options[
'extension']:
390 if tnode
is not None:
392 dnode = tnode.find(
'description')
393 if dnode
is not None:
394 dnode.text = data[
'desc']
395 knode = tnode.find(
'keywords')
396 if knode
is not None:
397 knode.text = data[
'keyw']
398 bnode = tnode.find(
'binary')
399 if bnode
is not None:
401 bnode = etree.Element(
'binary')
402 for f
in data[
'files']:
403 fnode = etree.Element(
'file')
409 tnode = etree.Element(
'task', attrib = {
'name' : data[
'name'] })
410 dnode = etree.Element(
'description')
411 dnode.text = data[
'desc']
413 knode = etree.Element(
'keywords')
414 knode.text = data[
'keyw']
416 bnode = etree.Element(
'binary')
417 for f
in data[
'files']:
418 fnode = etree.Element(
'file')
429 version = grass.version()[
'version'].
split(
'.')
430 grass.message(_(
"Downloading precompiled GRASS Addons <%s>...") % options[
'extension'])
431 url =
"http://wingrass.fsv.cvut.cz/grass%s%s/addons/grass-%s.%s.%s" % \
432 (version[0], version[1], version[0], version[1], version[2])
433 grass.debug(
"url=%s" % url, 1)
436 f = urlopen(url +
'/' + options[
'extension'] +
'.zip')
439 if not os.path.exists(options[
'prefix']):
440 os.mkdir(options[
'prefix'])
443 fo = tempfile.TemporaryFile()
445 zfobj = zipfile.ZipFile(fo)
446 for name
in zfobj.namelist():
447 if name.endswith(
'/'):
448 d = os.path.join(options[
'prefix'], name)
449 if not os.path.exists(d):
452 outfile = open(os.path.join(options[
'prefix'], name),
'wb')
453 outfile.write(zfobj.read(name))
458 grass.fatal(_(
"GRASS Addons <%s> not found") % options[
'extension'])
464 gisbase = os.getenv(
'GISBASE')
466 grass.fatal(_(
'$GISBASE not defined'))
469 grass.warning(_(
"Extension <%s> already installed. Re-installing...") % options[
'extension'])
471 if sys.platform ==
"win32":
477 grass.warning(_(
'Installation failed, sorry. Please check above error messages.'))
479 grass.message(_(
"Updating metadata file..."))
481 grass.message(_(
"Installation of <%s> successfully finished") % options[
'extension'])
487 if not os.environ.has_key(
'GRASS_ADDON_PATH')
or \
488 not os.environ[
'GRASS_ADDON_PATH']:
489 grass.warning(_(
'This add-on module will not function until you set the '
490 'GRASS_ADDON_PATH environment variable (see "g.manual variables")'))
494 gisbase = os.getenv(
'GISBASE')
497 if options[
'extension']
not in gui_list:
498 classchar = options[
'extension'].
split(
'.', 1)[0]
500 url = options[
'svnurl'] +
'/' + moduleclass +
'/' + options[
'extension']
502 url = options[
'svnurl'] +
'/gui/wxpython/' + options[
'extension']
504 grass.fatal(_(
"Installation of wxGUI extension requires -%s flag.") %
's')
506 grass.message(_(
"Fetching <%s> from GRASS-Addons SVN (be patient)...") % options[
'extension'])
509 if grass.verbosity() <= 2:
510 outdev = open(os.devnull,
'w')
514 if grass.call([
'svn',
'checkout',
515 url], stdout = outdev) != 0:
516 grass.fatal(_(
"GRASS Addons <%s> not found") % options[
'extension'])
518 dirs = {
'bin' : os.path.join(tmpdir, options[
'extension'],
'bin'),
519 'docs' : os.path.join(tmpdir, options[
'extension'],
'docs'),
520 'html' : os.path.join(tmpdir, options[
'extension'],
'docs',
'html'),
521 'man' : os.path.join(tmpdir, options[
'extension'],
'man'),
522 'man1' : os.path.join(tmpdir, options[
'extension'],
'man',
'man1'),
523 'scripts' : os.path.join(tmpdir, options[
'extension'],
'scripts'),
524 'etc' : os.path.join(tmpdir, options[
'extension'],
'etc'),
528 'MODULE_TOPDIR=%s' % gisbase.replace(
' ',
'\ '),
529 'BIN=%s' % dirs[
'bin'],
530 'HTMLDIR=%s' % dirs[
'html'],
531 'MANDIR=%s' % dirs[
'man1'],
532 'SCRIPTDIR=%s' % dirs[
'scripts'],
533 'ETC=%s' % os.path.join(dirs[
'etc'],options[
'extension'])
536 installCmd = [
'make',
537 'MODULE_TOPDIR=%s' % gisbase,
538 'ARCH_DISTDIR=%s' % os.path.join(tmpdir, options[
'extension']),
539 'INST_DIR=%s' % options[
'prefix'],
544 grass.message(_(
"To compile run:"))
545 sys.stderr.write(
' '.join(makeCmd) +
'\n')
546 grass.message(_(
"To install run:\n\n"))
547 sys.stderr.write(
' '.join(installCmd) +
'\n')
550 os.chdir(os.path.join(tmpdir, options[
'extension']))
552 grass.message(_(
"Compiling..."))
553 if options[
'extension']
not in gui_list:
554 ret = grass.call(makeCmd,
557 ret = grass.call([
'make',
558 'MODULE_TOPDIR=%s' % gisbase.replace(
' ',
'\ ')],
562 grass.fatal(_(
'Compilation failed, sorry. Please check above error messages.'))
564 if flags[
'i']
or options[
'extension']
in gui_list:
567 grass.message(_(
"Installing..."))
569 return grass.call(installCmd,
574 if os.path.exists(path)
and not os.listdir(path):
579 if sys.platform ==
'win32':
583 EXT_BIN = EXT_SCT =
''
586 if os.path.exists(os.path.join(options[
'prefix'],
'bin', options[
'extension']) + EXT_BIN):
587 shutil.move(os.path.join(options[
'prefix'],
'bin', options[
'extension']) + EXT_BIN,
588 os.path.join(options[
'prefix'], options[
'extension']) + EXT_BIN)
589 if os.path.exists(os.path.join(options[
'prefix'],
'scripts', options[
'extension'])):
590 shutil.move(os.path.join(options[
'prefix'],
'scripts', options[
'extension']),
591 os.path.join(options[
'prefix'], options[
'extension']))
592 if os.path.exists(os.path.join(options[
'prefix'],
'scripts', options[
'extension'] +
'.py')):
593 shutil.move(os.path.join(options[
'prefix'],
'scripts', options[
'extension'] +
'.py'),
594 os.path.join(options[
'prefix'], options[
'extension'] +
'.py'))
595 if sys.platform ==
'win32' and \
596 os.path.exists(os.path.join(options[
'prefix'],
'bin', options[
'extension']) + EXT_SCT):
597 shutil.move(os.path.join(options[
'prefix'],
'bin', options[
'extension']) + EXT_SCT,
598 os.path.join(options[
'prefix'], options[
'extension']) + EXT_SCT)
600 for line
in fileinput.FileInput(os.path.join(options[
'prefix'], options[
'extension']) + EXT_SCT,
602 line = line.replace(
"/scripts",
"")
606 if os.path.exists(os.path.join(options[
'prefix'],
'man',
'man1', options[
'extension'] +
'.1')):
607 shutil.move(os.path.join(options[
'prefix'],
'man',
'man1', options[
'extension'] +
'.1'),
608 os.path.join(options[
'prefix'],
'docs',
'man',
'man1', options[
'extension'] +
'.1'))
611 for d
in (
'bin',
'etc', os.path.join(
'man',
'man1'),
'man',
'scripts'):
616 fXML = os.path.join(options[
'prefix'],
'modules.xml')
617 if not os.path.exists(fXML):
622 tree = etree.fromstring(fo.read())
626 for node
in tree.findall(
'task'):
627 if node.get(
'name') == options[
'extension']:
631 if tnode
is not None:
639 fXML = os.path.join(options[
'prefix'],
'modules.xml')
640 name = options[
'extension']
642 grass.warning(_(
"Extension <%s> not found") % name)
645 grass.verbose(_(
"List of removed files:"))
647 grass.info(_(
"Files to be removed (use flag 'f' to force removal):"))
649 if os.path.exists(fXML):
651 tree = etree.fromstring(f.read())
653 for task
in tree.findall(
'task'):
654 if name == task.get(
'name', default =
'')
and \
655 task.find(
'binary')
is not None:
656 for f
in task.find(
'binary').findall(
'file'):
671 err.append((_(
"Unable to remove file '%s'") % fpath))
672 if force
and not removed:
673 grass.fatal(_(
"Extension <%s> not found") % options[
'extension'])
684 grass.message(_(
"Updating metadata file..."))
686 grass.message(_(
"Extension <%s> successfully uninstalled.") % options[
'extension'])
688 grass.warning(_(
"Extension <%s> not removed.\n"
689 "Re-run '%s' with 'f' flag to force removal") % (options[
'extension'],
'g.extension'))
695 for fpath
in [os.path.join(options[
'prefix'], options[
'extension']),
696 os.path.join(options[
'prefix'],
'bin', options[
'extension']),
697 os.path.join(options[
'prefix'],
'scripts', options[
'extension']),
698 os.path.join(options[
'prefix'],
'docs',
'html', options[
'extension'] +
'.html'),
699 os.path.join(options[
'prefix'],
'docs',
'man',
'man1', options[
'extension'] +
'.1'),
700 os.path.join(options[
'prefix'],
'man',
'man1', options[
'extension'] +
'.1')]:
701 if os.path.isfile(fpath):
710 dist_file = os.path.join(os.getenv(
'GISBASE'),
'docs',
'html', fil)
711 addons_file = os.path.join(options[
'prefix'],
'docs',
'html', fil)
713 if os.path.isfile(addons_file):
717 shutil.copyfile(dist_file,addons_file)
719 grass.fatal(_(
"Unable to create '%s': %s") % (addons_file, e))
722 if os.path.isdir(path):
728 grass.fatal(_(
"Unable to create '%s': %s") % (path, e))
730 grass.debug(
"'%s' created" % path)
733 create_dir(os.path.join(options[
'prefix'],
'bin'))
734 create_dir(os.path.join(options[
'prefix'],
'docs',
'html'))
738 create_dir(os.path.join(options[
'prefix'],
'docs',
'man',
'man1'))
739 create_dir(os.path.join(options[
'prefix'],
'man',
'man1'))
740 create_dir(os.path.join(options[
'prefix'],
'scripts'))
744 if sys.platform !=
"win32":
749 options[
'prefix'] = os.environ[
'GISBASE']
750 if options[
'prefix'] ==
'$GRASS_ADDON_PATH':
751 if not os.environ.has_key(
'GRASS_ADDON_PATH')
or \
752 not os.environ[
'GRASS_ADDON_PATH']:
753 major_version = int(grass.version()[
'version'].
split(
'.', 1)[0])
754 grass.warning(_(
"GRASS_ADDON_PATH is not defined, "
755 "installing to ~/.grass%d/addons/") % major_version)
756 options[
'prefix'] = os.path.join(os.environ[
'HOME'],
'.grass%d' % major_version,
'addons')
758 path_list = os.environ[
'GRASS_ADDON_PATH'].
split(os.pathsep)
759 if len(path_list) < 1:
760 grass.fatal(_(
"Invalid GRASS_ADDON_PATH value - '%s'") % os.environ[
'GRASS_ADDON_PATH'])
761 if len(path_list) > 1:
762 grass.warning(_(
"GRASS_ADDON_PATH has more items, using first defined - '%s'") % path_list[0])
763 options[
'prefix'] = path_list[0]
766 if flags[
'l']
or flags[
'c']
or flags[
'g']:
772 grass.message(_(
"List of installed extensions:"))
773 sys.stdout.write(
'\n'.join(elist))
774 sys.stdout.write(
'\n')
776 grass.info(_(
"No extension installed"))
779 if not options[
'extension']:
780 grass.fatal(_(
'You need to define an extension name or use -l'))
783 if options[
'operation'] !=
'add':
784 grass.warning(_(
"Flag 'd' is relevant only to 'operation=add'. Ignoring this flag."))
787 remove_tmpdir =
False
789 if options[
'operation'] ==
'add':
797 if __name__ ==
"__main__":
798 options, flags = grass.parser()
800 tmpdir = grass.tempdir()
801 atexit.register(cleanup)