g.extension.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #!/usr/bin/env python
  2. ############################################################################
  3. #
  4. # MODULE: g.extension.add
  5. # AUTHOR(S): Markus Neteler
  6. # Pythonized by Martin Landa
  7. # PURPOSE: tool to download and install extensions from GRASS Addons SVN into
  8. # local GRASS installation
  9. # COPYRIGHT: (C) 2009 by the Markus Neteler, GRASS Development Team
  10. #
  11. # This program is free software under the GNU General Public
  12. # License (>=v2). Read the file COPYING that comes with GRASS
  13. # for details.
  14. #
  15. # TODO: add sudo support where needed (i.e. check first permission to write into
  16. # $GISBASE directory)
  17. #############################################################################
  18. #%module
  19. #% label: Tool to maintain GRASS extensions in local GRASS installation.
  20. #% description: Downloads, installs extensions from GRASS Addons SVN repository into local GRASS installation or removes installed extensions.
  21. #% keywords: installation
  22. #% keywords: extensions
  23. #%end
  24. #%option
  25. #% key: extension
  26. #% type: string
  27. #% key_desc: name
  28. #% description: Name of extension to install/remove
  29. #% required: no
  30. #%end
  31. #%option
  32. #% key: operation
  33. #% type: string
  34. #% key_desc: name
  35. #% description: Operation to be performed
  36. #% required: no
  37. #% options: add,remove
  38. #% answer: add
  39. #%end
  40. #%option
  41. #% key: svnurl
  42. #% type: string
  43. #% key_desc: url
  44. #% description: SVN Addons repository URL
  45. #% required: yes
  46. #% answer: https://svn.osgeo.org/grass/grass-addons/grass7
  47. #%end
  48. #%option
  49. #% key: prefix
  50. #% type: string
  51. #% key_desc: path
  52. #% description: Prefix where to install extension
  53. #% answer: $(HOME)/.grass7/addons
  54. #% required: yes
  55. #%end
  56. #%option
  57. #% key: menuitem
  58. #% type: string
  59. #% key_desc: name
  60. #% label: Menu item in wxGUI
  61. #% description: Given as string, e.g. 'Imagery;Filter image'
  62. #% required: no
  63. #%end
  64. #%flag
  65. #% key: l
  66. #% description: List available modules in the GRASS Addons SVN repository
  67. #% guisection: Print
  68. #%end
  69. import os
  70. import sys
  71. import re
  72. import atexit
  73. import urllib
  74. from grass.script import core as grass
  75. # temp dir
  76. tmpdir = grass.tempfile()
  77. grass.try_remove(tmpdir)
  78. os.mkdir(tmpdir)
  79. def check():
  80. # check if we have the svn client
  81. if not grass.find_program('svn'):
  82. grass.fatal(_('svn client required. Please install subversion first.'))
  83. def expand_module_class_name(c):
  84. name = { 'd' : 'display',
  85. 'db' : 'database',
  86. 'g' : 'general',
  87. 'i' : 'imagery',
  88. 'm' : 'misc',
  89. 'ps' : 'postscript',
  90. 'p' : 'paint',
  91. 'r' : 'raster',
  92. 'r3' : 'raster3D',
  93. 's' : 'sites',
  94. 'v' : 'vector' }
  95. if name.has_key(c):
  96. return name[c]
  97. return c
  98. def list_available_modules(svnurl):
  99. grass.message(_('Fetching list of modules from GRASS-Addons SVN (be patient)...'))
  100. pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
  101. for d in ['d', 'db', 'g', 'i', 'ps',
  102. 'p', 'r', 'r3', 'v']:
  103. modclass = expand_module_class_name(d)
  104. url = svnurl + '/' + modclass
  105. f = urllib.urlopen(url)
  106. if not f:
  107. grass.warning(_("Unable to fetch '%s'") % url)
  108. continue
  109. for line in f.readlines():
  110. sline = pattern.search(line)
  111. if sline and sline.group(2).split('.', 1)[0] == d:
  112. print sline.group(2).rstrip('/')
  113. def cleanup():
  114. global tmpdir
  115. grass.try_rmdir(tmpdir)
  116. def install_extension(svnurl, prefix, module):
  117. gisbase = os.getenv('GISBASE')
  118. if not gisbase:
  119. grass.fatal(_('$GISBASE not defined'))
  120. if grass.find_program(module):
  121. grass.warning(_("Extension '%s' already installed. Will be updated...") % module)
  122. classchar = module.split('.', 1)[0]
  123. moduleclass = expand_module_class_name(classchar)
  124. url = svnurl + '/' + moduleclass + '/' + module
  125. grass.message(_("Fetching '%s' from GRASS-Addons SVN (be patient)...") % module)
  126. global tmpdir
  127. os.chdir(tmpdir)
  128. if grass.call(['svn', 'checkout',
  129. url]) != 0:
  130. grass.fatal(_("GRASS Addons '%s' not found in repository") % module)
  131. os.chdir(os.path.join(tmpdir, module))
  132. grass.message(_("Compiling '%s'...") % module)
  133. if grass.call(['make',
  134. 'MODULE_TOPDIR=%s' % gisbase]) != 0:
  135. grass.fatal(_('Compilation failed, sorry. Please check above error messages.'))
  136. grass.message(_("Installing '%s'...") % module)
  137. # can we write ?
  138. try:
  139. # replace with something better
  140. file = os.path.join(prefix, 'test')
  141. f = open(file, "w")
  142. f.close()
  143. os.remove(file)
  144. ret = grass.call(['make',
  145. 'MODULE_TOPDIR=%s' % gisbase,
  146. 'INST_DIR=%s' % prefix,
  147. 'install'])
  148. except IOError:
  149. ret = grass.call(['sudo', 'make',
  150. 'MODULE_TOPDIR=%s' % gisbase,
  151. 'INST_DIR=%s' % prefix,
  152. 'install'])
  153. if ret != 0:
  154. grass.warning(_('Installation failed, sorry. Please check above error messages.'))
  155. else:
  156. grass.message(_("Installation of '%s' successfully finished.") % module)
  157. def remove_extension(prefix, module):
  158. # is module available?
  159. if not grass.find_program(module):
  160. grass.fatal(_("'%s' not found") % module)
  161. for file in [os.path.join(prefix, 'bin', module),
  162. os.path.join(prefix, 'scripts', module),
  163. os.path.join(prefix, 'docs', 'html', module + '.html')]:
  164. if os.path.isfile(file):
  165. os.remove(file)
  166. grass.message(_("'%s' successfully uninstalled.") % module)
  167. def update_menu(menuitem, module, operation):
  168. grass.warning(_('Not implemented'))
  169. if operation == 'add':
  170. pass
  171. else: # remove
  172. pass
  173. def main():
  174. # check dependecies
  175. check()
  176. # list available modules
  177. if flags['l']:
  178. list_available_modules(options['svnurl'])
  179. return 0
  180. else:
  181. if not options['extension']:
  182. grass.fatal(_('You need to define an extension name or use -l'))
  183. # TODO: check more variable
  184. if '$(HOME)' in options['prefix']:
  185. options['prefix'] = options['prefix'].replace('$(HOME)', os.environ['HOME'])
  186. if not os.path.isdir(options['prefix']):
  187. try:
  188. os.makedirs(options['prefix'])
  189. except OSError, e:
  190. grass.fatal(_("Unable to create '%s'\n%s") % (options['prefix'], e))
  191. grass.warning(_("'%s' created") % options['prefix'])
  192. if options['operation'] == 'add':
  193. install_extension(options['svnurl'], options['prefix'], options['extension'])
  194. else: # remove
  195. remove_extension(options['prefix'], options['extension'])
  196. if options['menuitem']:
  197. update_menu(options['menuitem'], options['extension'], options['operation'])
  198. return 0
  199. if __name__ == "__main__":
  200. options, flags = grass.parser()
  201. atexit.register(cleanup)
  202. sys.exit(main())