GRASS Programmer's Manual
6.4.2(2012)
|
00001 #!/usr/bin/env python 00002 00003 ############################################################################ 00004 # 00005 # MODULE: g.extension 00006 # AUTHOR(S): Markus Neteler 00007 # Pythonized by Martin Landa 00008 # PURPOSE: Tool to download and install extensions from GRASS Addons SVN into 00009 # local GRASS installation 00010 # COPYRIGHT: (C) 2009-2011 by Markus Neteler, and the GRASS Development Team 00011 # 00012 # This program is free software under the GNU General 00013 # Public License (>=v2). Read the file COPYING that 00014 # comes with GRASS for details. 00015 # 00016 # TODO: add sudo support where needed (i.e. check first permission to write into 00017 # $GISBASE directory) 00018 ############################################################################# 00019 00020 #%module 00021 #% label: Maintains GRASS Addons extensions in local GRASS installation. 00022 #% description: Downloads, installs extensions from GRASS Addons SVN repository into local GRASS installation or removes installed extensions. 00023 #% keywords: general, installation, extensions 00024 #%end 00025 00026 #%option 00027 #% key: extension 00028 #% type: string 00029 #% key_desc: name 00030 #% description: Name of extension to install/remove 00031 #% guisection: Required 00032 #%end 00033 #%option 00034 #% key: operation 00035 #% type: string 00036 #% description: Operation to be performed 00037 #% required: yes 00038 #% options: add,remove 00039 #% answer: add 00040 #%end 00041 #%option 00042 #% key: svnurl 00043 #% type: string 00044 #% key_desc: url 00045 #% description: SVN Addons repository URL 00046 #% answer: http://svn.osgeo.org/grass/grass-addons/grass6 00047 #%end 00048 #%option 00049 #% key: prefix 00050 #% type: string 00051 #% key_desc: path 00052 #% description: Prefix where to install extension (ignored when flag -s is given) 00053 #% answer: $GRASS_ADDON_PATH 00054 #% required: no 00055 #%end 00056 00057 #%flag 00058 #% key: l 00059 #% description: List available extensions in the GRASS Addons SVN repository 00060 #% guisection: Print 00061 #%end 00062 #%flag 00063 #% key: c 00064 #% description: List available extensions in the GRASS Addons SVN repository including module description 00065 #% guisection: Print 00066 #%end 00067 #%flag 00068 #% key: g 00069 #% description: List available extensions in the GRASS Addons SVN repository (shell script style) 00070 #% guisection: Print 00071 #%end 00072 #%flag 00073 #% key: a 00074 #% description: List locally installed extension 00075 #% guisection: Print 00076 #%end 00077 #%flag 00078 #% key: s 00079 #% description: Install system-wide (may need system administrator rights) 00080 #% guisection: Install 00081 #%end 00082 #%flag 00083 #% key: d 00084 #% description: Download source code and exit 00085 #% guisection: Install 00086 #%end 00087 #%flag 00088 #% key: i 00089 #% description: Don't install new extension, just compile it 00090 #% guisection: Install 00091 #%end 00092 #%flag 00093 #% key: f 00094 #% description: Force removal when uninstalling extension (operation=remove) 00095 #% guisection: Remove 00096 #%end 00097 00098 import os 00099 import sys 00100 import re 00101 import atexit 00102 import shutil 00103 import glob 00104 import zipfile 00105 import tempfile 00106 import shutil 00107 import fileinput 00108 00109 from urllib2 import urlopen, HTTPError 00110 00111 try: 00112 import xml.etree.ElementTree as etree 00113 except ImportError: 00114 import elementtree.ElementTree as etree # Python <= 2.4 00115 00116 from grass.script import core as grass 00117 00118 # temp dir 00119 remove_tmpdir = True 00120 00121 # check requirements 00122 def check_progs(): 00123 for prog in ('svn', 'make', 'gcc'): 00124 if not grass.find_program(prog, ['--help']): 00125 grass.fatal(_("'%s' required. Please install '%s' first.") % (prog, prog)) 00126 00127 # expand prefix to class name 00128 def expand_module_class_name(c): 00129 name = { 'd' : 'display', 00130 'db' : 'database', 00131 'g' : 'general', 00132 'i' : 'imagery', 00133 'm' : 'misc', 00134 'ps' : 'postscript', 00135 'p' : 'paint', 00136 'r' : 'raster', 00137 'r3' : 'raster3d', 00138 's' : 'sites', 00139 'v' : 'vector', 00140 'gui' : 'gui/wxpython' } 00141 00142 if name.has_key(c): 00143 return name[c] 00144 00145 return c 00146 00147 00148 # list installed extensions 00149 def get_installed_extensions(force = False): 00150 fXML = os.path.join(options['prefix'], 'modules.xml') 00151 if not os.path.exists(fXML): 00152 if force: 00153 write_xml_modules(fXML) 00154 else: 00155 grass.warning(_("No metadata file available")) 00156 return [] 00157 00158 # read XML file 00159 fo = open(fXML, 'r') 00160 try: 00161 tree = etree.fromstring(fo.read()) 00162 except: 00163 os.remove(fXML) 00164 write_xml_modules(fXML) 00165 return [] 00166 fo.close() 00167 00168 ret = list() 00169 for tnode in tree.findall('task'): 00170 ret.append(tnode.get('name').strip()) 00171 00172 return ret 00173 00174 # list extensions (read XML file from grass.osgeo.org/addons) 00175 def list_available_extensions(): 00176 mlist = list() 00177 00178 # try to download XML metadata file first 00179 url = "http://grass.osgeo.org/addons/grass%s/modules.xml" % grass.version()['version'].split('.')[0] 00180 grass.debug("url=%s" % url, 1) 00181 try: 00182 f = urlopen(url) 00183 try: 00184 tree = etree.fromstring(f.read()) 00185 except: 00186 grass.fatal(_("Unable to parse '%s'") % url) 00187 00188 for mnode in tree.findall('task'): 00189 name = mnode.get('name') 00190 if flags['c'] or flags['g']: 00191 desc = mnode.find('description').text 00192 if not desc: 00193 desc = '' 00194 keyw = mnode.find('keywords').text 00195 if not keyw: 00196 keyw = '' 00197 00198 if flags['g']: 00199 print 'name=' + name 00200 print 'description=' + desc 00201 print 'keywords=' + keyw 00202 elif flags['c']: 00203 print name + ' - ' + desc 00204 else: 00205 print name 00206 except HTTPError: 00207 return list_available_extensions_svn() 00208 00209 return mlist 00210 00211 # list extensions (scan SVN repo) 00212 def list_available_extensions_svn(): 00213 mlist = list() 00214 grass.message(_('Fetching list of extensions from GRASS-Addons SVN (be patient)...')) 00215 pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE) 00216 00217 if flags['c']: 00218 grass.warning(_("Flag 'c' ignored, metadata file not available")) 00219 if flags['g']: 00220 grass.warning(_("Flag 'g' ignored, metadata file not available")) 00221 00222 prefix = ['d', 'db', 'g', 'i', 'm', 'ps', 00223 'p', 'r', 'r3', 's', 'v'] 00224 nprefix = len(prefix) 00225 for d in prefix: 00226 modclass = expand_module_class_name(d) 00227 grass.verbose(_("Checking for '%s' modules...") % modclass) 00228 00229 url = '%s/%s' % (options['svnurl'], modclass) 00230 grass.debug("url = %s" % url, debug = 2) 00231 try: 00232 f = urlopen(url) 00233 except HTTPError: 00234 grass.debug(_("Unable to fetch '%s'") % url, debug = 1) 00235 continue 00236 00237 for line in f.readlines(): 00238 # list extensions 00239 sline = pattern.search(line) 00240 if not sline: 00241 continue 00242 name = sline.group(2).rstrip('/') 00243 if name.split('.', 1)[0] == d: 00244 print name 00245 mlist.append(name) 00246 00247 mlist += list_wxgui_extensions() 00248 00249 return mlist 00250 00251 # list wxGUI extensions 00252 def list_wxgui_extensions(print_module = True): 00253 mlist = list() 00254 grass.debug('Fetching list of wxGUI extensions from GRASS-Addons SVN (be patient)...') 00255 pattern = re.compile(r'(<li><a href=".+">)(.+)(</a></li>)', re.IGNORECASE) 00256 grass.verbose(_("Checking for '%s' modules...") % 'gui/wxpython') 00257 00258 url = '%s/%s' % (options['svnurl'], 'gui/wxpython') 00259 grass.debug("url = %s" % url, debug = 2) 00260 f = urlopen(url) 00261 if not f: 00262 grass.warning(_("Unable to fetch '%s'") % url) 00263 return 00264 00265 for line in f.readlines(): 00266 # list extensions 00267 sline = pattern.search(line) 00268 if not sline: 00269 continue 00270 name = sline.group(2).rstrip('/') 00271 if name not in ('..', 'Makefile'): 00272 if print_module: 00273 print name 00274 mlist.append(name) 00275 00276 return mlist 00277 00278 def cleanup(): 00279 if remove_tmpdir: 00280 grass.try_rmdir(tmpdir) 00281 else: 00282 grass.message(_("Path to the source code:")) 00283 sys.stderr.write('%s\n' % os.path.join(tmpdir, options['extension'])) 00284 00285 # write out meta-file 00286 def write_xml_modules(name, tree = None): 00287 fo = open(name, 'w') 00288 version = grass.version()['version'].split('.')[0] 00289 fo.write('<?xml version="1.0" encoding="UTF-8"?>\n') 00290 fo.write('<!DOCTYPE task SYSTEM "grass-addons.dtd">\n') 00291 fo.write('<addons version="%s">\n' % version) 00292 00293 if tree is not None: 00294 for tnode in tree.findall('task'): 00295 indent = 4 00296 fo.write('%s<task name="%s">\n' % (' ' * indent, tnode.get('name'))) 00297 indent += 4 00298 fo.write('%s<description>%s</description>\n' % \ 00299 (' ' * indent, tnode.find('description').text)) 00300 fo.write('%s<keywords>%s</keywords>\n' % \ 00301 (' ' * indent, tnode.find('keywords').text)) 00302 bnode = tnode.find('binary') 00303 if bnode is not None: 00304 fo.write('%s<binary>\n' % (' ' * indent)) 00305 indent += 4 00306 for fnode in bnode.findall('file'): 00307 fpath = fnode.text.split(os.path.sep) 00308 if not flags['s']: # tidy citizen hacks 00309 if fpath[0] in ('bin', 'scripts'): 00310 del fpath[0] 00311 if fpath[0] == 'man': 00312 fpath.insert(0, 'docs') 00313 00314 fo.write('%s<file>%s</file>\n' % \ 00315 (' ' * indent, os.path.join(options['prefix'], 00316 os.path.sep.join(fpath)))) 00317 indent -= 4 00318 fo.write('%s</binary>\n' % (' ' * indent)) 00319 libgisRev = grass.version()['libgis_revision'] 00320 fo.write('%s<libgis revision="%s" />\n' % \ 00321 (' ' * indent, libgisRev)) 00322 indent -= 4 00323 fo.write('%s</task>\n' % (' ' * indent)) 00324 00325 fo.write('</addons>\n') 00326 fo.close() 00327 00328 # update local meta-file when installing new extension 00329 def install_extension_xml(): 00330 # read metadata from remote server 00331 url = "http://grass.osgeo.org/addons/grass%s/modules.xml" % grass.version()['version'].split('.')[0] 00332 data = None 00333 try: 00334 f = urlopen(url) 00335 tree = etree.fromstring(f.read()) 00336 for mnode in tree.findall('task'): 00337 name = mnode.get('name') 00338 if name != options['extension']: 00339 continue 00340 00341 fList = list() 00342 bnode = mnode.find('binary') 00343 windows = sys.platform == 'win32' 00344 if bnode is not None: 00345 for fnode in bnode.findall('file'): 00346 path = fnode.text.split('/') 00347 if windows: 00348 if path[0] == 'bin': 00349 path[-1] += '.exe' 00350 if path[0] == 'scripts': 00351 path[-1] += '.py' 00352 fList.append(os.path.sep.join(path)) 00353 00354 desc = mnode.find('description').text 00355 if not desc: 00356 desc = '' 00357 keyw = mnode.find('keywords').text 00358 if not keyw: 00359 keyw = '' 00360 00361 data = { 'name' : name, 00362 'desc' : desc, 00363 'keyw' : keyw, 00364 'files' : fList, 00365 } 00366 except HTTPError: 00367 grass.error(_("Unable to read metadata file from the remote server")) 00368 00369 if not data: 00370 grass.warning(_("No metadata available")) 00371 return 00372 00373 fXML = os.path.join(options['prefix'], 'modules.xml') 00374 # create an empty file if not exists 00375 if not os.path.exists(fXML): 00376 write_xml_modules(fXML) 00377 00378 # read XML file 00379 fo = open(fXML, 'r') 00380 tree = etree.fromstring(fo.read()) 00381 fo.close() 00382 00383 # update tree 00384 tnode = None 00385 for node in tree.findall('task'): 00386 if node.get('name') == options['extension']: 00387 tnode = node 00388 break 00389 00390 if tnode is not None: 00391 # update existing node 00392 dnode = tnode.find('description') 00393 if dnode is not None: 00394 dnode.text = data['desc'] 00395 knode = tnode.find('keywords') 00396 if knode is not None: 00397 knode.text = data['keyw'] 00398 bnode = tnode.find('binary') 00399 if bnode is not None: 00400 tnode.remove(bnode) 00401 bnode = etree.Element('binary') 00402 for f in data['files']: 00403 fnode = etree.Element('file') 00404 fnode.text = f 00405 bnode.append(fnode) 00406 tnode.append(bnode) 00407 else: 00408 # create new node for task 00409 tnode = etree.Element('task', attrib = { 'name' : data['name'] }) 00410 dnode = etree.Element('description') 00411 dnode.text = data['desc'] 00412 tnode.append(dnode) 00413 knode = etree.Element('keywords') 00414 knode.text = data['keyw'] 00415 tnode.append(knode) 00416 bnode = etree.Element('binary') 00417 for f in data['files']: 00418 fnode = etree.Element('file') 00419 fnode.text = f 00420 bnode.append(fnode) 00421 tnode.append(bnode) 00422 tree.append(tnode) 00423 00424 write_xml_modules(fXML, tree) 00425 00426 # install extension on MS Windows 00427 def install_extension_win(): 00428 ### TODO: do not use hardcoded url - http://wingrass.fsv.cvut.cz/grassXX/addonsX.X.X 00429 version = grass.version()['version'].split('.') 00430 grass.message(_("Downloading precompiled GRASS Addons <%s>...") % options['extension']) 00431 url = "http://wingrass.fsv.cvut.cz/grass%s%s/addons/grass-%s.%s.%s" % \ 00432 (version[0], version[1], version[0], version[1], version[2]) 00433 grass.debug("url=%s" % url, 1) 00434 00435 try: 00436 f = urlopen(url + '/' + options['extension'] + '.zip') 00437 00438 # create addons dir if not exists 00439 if not os.path.exists(options['prefix']): 00440 os.mkdir(options['prefix']) 00441 00442 # download data 00443 fo = tempfile.TemporaryFile() 00444 fo.write(f.read()) 00445 zfobj = zipfile.ZipFile(fo) 00446 for name in zfobj.namelist(): 00447 if name.endswith('/'): 00448 d = os.path.join(options['prefix'], name) 00449 if not os.path.exists(d): 00450 os.mkdir(d) 00451 else: 00452 outfile = open(os.path.join(options['prefix'], name), 'wb') 00453 outfile.write(zfobj.read(name)) 00454 outfile.close() 00455 00456 fo.close() 00457 except HTTPError: 00458 grass.fatal(_("GRASS Addons <%s> not found") % options['extension']) 00459 00460 return 0 00461 00462 # install extension 00463 def install_extension(): 00464 gisbase = os.getenv('GISBASE') 00465 if not gisbase: 00466 grass.fatal(_('$GISBASE not defined')) 00467 00468 if options['extension'] in get_installed_extensions(force = True): 00469 grass.warning(_("Extension <%s> already installed. Re-installing...") % options['extension']) 00470 00471 if sys.platform == "win32": 00472 ret = install_extension_win() 00473 else: 00474 ret = install_extension_other() 00475 00476 if ret != 0: 00477 grass.warning(_('Installation failed, sorry. Please check above error messages.')) 00478 else: 00479 grass.message(_("Updating metadata file...")) 00480 install_extension_xml() 00481 grass.message(_("Installation of <%s> successfully finished") % options['extension']) 00482 00483 # cleanup build cruft 00484 if not flags['s']: 00485 tidy_citizen() 00486 00487 if not os.environ.has_key('GRASS_ADDON_PATH') or \ 00488 not os.environ['GRASS_ADDON_PATH']: 00489 grass.warning(_('This add-on module will not function until you set the ' 00490 'GRASS_ADDON_PATH environment variable (see "g.manual variables")')) 00491 00492 # install extension on other plaforms 00493 def install_extension_other(): 00494 gisbase = os.getenv('GISBASE') 00495 gui_list = list_wxgui_extensions(print_module = False) 00496 00497 if options['extension'] not in gui_list: 00498 classchar = options['extension'].split('.', 1)[0] 00499 moduleclass = expand_module_class_name(classchar) 00500 url = options['svnurl'] + '/' + moduleclass + '/' + options['extension'] 00501 else: 00502 url = options['svnurl'] + '/gui/wxpython/' + options['extension'] 00503 if not flags['s']: 00504 grass.fatal(_("Installation of wxGUI extension requires -%s flag.") % 's') 00505 00506 grass.message(_("Fetching <%s> from GRASS-Addons SVN (be patient)...") % options['extension']) 00507 00508 os.chdir(tmpdir) 00509 if grass.verbosity() <= 2: 00510 outdev = open(os.devnull, 'w') 00511 else: 00512 outdev = sys.stdout 00513 00514 if grass.call(['svn', 'checkout', 00515 url], stdout = outdev) != 0: 00516 grass.fatal(_("GRASS Addons <%s> not found") % options['extension']) 00517 00518 dirs = { 'bin' : os.path.join(tmpdir, options['extension'], 'bin'), 00519 'docs' : os.path.join(tmpdir, options['extension'], 'docs'), 00520 'html' : os.path.join(tmpdir, options['extension'], 'docs', 'html'), 00521 'man' : os.path.join(tmpdir, options['extension'], 'man'), 00522 'man1' : os.path.join(tmpdir, options['extension'], 'man', 'man1'), 00523 'scripts' : os.path.join(tmpdir, options['extension'], 'scripts'), 00524 'etc' : os.path.join(tmpdir, options['extension'], 'etc'), 00525 } 00526 00527 makeCmd = ['make', 00528 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ '), 00529 'BIN=%s' % dirs['bin'], 00530 'HTMLDIR=%s' % dirs['html'], 00531 'MANDIR=%s' % dirs['man1'], 00532 'SCRIPTDIR=%s' % dirs['scripts'], 00533 'ETC=%s' % os.path.join(dirs['etc'],options['extension']) 00534 ] 00535 00536 installCmd = ['make', 00537 'MODULE_TOPDIR=%s' % gisbase, 00538 'ARCH_DISTDIR=%s' % os.path.join(tmpdir, options['extension']), 00539 'INST_DIR=%s' % options['prefix'], 00540 'install' 00541 ] 00542 00543 if flags['d']: 00544 grass.message(_("To compile run:")) 00545 sys.stderr.write(' '.join(makeCmd) + '\n') 00546 grass.message(_("To install run:\n\n")) 00547 sys.stderr.write(' '.join(installCmd) + '\n') 00548 return 00549 00550 os.chdir(os.path.join(tmpdir, options['extension'])) 00551 00552 grass.message(_("Compiling...")) 00553 if options['extension'] not in gui_list: 00554 ret = grass.call(makeCmd, 00555 stdout = outdev) 00556 else: 00557 ret = grass.call(['make', 00558 'MODULE_TOPDIR=%s' % gisbase.replace(' ', '\ ')], 00559 stdout = outdev) 00560 00561 if ret != 0: 00562 grass.fatal(_('Compilation failed, sorry. Please check above error messages.')) 00563 00564 if flags['i'] or options['extension'] in gui_list: 00565 return 00566 00567 grass.message(_("Installing...")) 00568 00569 return grass.call(installCmd, 00570 stdout = outdev) 00571 00572 # remove dir if empty 00573 def remove_empty_dir(path): 00574 if os.path.exists(path) and not os.listdir(path): 00575 os.removedirs(path) 00576 00577 # see http://lists.osgeo.org/pipermail/grass-dev/2011-December/056938.html 00578 def tidy_citizen(): 00579 if sys.platform == 'win32': 00580 EXT_BIN = '.exe' 00581 EXT_SCT = '.bat' 00582 else: 00583 EXT_BIN = EXT_SCT = '' 00584 00585 # move script/ and bin/ to main dir 00586 if os.path.exists(os.path.join(options['prefix'], 'bin', options['extension']) + EXT_BIN): 00587 shutil.move(os.path.join(options['prefix'], 'bin', options['extension']) + EXT_BIN, 00588 os.path.join(options['prefix'], options['extension']) + EXT_BIN) 00589 if os.path.exists(os.path.join(options['prefix'], 'scripts', options['extension'])): 00590 shutil.move(os.path.join(options['prefix'], 'scripts', options['extension']), 00591 os.path.join(options['prefix'], options['extension'])) 00592 if os.path.exists(os.path.join(options['prefix'], 'scripts', options['extension'] + '.py')): 00593 shutil.move(os.path.join(options['prefix'], 'scripts', options['extension'] + '.py'), 00594 os.path.join(options['prefix'], options['extension'] + '.py')) 00595 if sys.platform == 'win32' and \ 00596 os.path.exists(os.path.join(options['prefix'], 'bin', options['extension']) + EXT_SCT): 00597 shutil.move(os.path.join(options['prefix'], 'bin', options['extension']) + EXT_SCT, 00598 os.path.join(options['prefix'], options['extension']) + EXT_SCT) 00599 # fix script path 00600 for line in fileinput.FileInput(os.path.join(options['prefix'], options['extension']) + EXT_SCT, 00601 inplace = True): 00602 line = line.replace("/scripts", "") 00603 print line 00604 00605 # move man/ into docs/ 00606 if os.path.exists(os.path.join(options['prefix'], 'man', 'man1', options['extension'] + '.1')): 00607 shutil.move(os.path.join(options['prefix'], 'man', 'man1', options['extension'] + '.1'), 00608 os.path.join(options['prefix'], 'docs', 'man', 'man1', options['extension'] + '.1')) 00609 00610 # if empty, rmdir bin, etc, man, scripts 00611 for d in ('bin', 'etc', os.path.join('man', 'man1'), 'man', 'scripts'): 00612 remove_empty_dir(os.path.join(options['prefix'], d)) 00613 00614 # update local meta-file when removing existing extension 00615 def remove_extension_xml(): 00616 fXML = os.path.join(options['prefix'], 'modules.xml') 00617 if not os.path.exists(fXML): 00618 return 00619 00620 # read XML file 00621 fo = open(fXML, 'r') 00622 tree = etree.fromstring(fo.read()) 00623 fo.close() 00624 00625 tnode = None 00626 for node in tree.findall('task'): 00627 if node.get('name') == options['extension']: 00628 tnode = node 00629 break 00630 00631 if tnode is not None: 00632 tree.remove(tnode) 00633 00634 write_xml_modules(fXML, tree) 00635 00636 # remove existing extension (reading XML file) 00637 def remove_extension(force = False): 00638 # try to read XML metadata file first 00639 fXML = os.path.join(options['prefix'], 'modules.xml') 00640 name = options['extension'] 00641 if name not in get_installed_extensions(): 00642 grass.warning(_("Extension <%s> not found") % name) 00643 00644 if force: 00645 grass.verbose(_("List of removed files:")) 00646 else: 00647 grass.info(_("Files to be removed (use flag 'f' to force removal):")) 00648 00649 if os.path.exists(fXML): 00650 f = open(fXML, 'r') 00651 tree = etree.fromstring(f.read()) 00652 flist = [] 00653 for task in tree.findall('task'): 00654 if name == task.get('name', default = '') and \ 00655 task.find('binary') is not None: 00656 for f in task.find('binary').findall('file'): 00657 flist.append(f.text) 00658 00659 if flist: 00660 removed = False 00661 err = list() 00662 for fpath in flist: 00663 try: 00664 if force: 00665 grass.verbose(fpath) 00666 os.remove(fpath) 00667 removed = True 00668 else: 00669 print fpath 00670 except OSError: 00671 err.append((_("Unable to remove file '%s'") % fpath)) 00672 if force and not removed: 00673 grass.fatal(_("Extension <%s> not found") % options['extension']) 00674 00675 if err: 00676 for e in err: 00677 grass.error(e) 00678 else: 00679 remove_extension_std(force) 00680 else: 00681 remove_extension_std(force) 00682 00683 if force: 00684 grass.message(_("Updating metadata file...")) 00685 remove_extension_xml() 00686 grass.message(_("Extension <%s> successfully uninstalled.") % options['extension']) 00687 else: 00688 grass.warning(_("Extension <%s> not removed.\n" 00689 "Re-run '%s' with 'f' flag to force removal") % (options['extension'], 'g.extension')) 00690 00691 # remove exising extension (using standard files layout) 00692 def remove_extension_std(force = False): 00693 # try even if module does not seem to be available, 00694 # as the user may be trying to get rid of left over cruft 00695 for fpath in [os.path.join(options['prefix'], options['extension']), 00696 os.path.join(options['prefix'], 'bin', options['extension']), 00697 os.path.join(options['prefix'], 'scripts', options['extension']), 00698 os.path.join(options['prefix'], 'docs', 'html', options['extension'] + '.html'), 00699 os.path.join(options['prefix'], 'docs', 'man', 'man1', options['extension'] + '.1'), 00700 os.path.join(options['prefix'], 'man', 'man1', options['extension'] + '.1')]: 00701 if os.path.isfile(fpath): 00702 if force: 00703 grass.verbose(fpath) 00704 os.remove(fpath) 00705 else: 00706 print fpath 00707 00708 # check links in CSS 00709 def check_style_files(fil): 00710 dist_file = os.path.join(os.getenv('GISBASE'), 'docs', 'html', fil) 00711 addons_file = os.path.join(options['prefix'], 'docs', 'html', fil) 00712 00713 if os.path.isfile(addons_file): 00714 return 00715 00716 try: 00717 shutil.copyfile(dist_file,addons_file) 00718 except OSError, e: 00719 grass.fatal(_("Unable to create '%s': %s") % (addons_file, e)) 00720 00721 def create_dir(path): 00722 if os.path.isdir(path): 00723 return 00724 00725 try: 00726 os.makedirs(path) 00727 except OSError, e: 00728 grass.fatal(_("Unable to create '%s': %s") % (path, e)) 00729 00730 grass.debug("'%s' created" % path) 00731 00732 def check_dirs(): 00733 create_dir(os.path.join(options['prefix'], 'bin')) 00734 create_dir(os.path.join(options['prefix'], 'docs', 'html')) 00735 check_style_files('grass_logo.png') 00736 check_style_files('grassdocs.css') 00737 # create_dir(os.path.join(options['prefix'], 'etc')) 00738 create_dir(os.path.join(options['prefix'], 'docs', 'man', 'man1')) 00739 create_dir(os.path.join(options['prefix'], 'man', 'man1')) 00740 create_dir(os.path.join(options['prefix'], 'scripts')) 00741 00742 def main(): 00743 # check dependecies 00744 if sys.platform != "win32": 00745 check_progs() 00746 00747 # define path 00748 if flags['s']: 00749 options['prefix'] = os.environ['GISBASE'] 00750 if options['prefix'] == '$GRASS_ADDON_PATH': 00751 if not os.environ.has_key('GRASS_ADDON_PATH') or \ 00752 not os.environ['GRASS_ADDON_PATH']: 00753 major_version = int(grass.version()['version'].split('.', 1)[0]) 00754 grass.warning(_("GRASS_ADDON_PATH is not defined, " 00755 "installing to ~/.grass%d/addons/") % major_version) 00756 options['prefix'] = os.path.join(os.environ['HOME'], '.grass%d' % major_version, 'addons') 00757 else: 00758 path_list = os.environ['GRASS_ADDON_PATH'].split(os.pathsep) 00759 if len(path_list) < 1: 00760 grass.fatal(_("Invalid GRASS_ADDON_PATH value - '%s'") % os.environ['GRASS_ADDON_PATH']) 00761 if len(path_list) > 1: 00762 grass.warning(_("GRASS_ADDON_PATH has more items, using first defined - '%s'") % path_list[0]) 00763 options['prefix'] = path_list[0] 00764 00765 # list available modules 00766 if flags['l'] or flags['c'] or flags['g']: 00767 list_available_extensions() 00768 return 0 00769 elif flags['a']: 00770 elist = get_installed_extensions() 00771 if elist: 00772 grass.message(_("List of installed extensions:")) 00773 sys.stdout.write('\n'.join(elist)) 00774 sys.stdout.write('\n') 00775 else: 00776 grass.info(_("No extension installed")) 00777 return 0 00778 else: 00779 if not options['extension']: 00780 grass.fatal(_('You need to define an extension name or use -l')) 00781 00782 if flags['d']: 00783 if options['operation'] != 'add': 00784 grass.warning(_("Flag 'd' is relevant only to 'operation=add'. Ignoring this flag.")) 00785 else: 00786 global remove_tmpdir 00787 remove_tmpdir = False 00788 00789 if options['operation'] == 'add': 00790 check_dirs() 00791 install_extension() 00792 else: # remove 00793 remove_extension(flags['f']) 00794 00795 return 0 00796 00797 if __name__ == "__main__": 00798 options, flags = grass.parser() 00799 global tmpdir 00800 tmpdir = grass.tempdir() 00801 atexit.register(cleanup) 00802 sys.exit(main())