g.extension.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. #!/usr/bin/env python
  2. ############################################################################
  3. #
  4. # MODULE: g.extension
  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-2011 by Markus Neteler, and the GRASS Development Team
  10. #
  11. # This program is free software under the GNU General
  12. # Public License (>=v2). Read the file COPYING that
  13. # comes with GRASS 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 the 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: general
  22. #% keywords: installation
  23. #% keywords: extensions
  24. #%end
  25. #%option
  26. #% key: extension
  27. #% type: string
  28. #% key_desc: name
  29. #% description: Name of extension to install/remove
  30. #% required: yes
  31. #%end
  32. #%option
  33. #% key: operation
  34. #% type: string
  35. #% description: Operation to be performed
  36. #% required: yes
  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. #% answer: http://svn.osgeo.org/grass/grass-addons/grass7
  46. #%end
  47. #%option
  48. #% key: prefix
  49. #% type: string
  50. #% key_desc: path
  51. #% description: Prefix where to install extension (ignored when flag -s is given)
  52. #% answer: $GRASS_ADDON_BASE
  53. #% required: no
  54. #%end
  55. #%flag
  56. #% key: l
  57. #% description: List available extensions in the GRASS Addons SVN repository
  58. #% guisection: Print
  59. #% suppress_required: yes
  60. #%end
  61. #%flag
  62. #% key: c
  63. #% description: List available extensions in the GRASS Addons SVN repository including module description
  64. #% guisection: Print
  65. #% suppress_required: yes
  66. #%end
  67. #%flag
  68. #% key: g
  69. #% description: List available extensions in the GRASS Addons SVN repository (shell script style)
  70. #% guisection: Print
  71. #% suppress_required: yes
  72. #%end
  73. #%flag
  74. #% key: a
  75. #% description: List locally installed extensions
  76. #% guisection: Print
  77. #% suppress_required: yes
  78. #%end
  79. #%flag
  80. #% key: s
  81. #% description: Install system-wide (may need system administrator rights)
  82. #% guisection: Install
  83. #%end
  84. #%flag
  85. #% key: d
  86. #% description: Download source code and exit
  87. #% guisection: Install
  88. #%end
  89. #%flag
  90. #% key: i
  91. #% description: Don't install new extension, just compile it
  92. #% guisection: Install
  93. #%end
  94. #%flag
  95. #% key: f
  96. #% description: Force removal when uninstalling extension (operation=remove)
  97. #% guisection: Remove
  98. #%end
  99. import os
  100. import sys
  101. import re
  102. import atexit
  103. import shutil
  104. import glob
  105. import zipfile
  106. import tempfile
  107. import shutil
  108. from urllib2 import urlopen, HTTPError
  109. try:
  110. import xml.etree.ElementTree as etree
  111. except ImportError:
  112. import elementtree.ElementTree as etree # Python <= 2.4
  113. from grass.script import core as grass
  114. # temp dir
  115. remove_tmpdir = True
  116. # check requirements
  117. def check_progs():
  118. for prog in ('svn', 'make', 'gcc'):
  119. if not grass.find_program(prog, ['--help']):
  120. grass.fatal(_("'%s' required. Please install '%s' first.") % (prog, prog))
  121. # expand prefix to class name
  122. def expand_module_class_name(c):
  123. name = { 'd' : 'display',
  124. 'db' : 'database',
  125. 'g' : 'general',
  126. 'i' : 'imagery',
  127. 'm' : 'misc',
  128. 'ps' : 'postscript',
  129. 'p' : 'paint',
  130. 'r' : 'raster',
  131. 'r3' : 'raster3d',
  132. 's' : 'sites',
  133. 'v' : 'vector',
  134. 'gui' : 'gui/wxpython' }
  135. if name.has_key(c):
  136. return name[c]
  137. return c
  138. # list installed extensions
  139. def get_installed_extensions(force = False):
  140. fXML = os.path.join(options['prefix'], 'modules.xml')
  141. if not os.path.exists(fXML):
  142. if force:
  143. write_xml_modules(fXML)
  144. else:
  145. grass.warning(_("No metadata file available"))
  146. return []
  147. # read XML file
  148. fo = open(fXML, 'r')
  149. try:
  150. tree = etree.fromstring(fo.read())
  151. except:
  152. os.remove(fXML)
  153. write_xml_modules(fXML)
  154. return []
  155. fo.close()
  156. ret = list()
  157. for tnode in tree.findall('task'):
  158. ret.append(tnode.get('name'))
  159. return ret
  160. # list extensions (read XML file from grass.osgeo.org/addons)
  161. def list_available_extensions():
  162. mlist = list()
  163. # try to download XML metadata file first
  164. url = "http://grass.osgeo.org/addons/grass%s.xml" % grass.version()['version'].split('.')[0]
  165. try:
  166. f = urlopen(url)
  167. tree = etree.fromstring(f.read())
  168. for mnode in tree.findall('task'):
  169. name = mnode.get('name')
  170. if flags['c'] or flags['g']:
  171. desc = mnode.find('description').text
  172. if not desc:
  173. desc = ''
  174. keyw = mnode.find('keywords').text
  175. if not keyw:
  176. keyw = ''
  177. if flags['g']:
  178. print 'name=' + name
  179. print 'description=' + desc
  180. print 'keywords=' + keyw
  181. elif flags['c']:
  182. print name + ' - ' + desc
  183. else:
  184. print name
  185. except HTTPError:
  186. return list_available_extensions_svn()
  187. return mlist
  188. # list extensions (scan SVN repo)
  189. def list_available_extensions_svn():
  190. mlist = list()
  191. grass.message(_('Fetching list of extensions from GRASS-Addons SVN (be patient)...'))
  192. pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
  193. if flags['c']:
  194. grass.warning(_("Flag 'c' ignored, metadata file not available"))
  195. if flags['g']:
  196. grass.warning(_("Flag 'g' ignored, metadata file not available"))
  197. prefix = ['d', 'db', 'g', 'i', 'm', 'ps',
  198. 'p', 'r', 'r3', 's', 'v']
  199. nprefix = len(prefix)
  200. for d in prefix:
  201. modclass = expand_module_class_name(d)
  202. grass.verbose(_("Checking for '%s' modules...") % modclass)
  203. url = '%s/%s' % (options['svnurl'], modclass)
  204. grass.debug("url = %s" % url, debug = 2)
  205. try:
  206. f = urlopen(url)
  207. except HTTPError:
  208. grass.debug(_("Unable to fetch '%s'") % url, debug = 1)
  209. continue
  210. for line in f.readlines():
  211. # list extensions
  212. sline = pattern.search(line)
  213. if not sline:
  214. continue
  215. name = sline.group(2).rstrip('/')
  216. if name.split('.', 1)[0] == d:
  217. print name
  218. mlist.append(name)
  219. mlist += list_wxgui_extensions()
  220. return mlist
  221. # list wxGUI extensions
  222. def list_wxgui_extensions(print_module = True):
  223. mlist = list()
  224. grass.debug('Fetching list of wxGUI extensions from GRASS-Addons SVN (be patient)...')
  225. pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
  226. grass.verbose(_("Checking for '%s' modules...") % 'gui/wxpython')
  227. url = '%s/%s' % (options['svnurl'], 'gui/wxpython')
  228. grass.debug("url = %s" % url, debug = 2)
  229. f = urlopen(url)
  230. if not f:
  231. grass.warning(_("Unable to fetch '%s'") % url)
  232. return
  233. for line in f.readlines():
  234. # list extensions
  235. sline = pattern.search(line)
  236. if not sline:
  237. continue
  238. name = sline.group(2).rstrip('/')
  239. if name not in ('..', 'Makefile'):
  240. if print_module:
  241. print name
  242. mlist.append(name)
  243. return mlist
  244. def cleanup():
  245. if remove_tmpdir:
  246. grass.try_rmdir(tmpdir)
  247. else:
  248. grass.message(_("Path to the source code:"))
  249. sys.stderr.write('%s\n' % os.path.join(tmpdir, options['extension']))
  250. # write out meta-file
  251. def write_xml_modules(name, tree = None):
  252. fo = open(name, 'w')
  253. version = grass.version()['version'].split('.')[0]
  254. fo.write('<?xml version="1.0" encoding="UTF-8"?>\n')
  255. fo.write('<!DOCTYPE task SYSTEM "grass-addons.dtd">\n')
  256. fo.write('<addons version="%s">\n' % version)
  257. if tree is not None:
  258. for tnode in tree.findall('task'):
  259. indent = 4
  260. fo.write('%s<task name="%s">\n' % (' ' * indent, tnode.get('name')))
  261. indent += 4
  262. fo.write('%s<description>%s</description>\n' % \
  263. (' ' * indent, tnode.find('description').text))
  264. fo.write('%s<keywords>%s</keywords>\n' % \
  265. (' ' * indent, tnode.find('keywords').text))
  266. bnode = tnode.find('binary')
  267. if bnode is not None:
  268. fo.write('%s<binary>\n' % (' ' * indent))
  269. indent += 4
  270. for fnode in bnode.findall('file'):
  271. fo.write('%s<file>%s</file>\n' % \
  272. (' ' * indent, os.path.join(options['prefix'], fnode.text)))
  273. indent -= 4
  274. fo.write('%s</binary>\n' % (' ' * indent))
  275. libgisRev = grass.version()['libgis_revision']
  276. fo.write('%s<libgis revision="%s" />\n' % \
  277. (' ' * indent, libgisRev))
  278. indent -= 4
  279. fo.write('%s</task>\n' % (' ' * indent))
  280. fo.write('</addons>\n')
  281. fo.close()
  282. # update local meta-file when installing new extension
  283. def install_extension_xml():
  284. # read metadata from remote server
  285. url = "http://grass.osgeo.org/addons/grass%s.xml" % grass.version()['version'].split('.')[0]
  286. data = None
  287. try:
  288. f = urlopen(url)
  289. tree = etree.fromstring(f.read())
  290. for mnode in tree.findall('task'):
  291. name = mnode.get('name')
  292. if name != options['extension']:
  293. continue
  294. fList = list()
  295. bnode = mnode.find('binary')
  296. windows = sys.platform == 'win32'
  297. if bnode is not None:
  298. for fnode in bnode.findall('file'):
  299. path = fnode.text.split('/')
  300. if windows:
  301. if path[0] == 'bin':
  302. path[-1] += '.exe'
  303. if path[0] == 'scripts':
  304. path[-1] += '.py'
  305. fList.append(os.path.sep.join(path))
  306. desc = mnode.find('description').text
  307. if not desc:
  308. desc = ''
  309. keyw = mnode.find('keywords').text
  310. if not keyw:
  311. keyw = ''
  312. data = { 'name' : name,
  313. 'desc' : desc,
  314. 'keyw' : keyw,
  315. 'files' : fList,
  316. }
  317. except HTTPError:
  318. grass.error(_("Unable to read metadata file from the remote server"))
  319. if not data:
  320. grass.warning(_("No metadata available"))
  321. return
  322. fXML = os.path.join(options['prefix'], 'modules.xml')
  323. # create an empty file if not exists
  324. if not os.path.exists(fXML):
  325. write_xml_modules(fXML)
  326. # read XML file
  327. fo = open(fXML, 'r')
  328. tree = etree.fromstring(fo.read())
  329. fo.close()
  330. # update tree
  331. tnode = None
  332. for node in tree.findall('task'):
  333. if node.get('name') == options['extension']:
  334. tnode = node
  335. break
  336. if tnode is not None:
  337. # update existing node
  338. dnode = tnode.find('description')
  339. if dnode is not None:
  340. dnode.text = data['desc']
  341. knode = tnode.find('keywords')
  342. if knode is not None:
  343. knode.text = data['keyw']
  344. bnode = tnode.find('binary')
  345. if bnode is not None:
  346. tnode.remove(bnode)
  347. bnode = etree.Element('binary')
  348. for f in data['files']:
  349. fnode = etree.Element('file')
  350. fnode.text = f
  351. bnode.append(fnode)
  352. tnode.append(bnode)
  353. else:
  354. # create new node for task
  355. tnode = etree.Element('task', attrib = { 'name' : data['name'] })
  356. dnode = etree.Element('description')
  357. dnode.text = data['desc']
  358. tnode.append(dnode)
  359. knode = etree.Element('keywords')
  360. knode.text = data['keyw']
  361. tnode.append(knode)
  362. bnode = etree.Element('binary')
  363. for f in data['files']:
  364. fnode = etree.Element('file')
  365. fnode.text = f
  366. bnode.append(fnode)
  367. tnode.append(bnode)
  368. tree.append(tnode)
  369. write_xml_modules(fXML, tree)
  370. # install extension on MS Windows
  371. def install_extension_win():
  372. ### TODO: do not use hardcoded url
  373. version = grass.version()['version'].split('.')
  374. url = "http://wingrass.fsv.cvut.cz/grass%s%s/addons/" % (version[0], version[1])
  375. grass.message(_("Downloading precompiled GRASS Addons <%s>...") % options['extension'])
  376. try:
  377. f = urlopen(url + options['extension'] + '.zip')
  378. # create addons dir if not exists
  379. if not os.path.exists(options['prefix']):
  380. os.mkdir(options['prefix'])
  381. # download data
  382. fo = tempfile.TemporaryFile()
  383. fo.write(f.read())
  384. zfobj = zipfile.ZipFile(fo)
  385. for name in zfobj.namelist():
  386. if name.endswith('/'):
  387. d = os.path.join(options['prefix'], name)
  388. if not os.path.exists(d):
  389. os.mkdir(d)
  390. else:
  391. outfile = open(os.path.join(options['prefix'], name), 'wb')
  392. outfile.write(zfobj.read(name))
  393. outfile.close()
  394. fo.close()
  395. except HTTPError:
  396. grass.fatal(_("GRASS Addons <%s> not found") % options['extension'])
  397. return 0
  398. # install extension
  399. def install_extension():
  400. gisbase = os.getenv('GISBASE')
  401. if not gisbase:
  402. grass.fatal(_('$GISBASE not defined'))
  403. if options['extension'] in get_installed_extensions(force = True):
  404. grass.warning(_("Extension <%s> already installed. Re-installing...") % options['extension'])
  405. if sys.platform == "win32":
  406. ret = install_extension_win()
  407. else:
  408. ret = install_extension_other()
  409. if ret != 0:
  410. grass.warning(_('Installation failed, sorry. Please check above error messages.'))
  411. else:
  412. grass.message(_("Updating metadata file..."))
  413. install_extension_xml()
  414. grass.message(_("Installation of <%s> successfully finished") % options['extension'])
  415. if not os.getenv('GRASS_ADDON_BASE'):
  416. grass.warning(_('This add-on module will not function until you set the '
  417. 'GRASS_ADDON_BASE environment variable (see "g.manual variables")'))
  418. # install extension on other plaforms
  419. def install_extension_other():
  420. gisbase = os.getenv('GISBASE')
  421. gui_list = list_wxgui_extensions(print_module = False)
  422. if options['extension'] not in gui_list:
  423. classchar = options['extension'].split('.', 1)[0]
  424. moduleclass = expand_module_class_name(classchar)
  425. url = options['svnurl'] + '/' + moduleclass + '/' + options['extension']
  426. else:
  427. url = options['svnurl'] + '/gui/wxpython/' + options['extension']
  428. if not flags['s']:
  429. grass.fatal(_("Installation of wxGUI extension requires -%s flag.") % 's')
  430. grass.message(_("Fetching <%s> from GRASS-Addons SVN (be patient)...") % options['extension'])
  431. os.chdir(tmpdir)
  432. if grass.verbosity() <= 2:
  433. outdev = open(os.devnull, 'w')
  434. else:
  435. outdev = sys.stdout
  436. if grass.call(['svn', 'checkout',
  437. url], stdout = outdev) != 0:
  438. grass.fatal(_("GRASS Addons <%s> not found") % options['extension'])
  439. dirs = { 'bin' : os.path.join(tmpdir, options['extension'], 'bin'),
  440. 'docs' : os.path.join(tmpdir, options['extension'], 'docs'),
  441. 'html' : os.path.join(tmpdir, options['extension'], 'docs', 'html'),
  442. 'man' : os.path.join(tmpdir, options['extension'], 'man'),
  443. 'man1' : os.path.join(tmpdir, options['extension'], 'man', 'man1'),
  444. 'scripts' : os.path.join(tmpdir, options['extension'], 'scripts'),
  445. 'etc' : os.path.join(tmpdir, options['extension'], 'etc'),
  446. }
  447. makeCmd = ['make',
  448. 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ '),
  449. 'BIN=%s' % dirs['bin'],
  450. 'HTMLDIR=%s' % dirs['html'],
  451. 'MANDIR=%s' % dirs['man1'],
  452. 'SCRIPTDIR=%s' % dirs['scripts'],
  453. 'ETC=%s' % os.path.join(dirs['etc'],options['extension'])
  454. ]
  455. installCmd = ['make',
  456. 'MODULE_TOPDIR=%s' % gisbase,
  457. 'ARCH_DISTDIR=%s' % os.path.join(tmpdir, options['extension']),
  458. 'INST_DIR=%s' % options['prefix'],
  459. 'install'
  460. ]
  461. if flags['d']:
  462. grass.message(_("To compile run:"))
  463. sys.stderr.write(' '.join(makeCmd) + '\n')
  464. grass.message(_("To install run:\n\n"))
  465. sys.stderr.write(' '.join(installCmd) + '\n')
  466. return
  467. os.chdir(os.path.join(tmpdir, options['extension']))
  468. grass.message(_("Compiling..."))
  469. if options['extension'] not in gui_list:
  470. ret = grass.call(makeCmd,
  471. stdout = outdev)
  472. else:
  473. ret = grass.call(['make',
  474. 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ ')],
  475. stdout = outdev)
  476. if ret != 0:
  477. grass.fatal(_('Compilation failed, sorry. Please check above error messages.'))
  478. if flags['i'] or options['extension'] in gui_list:
  479. return
  480. grass.message(_("Installing..."))
  481. return grass.call(installCmd,
  482. stdout = outdev)
  483. # update local meta-file when removing existing extension
  484. def remove_extension_xml():
  485. fXML = os.path.join(options['prefix'], 'modules.xml')
  486. if not os.path.exists(fXML):
  487. return
  488. # read XML file
  489. fo = open(fXML, 'r')
  490. tree = etree.fromstring(fo.read())
  491. fo.close()
  492. tnode = None
  493. for node in tree.findall('task'):
  494. if node.get('name') == options['extension']:
  495. tnode = node
  496. break
  497. if tnode is not None:
  498. tree.remove(tnode)
  499. write_xml_modules(fXML, tree)
  500. # remove existing extension (reading XML file)
  501. def remove_extension(force = False):
  502. # try to read XML metadata file first
  503. fXML = os.path.join(options['prefix'], 'modules.xml')
  504. name = options['extension']
  505. if name not in get_installed_extensions():
  506. # try even if module does not seem to be available,
  507. # as the user may be trying to get rid of left over cruft
  508. grass.warning(_("Extension <%s> not found") % name)
  509. if force:
  510. grass.verbose(_("List of removed files:"))
  511. else:
  512. grass.info(_("Files to be removed (use flag 'f' to force removal):"))
  513. if os.path.exists(fXML):
  514. f = open(fXML, 'r')
  515. tree = etree.fromstring(f.read())
  516. flist = []
  517. for task in tree.findall('task'):
  518. if name == task.get('name', default = '') and \
  519. task.find('binary') is not None:
  520. for f in task.find('binary').findall('file'):
  521. flist.append(f.text)
  522. if flist:
  523. removed = False
  524. err = list()
  525. for fpath in flist:
  526. try:
  527. if force:
  528. grass.verbose(fpath)
  529. os.remove(fpath)
  530. removed = True
  531. else:
  532. print fpath
  533. except OSError:
  534. err.append((_("Unable to remove file '%s'") % fpath))
  535. if force and not removed:
  536. grass.fatal(_("Extension <%s> not found") % options['extension'])
  537. if err:
  538. for e in err:
  539. grass.error(e)
  540. else:
  541. remove_extension_std(force)
  542. else:
  543. remove_extension_std(force)
  544. if force:
  545. grass.message(_("Updating metadata file..."))
  546. remove_extension_xml()
  547. grass.message(_("Extension <%s> successfully uninstalled.") % options['extension'])
  548. else:
  549. grass.warning(_("Extension <%s> not removed.\n"
  550. "Re-run '%s' with 'f' flag to force removal") % (options['extension'], 'g.extension'))
  551. # remove exising extension (using standard files layout)
  552. def remove_extension_std(force = False):
  553. for fpath in [os.path.join(options['prefix'], 'bin', options['extension']),
  554. os.path.join(options['prefix'], 'scripts', options['extension']),
  555. os.path.join(options['prefix'], 'docs', 'html', options['extension'] + '.html'),
  556. os.path.join(options['prefix'], 'man', 'man1', options['extension'] + '.1')]:
  557. if os.path.isfile(fpath):
  558. if force:
  559. grass.verbose(fpath)
  560. os.remove(fpath)
  561. else:
  562. print fpath
  563. # check links in CSS
  564. def check_style_files(fil):
  565. dist_file = os.path.join(os.getenv('GISBASE'), 'docs', 'html', fil)
  566. addons_file = os.path.join(options['prefix'], 'docs', 'html', fil)
  567. if os.path.isfile(addons_file):
  568. return
  569. try:
  570. shutil.copyfile(dist_file,addons_file)
  571. except OSError, e:
  572. grass.fatal(_("Unable to create '%s': %s") % (addons_file, e))
  573. def create_dir(path):
  574. if os.path.isdir(path):
  575. return
  576. try:
  577. os.makedirs(path)
  578. except OSError, e:
  579. grass.fatal(_("Unable to create '%s': %s") % (path, e))
  580. grass.debug("'%s' created" % path)
  581. def check_dirs():
  582. create_dir(os.path.join(options['prefix'], 'bin'))
  583. create_dir(os.path.join(options['prefix'], 'docs', 'html'))
  584. check_style_files('grass_logo.png')
  585. check_style_files('grassdocs.css')
  586. create_dir(os.path.join(options['prefix'], 'etc'))
  587. create_dir(os.path.join(options['prefix'], 'man', 'man1'))
  588. create_dir(os.path.join(options['prefix'], 'scripts'))
  589. def main():
  590. # check dependecies
  591. if sys.platform != "win32":
  592. check_progs()
  593. # define path
  594. if flags['s']:
  595. options['prefix'] = os.environ['GISBASE']
  596. if options['prefix'] == '$GRASS_ADDON_BASE':
  597. if not os.getenv('GRASS_ADDON_BASE'):
  598. major_version = int(grass.version()['version'].split('.', 1)[0])
  599. grass.warning(_("GRASS_ADDON_BASE is not defined, "
  600. "installing to ~/.grass%d/addons") % major_version)
  601. options['prefix'] = os.path.join(os.environ['HOME'], '.grass%d' % major_version, 'addons')
  602. else:
  603. path_list = os.environ['GRASS_ADDON_BASE'].split(os.pathsep)
  604. if len(path_list) < 1:
  605. grass.fatal(_("Invalid GRASS_ADDON_BASE value - '%s'") % os.environ['GRASS_ADDON_BASE'])
  606. if len(path_list) > 1:
  607. grass.warning(_("GRASS_ADDON_BASE has more items, using first defined - '%s'") % path_list[0])
  608. options['prefix'] = path_list[0]
  609. # list available extensions
  610. if flags['l'] or flags['c'] or flags['g']:
  611. list_available_extensions()
  612. return 0
  613. elif flags['a']:
  614. elist = get_installed_extensions()
  615. if elist:
  616. grass.message(_("List of installed extensions:"))
  617. print os.linesep.join(elist)
  618. else:
  619. grass.info(_("No extension installed"))
  620. return 0
  621. else:
  622. if not options['extension']:
  623. grass.fatal(_('You need to define an extension name or use -l'))
  624. if flags['d']:
  625. if options['operation'] != 'add':
  626. grass.warning(_("Flag 'd' is relevant only to 'operation=add'. Ignoring this flag."))
  627. else:
  628. global remove_tmpdir
  629. remove_tmpdir = False
  630. if options['operation'] == 'add':
  631. check_dirs()
  632. install_extension()
  633. else: # remove
  634. remove_extension(flags['f'])
  635. return 0
  636. if __name__ == "__main__":
  637. options, flags = grass.parser()
  638. global tmpdir
  639. tmpdir = grass.tempdir()
  640. atexit.register(cleanup)
  641. sys.exit(main())