g.extension.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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. #% required: yes
  46. #% answer: http://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 (ignored when flag -s is given)
  53. #% answer: $GRASS_ADDON_PATH
  54. #% required: no
  55. #%end
  56. #%flag
  57. #% key: l
  58. #% description: List available modules in the GRASS Addons SVN repository
  59. #% guisection: Print
  60. #% suppress_required: yes
  61. #%end
  62. #%flag
  63. #% key: c
  64. #% description: List available modules in the GRASS Addons SVN repository including module description
  65. #% guisection: Print
  66. #% suppress_required: yes
  67. #%end
  68. #%flag
  69. #% key: g
  70. #% description: List available modules in the GRASS Addons SVN repository (shell script style)
  71. #% guisection: Print
  72. #% suppress_required: yes
  73. #%end
  74. #%flag
  75. #% key: s
  76. #% description: Install system-wide (may need system administrator rights)
  77. #%end
  78. #%flag
  79. #% key: d
  80. #% description: Download source code and exit
  81. #%end
  82. #%flag
  83. #% key: i
  84. #% description: Don't install new extension, just compile it
  85. #%end
  86. import os
  87. import sys
  88. import re
  89. import atexit
  90. import shutil
  91. import glob
  92. import zipfile
  93. import tempfile
  94. import shutil
  95. from urllib2 import urlopen, HTTPError
  96. try:
  97. import xml.etree.ElementTree as etree
  98. except ImportError:
  99. import elementtree.ElementTree as etree # Python <= 2.4
  100. from grass.script import core as grass
  101. # temp dir
  102. remove_tmpdir = True
  103. def check():
  104. for prog in ('svn', 'make', 'gcc'):
  105. if not grass.find_program(prog, ['--help']):
  106. grass.fatal(_("'%s' required. Please install '%s' first.") % (prog, prog))
  107. def expand_module_class_name(c):
  108. name = { 'd' : 'display',
  109. 'db' : 'database',
  110. 'g' : 'general',
  111. 'i' : 'imagery',
  112. 'm' : 'misc',
  113. 'ps' : 'postscript',
  114. 'p' : 'paint',
  115. 'r' : 'raster',
  116. 'r3' : 'raster3d',
  117. 's' : 'sites',
  118. 'v' : 'vector',
  119. 'gui' : 'gui/wxpython' }
  120. if name.has_key(c):
  121. return name[c]
  122. return c
  123. def list_available_modules():
  124. mlist = list()
  125. # try to download XML metadata file first
  126. url = "http://grass.osgeo.org/addons/grass%s.xml" % grass.version()['version'].split('.')[0]
  127. try:
  128. f = urlopen(url)
  129. tree = etree.fromstring(f.read())
  130. for mnode in tree.findall('task'):
  131. name = mnode.get('name')
  132. if flags['c'] or flags['g']:
  133. desc = mnode.find('description').text
  134. if not desc:
  135. desc = ''
  136. keyw = mnode.find('keywords').text
  137. if not keyw:
  138. keyw = ''
  139. if flags['g']:
  140. print 'name=' + name
  141. print 'description=' + desc
  142. print 'keywords=' + keyw
  143. elif flags['c']:
  144. print name + ' - ' + desc
  145. else:
  146. print name
  147. except HTTPError:
  148. return list_available_modules_svn()
  149. return mlist
  150. def list_available_modules_svn():
  151. mlist = list()
  152. grass.message(_('Fetching list of modules from GRASS-Addons SVN (be patient)...'))
  153. pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
  154. i = 0
  155. prefix = ['d', 'db', 'g', 'i', 'm', 'ps',
  156. 'p', 'r', 'r3', 's', 'v']
  157. nprefix = len(prefix)
  158. for d in prefix:
  159. if flags['g']:
  160. grass.percent(i, nprefix, 1)
  161. i += 1
  162. modclass = expand_module_class_name(d)
  163. grass.verbose(_("Checking for '%s' modules...") % modclass)
  164. url = '%s/%s' % (options['svnurl'], modclass)
  165. grass.debug("url = %s" % url, debug = 2)
  166. try:
  167. f = urlopen(url)
  168. except HTTPError:
  169. grass.debug(_("Unable to fetch '%s'") % url, debug = 1)
  170. continue
  171. for line in f.readlines():
  172. # list modules
  173. sline = pattern.search(line)
  174. if not sline:
  175. continue
  176. name = sline.group(2).rstrip('/')
  177. if name.split('.', 1)[0] == d:
  178. print_module_desc(name, url)
  179. mlist.append(name)
  180. mlist += list_wxgui_extensions()
  181. if flags['g']:
  182. grass.percent(1, 1, 1)
  183. return mlist
  184. def list_wxgui_extensions(print_module = True):
  185. mlist = list()
  186. grass.debug('Fetching list of wxGUI extensions from GRASS-Addons SVN (be patient)...')
  187. pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE)
  188. grass.verbose(_("Checking for '%s' modules...") % 'gui/wxpython')
  189. url = '%s/%s' % (options['svnurl'], 'gui/wxpython')
  190. grass.debug("url = %s" % url, debug = 2)
  191. f = urlopen(url)
  192. if not f:
  193. grass.warning(_("Unable to fetch '%s'") % url)
  194. return
  195. for line in f.readlines():
  196. # list modules
  197. sline = pattern.search(line)
  198. if not sline:
  199. continue
  200. name = sline.group(2).rstrip('/')
  201. if name not in ('..', 'Makefile'):
  202. if print_module:
  203. print_module_desc(name, url)
  204. mlist.append(name)
  205. return mlist
  206. def print_module_desc(name, url):
  207. if not flags['c'] and not flags['g']:
  208. print name
  209. return
  210. if flags['g']:
  211. print 'name=' + name
  212. # check main.c first
  213. desc = get_module_desc(url + '/' + name + '/' + name)
  214. if not desc:
  215. desc = get_module_desc(url + '/' + name + '/main.c', script = False)
  216. if not desc:
  217. if not flags['g']:
  218. print name + ' - '
  219. return
  220. if flags['g']:
  221. print 'description=' + desc.get('description', '')
  222. print 'keywords=' + ','.join(desc.get('keywords', list()))
  223. else:
  224. print name + ' - ' + desc.get('description', '')
  225. def get_module_desc(url, script = True):
  226. grass.debug('url=%s' % url)
  227. try:
  228. f = urlopen(url)
  229. except HTTPError:
  230. return {}
  231. if script:
  232. ret = get_module_script(f)
  233. else:
  234. ret = get_module_main(f)
  235. return ret
  236. def get_module_main(f):
  237. if not f:
  238. return dict()
  239. ret = { 'keyword' : list() }
  240. pattern = re.compile(r'(module.*->)(.+)(=)(.*)', re.IGNORECASE)
  241. keyword = re.compile(r'(G_add_keyword\()(.+)(\);)', re.IGNORECASE)
  242. key = ''
  243. value = ''
  244. for line in f.readlines():
  245. line = line.strip()
  246. find = pattern.search(line)
  247. if find:
  248. key = find.group(2).strip()
  249. line = find.group(4).strip()
  250. else:
  251. find = keyword.search(line)
  252. if find:
  253. ret['keyword'].append(find.group(2).replace('"', '').replace('_(', '').replace(')', ''))
  254. if key:
  255. value += line
  256. if line[-2:] == ');':
  257. value = value.replace('"', '').replace('_(', '').replace(');', '')
  258. if key == 'keywords':
  259. ret[key] = map(lambda x: x.strip(), value.split(','))
  260. else:
  261. ret[key] = value
  262. key = value = ''
  263. return ret
  264. def get_module_script(f):
  265. ret = dict()
  266. if not f:
  267. return ret
  268. begin = re.compile(r'#%.*module', re.IGNORECASE)
  269. end = re.compile(r'#%.*end', re.IGNORECASE)
  270. mline = None
  271. for line in f.readlines():
  272. if not mline:
  273. mline = begin.search(line)
  274. if mline:
  275. if end.search(line):
  276. break
  277. try:
  278. key, value = line.split(':', 1)
  279. key = key.replace('#%', '').strip()
  280. value = value.strip()
  281. if key == 'keywords':
  282. ret[key] = map(lambda x: x.strip(), value.split(','))
  283. else:
  284. ret[key] = value
  285. except ValueError:
  286. pass
  287. return ret
  288. def cleanup():
  289. if remove_tmpdir:
  290. grass.try_rmdir(tmpdir)
  291. else:
  292. grass.message(_("Path to the source code:"))
  293. sys.stderr.write('%s\n' % os.path.join(tmpdir, options['extension']))
  294. def install_extension_win():
  295. ### TODO: do not use hardcoded url
  296. version = grass.version()['version'].split('.')
  297. url = "http://wingrass.fsv.cvut.cz/grass%s%s/addons/" % (version[0], version[1])
  298. grass.message(_("Downloading precompiled GRASS Addons <%s>...") % options['extension'])
  299. try:
  300. f = urlopen(url + options['extension'] + '.zip')
  301. # create addons dir if not exists
  302. if not os.path.exists(options['prefix']):
  303. os.mkdir(options['prefix'])
  304. # download data
  305. fo = tempfile.TemporaryFile()
  306. fo.write(f.read())
  307. zfobj = zipfile.ZipFile(fo)
  308. for name in zfobj.namelist():
  309. if name.endswith('/'):
  310. d = os.path.join(options['prefix'], name)
  311. if not os.path.exists(d):
  312. os.mkdir(d)
  313. else:
  314. outfile = open(os.path.join(options['prefix'], name), 'wb')
  315. outfile.write(zfobj.read(name))
  316. outfile.close()
  317. fo.close()
  318. except HTTPError:
  319. grass.fatal(_("GRASS Addons <%s> not found") % options['extension'])
  320. def install_extension():
  321. gisbase = os.getenv('GISBASE')
  322. if not gisbase:
  323. grass.fatal(_('$GISBASE not defined'))
  324. if grass.find_program(options['extension'], ['--help']):
  325. grass.warning(_("Extension <%s> already installed. Will be updated...") % options['extension'])
  326. if sys.platform == "win32":
  327. install_extension_win()
  328. else:
  329. install_extension_other()
  330. # manual page: fix href
  331. if os.getenv('GRASS_ADDON_PATH'):
  332. html_man = os.path.join(os.getenv('GRASS_ADDON_PATH'), 'docs', 'html', options['extension'] + '.html')
  333. if os.path.exists(html_man):
  334. fd = open(html_man)
  335. html_str = '\n'.join(fd.readlines())
  336. fd.close()
  337. for rep in ('grassdocs.css', 'grass_logo.png'):
  338. patt = re.compile(rep, re.IGNORECASE)
  339. html_str = patt.sub(os.path.join(gisbase, 'docs', 'html', rep),
  340. html_str)
  341. patt = re.compile(r'(<a href=")(d|db|g|i|m|p|ps|r|r3|s|v|wxGUI)(\.)(.+)(.html">)', re.IGNORECASE)
  342. while True:
  343. m = patt.search(html_str)
  344. if not m:
  345. break
  346. html_str = patt.sub(m.group(1) + os.path.join(gisbase, 'docs', 'html',
  347. m.group(2) + m.group(3) + m.group(4)) + m.group(5),
  348. html_str, count = 1)
  349. fd = open(html_man, "w")
  350. fd.write(html_str)
  351. fd.close()
  352. # symlink for binaries needed, see http://trac.osgeo.org/grass/changeset/49124
  353. src = None
  354. if sys.platform == 'win32':
  355. bin_ext = '.exe'
  356. sct_ext = '.py'
  357. else:
  358. bin_ext = sct_ext = ''
  359. if os.path.exists(os.path.join(options['prefix'], 'bin',
  360. options['extension'] + bin_ext)):
  361. src = os.path.join(options['prefix'], 'bin', options['extension']) + bin_ext
  362. dst = os.path.join(options['prefix'], options['extension']) + bin_ext
  363. elif os.path.exists(os.path.join(options['prefix'], 'scripts',
  364. options['extension'] + sct_ext)):
  365. src = os.path.join(options['prefix'], 'scripts', options['extension']) + sct_ext
  366. dst = os.path.join(options['prefix'], options['extension']) + sct_ext
  367. if src and not os.path.exists(dst):
  368. if sys.platform == 'win32':
  369. shutil.copyfile(src, dst)
  370. else:
  371. os.symlink(src, dst)
  372. if not os.environ.has_key('GRASS_ADDON_PATH') or \
  373. not os.environ['GRASS_ADDON_PATH']:
  374. grass.warning(_('This add-on module will not function until you set the '
  375. 'GRASS_ADDON_PATH environment variable (see "g.manual variables")'))
  376. def install_extension_other():
  377. gisbase = os.getenv('GISBASE')
  378. gui_list = list_wxgui_extensions(print_module = False)
  379. if options['extension'] not in gui_list:
  380. classchar = options['extension'].split('.', 1)[0]
  381. moduleclass = expand_module_class_name(classchar)
  382. url = options['svnurl'] + '/' + moduleclass + '/' + options['extension']
  383. else:
  384. url = options['svnurl'] + '/gui/wxpython/' + options['extension']
  385. if not flags['s']:
  386. grass.fatal(_("Installation of wxGUI extension requires -%s flag.") % 's')
  387. grass.message(_("Fetching '%s' from GRASS-Addons SVN (be patient)...") % options['extension'])
  388. os.chdir(tmpdir)
  389. if grass.verbosity() == 0:
  390. outdev = open(os.devnull, 'w')
  391. else:
  392. outdev = sys.stdout
  393. if grass.call(['svn', 'checkout',
  394. url], stdout = outdev) != 0:
  395. grass.fatal(_("GRASS Addons <%s> not found") % options['extension'])
  396. dirs = { 'bin' : os.path.join(tmpdir, options['extension'], 'bin'),
  397. 'docs' : os.path.join(tmpdir, options['extension'], 'docs'),
  398. 'html' : os.path.join(tmpdir, options['extension'], 'docs', 'html'),
  399. 'man' : os.path.join(tmpdir, options['extension'], 'man'),
  400. 'man1' : os.path.join(tmpdir, options['extension'], 'man', 'man1'),
  401. 'scripts' : os.path.join(tmpdir, options['extension'], 'scripts'),
  402. 'etc' : os.path.join(tmpdir, options['extension'], 'etc'),
  403. }
  404. makeCmd = ['make',
  405. 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ '),
  406. 'BIN=%s' % dirs['bin'],
  407. 'HTMLDIR=%s' % dirs['html'],
  408. 'MANDIR=%s' % dirs['man1'],
  409. 'SCRIPTDIR=%s' % dirs['scripts'],
  410. 'ETC=%s' % os.path.join(dirs['etc'],options['extension'])
  411. ]
  412. installCmd = ['make',
  413. 'MODULE_TOPDIR=%s' % gisbase,
  414. 'ARCH_DISTDIR=%s' % os.path.join(tmpdir, options['extension']),
  415. 'INST_DIR=%s' % options['prefix'],
  416. 'install'
  417. ]
  418. if flags['d']:
  419. grass.message(_("To compile run:"))
  420. sys.stderr.write(' '.join(makeCmd) + '\n')
  421. grass.message(_("To install run:\n\n"))
  422. sys.stderr.write(' '.join(installCmd) + '\n')
  423. return
  424. os.chdir(os.path.join(tmpdir, options['extension']))
  425. grass.message(_("Compiling '%s'...") % options['extension'])
  426. if options['extension'] not in gui_list:
  427. ret = grass.call(makeCmd,
  428. stdout = outdev)
  429. else:
  430. ret = grass.call(['make',
  431. 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ ')],
  432. stdout = outdev)
  433. if ret != 0:
  434. grass.fatal(_('Compilation failed, sorry. Please check above error messages.'))
  435. if flags['i'] or options['extension'] in gui_list:
  436. return
  437. grass.message(_("Installing '%s'...") % options['extension'])
  438. ret = grass.call(installCmd,
  439. stdout = outdev)
  440. if ret != 0:
  441. grass.warning(_('Installation failed, sorry. Please check above error messages.'))
  442. else:
  443. grass.message(_("Installation of '%s' successfully finished.") % options['extension'])
  444. # manual page: fix href
  445. if os.getenv('GRASS_ADDON_PATH'):
  446. html_man = os.path.join(os.getenv('GRASS_ADDON_PATH'), 'docs', 'html', options['extension'] + '.html')
  447. if os.path.exists(html_man):
  448. fd = open(html_man)
  449. html_str = '\n'.join(fd.readlines())
  450. fd.close()
  451. for rep in ('grassdocs.css', 'grass_logo.png'):
  452. patt = re.compile(rep, re.IGNORECASE)
  453. html_str = patt.sub(os.path.join(gisbase, 'docs', 'html', rep),
  454. html_str)
  455. patt = re.compile(r'(<a href=")(d|db|g|i|m|p|ps|r|r3|s|v|wxGUI)(\.)(.+)(.html">)', re.IGNORECASE)
  456. while True:
  457. m = patt.search(html_str)
  458. if not m:
  459. break
  460. html_str = patt.sub(m.group(1) + os.path.join(gisbase, 'docs', 'html',
  461. m.group(2) + m.group(3) + m.group(4)) + m.group(5),
  462. html_str, count = 1)
  463. fd = open(html_man, "w")
  464. fd.write(html_str)
  465. fd.close()
  466. if not os.environ.has_key('GRASS_ADDON_PATH') or \
  467. not os.environ['GRASS_ADDON_PATH']:
  468. grass.warning(_('This add-on module will not function until you set the '
  469. 'GRASS_ADDON_PATH environment variable (see "g.manual variables")'))
  470. def remove_extension():
  471. # try to download XML metadata file first
  472. url = "http://grass.osgeo.org/addons/grass%s.xml" % grass.version()['version'].split('.')[0]
  473. name = options['extension']
  474. try:
  475. f = urlopen(url)
  476. tree = etree.fromstring(f.read())
  477. flist = []
  478. for task in tree.findall('task'):
  479. if name == task.get('name', default = '') and \
  480. task.find('binary') is not None:
  481. for f in task.find('binary').findall('file'):
  482. fname = f.text
  483. if fname:
  484. fpath = fname.split('/')
  485. if sys.platform == 'win32':
  486. if fpath[0] == 'bin':
  487. fpath[-1] += '.exe'
  488. if fpath[0] == 'scripts':
  489. fpath[-1] += '.py'
  490. flist.append(fpath)
  491. if flist:
  492. removed = False
  493. err = list()
  494. for f in flist:
  495. fpath = os.path.join(options['prefix'], os.path.sep.join(f))
  496. try:
  497. os.remove(fpath)
  498. removed = True
  499. except OSError:
  500. err.append((_("Unable to remove file '%s'") % fpath))
  501. if not removed:
  502. grass.fatal(_("Extension <%s> not found") % options['extension'])
  503. if err:
  504. for e in err:
  505. grass.error(e)
  506. else:
  507. remove_extension_std()
  508. except HTTPError:
  509. remove_extension_std()
  510. grass.message(_("Extension <%s> successfully uninstalled.") % options['extension'])
  511. def remove_extension_std():
  512. # is module available?
  513. if not os.path.exists(os.path.join(options['prefix'], 'bin', options['extension'])):
  514. grass.fatal(_("Extension <%s> not found") % options['extension'])
  515. for file in [os.path.join(options['prefix'], 'bin', options['extension']),
  516. os.path.join(options['prefix'], 'scripts', options['extension']),
  517. os.path.join(options['prefix'], 'docs', 'html', options['extension'] + '.html')]:
  518. if os.path.isfile(file):
  519. os.remove(file)
  520. def create_dir(path):
  521. if os.path.isdir(path):
  522. return
  523. try:
  524. os.makedirs(path)
  525. except OSError, e:
  526. grass.fatal(_("Unable to create '%s': %s") % (path, e))
  527. grass.debug("'%s' created" % path)
  528. def check_style_files(fil):
  529. #check the links to grassdocs.css/grass_logo.png to a correct manual page of addons
  530. dist_file = os.path.join(os.getenv('GISBASE'),'docs','html',fil)
  531. addons_file = os.path.join(options['prefix'],'docs','html',fil)
  532. #check if file already exists in the grass addons docs html path
  533. if os.path.isfile(addons_file):
  534. return
  535. #otherwise copy the file from $GISBASE/docs/html, it doesn't use link
  536. #because os.symlink it work only in Linux
  537. else:
  538. try:
  539. shutil.copyfile(dist_file,addons_file)
  540. except OSError, e:
  541. grass.fatal(_("Unable to create '%s': %s") % (addons_file, e))
  542. def check_dirs():
  543. create_dir(os.path.join(options['prefix'], 'bin'))
  544. create_dir(os.path.join(options['prefix'], 'docs', 'html'))
  545. check_style_files('grass_logo.png')
  546. check_style_files('grassdocs.css')
  547. create_dir(os.path.join(options['prefix'], 'man', 'man1'))
  548. create_dir(os.path.join(options['prefix'], 'scripts'))
  549. def main():
  550. # check dependecies
  551. if sys.platform != "win32":
  552. check()
  553. # list available modules
  554. if flags['l'] or flags['c'] or flags['g']:
  555. list_available_modules()
  556. return 0
  557. else:
  558. if not options['extension']:
  559. grass.fatal(_('You need to define an extension name or use -l'))
  560. # define path
  561. if flags['s']:
  562. options['prefix'] = os.environ['GISBASE']
  563. if options['prefix'] == '$GRASS_ADDON_PATH':
  564. if not os.environ.has_key('GRASS_ADDON_PATH') or \
  565. not os.environ['GRASS_ADDON_PATH']:
  566. major_version = int(grass.version()['version'].split('.', 1)[0])
  567. grass.warning(_("GRASS_ADDON_PATH is not defined, "
  568. "installing to ~/.grass%d/addons/") % major_version)
  569. options['prefix'] = os.path.join(os.environ['HOME'], '.grass%d' % major_version, 'addons')
  570. else:
  571. path_list = os.environ['GRASS_ADDON_PATH'].split(os.pathsep)
  572. if len(path_list) < 1:
  573. grass.fatal(_("Invalid GRASS_ADDON_PATH value - '%s'") % os.environ['GRASS_ADDON_PATH'])
  574. if len(path_list) > 1:
  575. grass.warning(_("GRASS_ADDON_PATH has more items, using first defined - '%s'") % path_list[0])
  576. options['prefix'] = path_list[0]
  577. # check dirs
  578. check_dirs()
  579. if flags['d']:
  580. if options['operation'] != 'add':
  581. grass.warning(_("Flag 'd' is relevant only to 'operation=add'. Ignoring this flag."))
  582. else:
  583. global remove_tmpdir
  584. remove_tmpdir = False
  585. if options['operation'] == 'add':
  586. install_extension()
  587. else: # remove
  588. remove_extension()
  589. return 0
  590. if __name__ == "__main__":
  591. options, flags = grass.parser()
  592. global tmpdir
  593. tmpdir = grass.tempdir()
  594. atexit.register(cleanup)
  595. sys.exit(main())