multiproject_cli.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2010, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 from __future__ import print_function
00034 import sys
00035 import os
00036 import textwrap
00037 import shutil
00038 from optparse import OptionParser, IndentedHelpFormatter
00039 from common import samefile, select_element, select_elements, MultiProjectException
00040 from config_yaml import PathSpec
00041 
00042 import multiproject_cmd
00043 
00044 # implementation of single CLI commands (extracted for use in several overlapping scripts)
00045 
00046 __MULTIPRO_CMD_DICT__={
00047   "help"     : "provide help for commands",
00048   "init"     : "set up a directory as workspace",
00049   "info"     : "Overview of some entries",
00050   "merge"    : "merges your workspace with another config set",
00051   "set"      : "add or changes one entry from your workspace config",
00052   "update"   : "update or check out some of your config elements",
00053   "remove"   : "remove an entry from your workspace config, without deleting files",
00054   "snapshot" : "write a file specifying repositories to have the version they currently have",
00055   "diff"     : "print a diff over some SCM controlled entries",
00056   "status"   : "print the change status of files in some SCM controlled entries",
00057 }
00058 
00059 class IndentedHelpFormatterWithNL(IndentedHelpFormatter):
00060     def format_description(self, description):
00061         if not description: return ""
00062         desc_width = self.width - self.current_indent
00063         indent = " "*self.current_indent
00064         # the above is still the same
00065         bits = description.split('\n')
00066         formatted_bits = [
00067             textwrap.fill(bit,
00068                           desc_width,
00069                           initial_indent=indent,
00070                           subsequent_indent=indent)
00071             for bit in bits]
00072         result = "\n".join(formatted_bits) + "\n"
00073         return result 
00074 
00075 
00076 def _get_mode_from_options(parser, options):
00077     mode = 'prompt'
00078     if options.delete_changed:
00079         mode = 'delete'
00080     if options.abort_changed:
00081         if mode == 'delete':
00082             parser.error("delete-changed-uris is mutually exclusive with abort-changed-uris")
00083         mode = 'abort'
00084     if options.backup_changed != '':
00085         if mode == 'delete':
00086             parser.error("delete-changed-uris is mutually exclusive with backup-changed-uris")
00087         if mode == 'abort':
00088             parser.error("abort-changed-uris is mutually exclusive with backup-changed-uris")
00089         mode = 'backup'
00090     return mode
00091 
00092   
00093 
00094 class MultiprojectCLI:
00095 
00096     def __init__(self, config_filename = None):
00097         self.config_filename = config_filename
00098 
00099     # def cmd_init(self, argv):
00100     #     raise Exception("Not implemented yet")
00101     # TODO enable when making multiproject an independent CLI
00102 
00103     
00104     # def cmd_merge(self, target_path, argv, config = None):
00105     #     raise Exception("Not implemented yet")
00106     # TODO enable when making multiproject an independent CLI
00107 
00108 
00109     def cmd_diff(self, target_path, argv, config = None):
00110         parser = OptionParser(usage="usage: rosws diff [localname]* ",
00111                               description=__MULTIPRO_CMD_DICT__["diff"],
00112                               epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
00113         # required here but used one layer above
00114         parser.add_option("-t", "--target-workspace", dest="workspace", default=None,
00115                           help="which workspace to use",
00116                           action="store")
00117         (options, args) = parser.parse_args(argv)
00118         
00119         if config == None:
00120             config = multiproject_cmd.get_config(target_path, [], config_filename = self.config_filename)
00121         elif config.get_base_path() != target_path:
00122             raise MultiProjectException("Config path does not match %s %s "%(config.get_base_path(), target_path))
00123 
00124         
00125         if len(args) > 0:
00126             difflist = multiproject_cmd.cmd_diff(config, localnames = args)
00127         else:
00128             difflist = multiproject_cmd.cmd_diff(config)
00129         alldiff = ""
00130         for entrydiff in difflist:
00131             if entrydiff['diff'] != None:
00132                 alldiff += entrydiff['diff']
00133         print(alldiff)
00134             
00135         return False
00136     
00137     
00138     def cmd_status(self, target_path, argv, config = None):
00139         parser = OptionParser(usage="usage: rosws status [localname]* ",
00140                               description=__MULTIPRO_CMD_DICT__["status"] + ". The status columns meanings are as the respective SCM defines them.",
00141                               epilog="""See: http://www.ros.org/wiki/rosinstall for details""")
00142         parser.add_option("--untracked", dest="untracked", default=False,
00143                           help="Also shows untracked files",
00144                           action="store_true")
00145         # -t option required here for help but used one layer above, see cli_common
00146         parser.add_option("-t", "--target-workspace", dest="workspace", default=None,
00147                           help="which workspace to use",
00148                           action="store")
00149         (options, args) = parser.parse_args(argv)
00150     
00151         if config == None:
00152             config = multiproject_cmd.get_config(target_path, [], config_filename = self.config_filename)
00153         elif config.get_base_path() != target_path:
00154             raise MultiProjectException("Config path does not match %s %s "%(config.get_base_path(), target_path))
00155 
00156         if len(args) > 0:
00157             statuslist = multiproject_cmd.cmd_status(config,
00158                                                      localnames = args,
00159                                                      untracked = options.untracked)
00160         else:
00161             statuslist = multiproject_cmd.cmd_status(config, untracked = options.untracked)
00162         allstatus=""
00163         for entrystatus in statuslist:
00164             if entrystatus['status'] != None:
00165                 allstatus += entrystatus['status']
00166         print(allstatus)
00167         return 0
00168 
00169     
00170     def cmd_set(self, target_path, argv, config = None):
00171         """
00172         command for modifying/adding a single entry
00173         :param target_path: where to look for config
00174         :param config: config to use instead of parsing file anew
00175         """
00176         parser = OptionParser(usage="usage: rosws set [localname] [SCM-URI]?  [--(detached|svn|hg|git|bzr)] [--version=VERSION]]",
00177                               formatter = IndentedHelpFormatterWithNL(),
00178                               description=__MULTIPRO_CMD_DICT__["set"] + """
00179 The command will infer whether you want to add or modify an entry. If
00180 you modify, it will only change the details you provide, keeping
00181 those you did not provide. if you only provide a uri, will use the
00182 basename of it as localname unless such an element already exists.
00183 
00184 The command only changes the configuration, to checkout or update
00185 the element, run rosws update afterwards.
00186 
00187 Examples:
00188 $ rosws set robot_model --hg https://kforge.ros.org/robotmodel/robot_model
00189 $ rosws set robot_model --version robot_model-1.7.1
00190 $ rosws set robot_model --detached
00191 """,
00192                               epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
00193         parser.add_option("--detached", dest="detach", default=False,
00194                           help="make an entry unmanaged (default for new element)",
00195                           action="store_true")
00196         parser.add_option("-v","--version-new", dest="version", default=None,
00197                           help="point SCM to this version",
00198                           action="store")
00199         parser.add_option("--git", dest="git", default=False,
00200                           help="make an entry a git entry",
00201                           action="store_true")
00202         parser.add_option("--svn", dest="svn", default=False,
00203                           help="make an entry a subversion entry",
00204                           action="store_true")
00205         parser.add_option("--hg", dest="hg", default=False,
00206                           help="make an entry a mercurial entry",
00207                           action="store_true")
00208         parser.add_option("--bzr", dest="bzr", default=False,
00209                           help="make an entry a bazaar entry",
00210                           action="store_true")
00211         parser.add_option("-y", "--confirm", dest="confirm", default='',
00212                           help="Do not ask for confirmation",
00213                           action="store_true")
00214         # -t option required here for help but used one layer above, see cli_common
00215         parser.add_option("-t", "--target-workspace", dest="workspace", default=None,
00216                           help="which workspace to use",
00217                           action="store")
00218         (options, args) = parser.parse_args(argv)
00219 
00220         if len(args) > 2:
00221             print("Error: Too many arguments.")
00222             print(parser.usage)
00223             return -1
00224         
00225         if config == None:
00226             config = multiproject_cmd.get_config(target_path, [], config_filename = self.config_filename)
00227         elif config.get_base_path() != target_path:
00228             raise MultiProjectException("Config path does not match %s %s "%(config.get_base_path(), target_path))
00229 
00230         scmtype = None
00231         count_scms = 0
00232         if options.git:
00233             scmtype = 'git'
00234             count_scms +=1
00235         if options.svn:
00236             scmtype = 'svn'
00237             count_scms +=1
00238         if options.hg:
00239             scmtype = 'hg'
00240             count_scms +=1
00241         if options.bzr:
00242             scmtype = 'bzr'
00243             count_scms +=1
00244         if options.detach:
00245             count_scms +=1
00246         if count_scms > 1:
00247             parser.error("You cannot provide more than one scm provider option")
00248           
00249         if len(args) == 0:
00250             parser.error("Must provide a localname")
00251         
00252         element = select_element(config.get_config_elements(), args[0])
00253 
00254         uri = None
00255         if len(args) == 2:
00256             uri = args[1]
00257         version = None
00258         if options.version is not None:
00259             version = options.version.strip("'\"")
00260         
00261         if element is None:
00262             # asssume is insert, choose localname
00263             localname = os.path.normpath(args[0])
00264             rel_path = os.path.relpath(os.path.realpath(localname),
00265                                        os.path.realpath(config.get_base_path()))
00266             if os.path.isabs(localname):
00267                 # use shorter localname for folders inside workspace
00268                 if not rel_path.startswith('..'):
00269                     localname = rel_path
00270             else:
00271                 # got a relative path as localname, could point to a dir or be meant relative to workspace
00272                 if not samefile(os.getcwd(), config.get_base_path()):
00273                     if os.path.isdir(localname):
00274                         parser.error("Cannot decide which one you want to add:\n%s\n%s"%(os.path.abspath(localname),
00275                                                                                          os.path.join(config.get_base_path(), localname)))
00276                     if not rel_path.startswith('..'):
00277                         localname = rel_path
00278 
00279             spec = PathSpec(local_name = localname,
00280                             uri = uri,
00281                             version = version,
00282                             scmtype = scmtype)
00283             print("     Add element: \n %s"%spec)
00284         else:
00285             # modify
00286             old_spec = element.get_path_spec()
00287             if options.detach:
00288                 spec = PathSpec(local_name = element.get_local_name())
00289             else:
00290                 # '' evals to False, we do not want that
00291                 if version is None:
00292                     version = old_spec.get_version()
00293                 spec = PathSpec(local_name = element.get_local_name(),
00294                                 uri = uri or old_spec.get_uri(),
00295                                 version = version,
00296                                 scmtype = scmtype or old_spec.get_scmtype(),
00297                                 path = old_spec.get_path())
00298             if spec.get_legacy_yaml() == old_spec.get_legacy_yaml():
00299                 if not options.detach:
00300                     parser.error("No change provided, did you mean --detach ?")
00301                 parser.error("No change provided.")
00302             print("     Change element from: \n %s\n     to\n %s"%(old_spec, spec))
00303 
00304         config.add_path_spec(spec, merge_strategy = 'MergeReplace')
00305         if not options.confirm:
00306             abort = None
00307             prompt = "Continue(y/n): "
00308             while abort == None:
00309                 mode_input = raw_input(prompt)
00310                 if mode_input == 'y':
00311                     abort = False
00312                 elif mode_input == 'n':
00313                     abort = True
00314             if abort:
00315                 print("No changes made.")
00316                 return 0
00317         print("Overwriting %s"%os.path.join(config.get_base_path(), self.config_filename))
00318         shutil.move(os.path.join(config.get_base_path(), self.config_filename), "%s.bak"%os.path.join(config.get_base_path(), self.config_filename))
00319         multiproject_cmd.cmd_persist_config(config, self.config_filename)
00320 
00321         if (spec.get_scmtype() is not None):
00322           print("Config changed, remember to run 'rosws update %s' to update the folder from %s"%(spec.get_local_name(), spec.get_scmtype()))
00323         # for element in config.get_config_elements():
00324         #   if element.get_local_name() == spec.get_local_name():
00325         #     if element.is_vcs_element():
00326         #       element.install(checkout = not os.path.exists(os.path.join(config.get_base_path(), spec.get_local_name())))
00327         #       break
00328         return 0
00329 
00330 
00331     def cmd_update(self, target_path, argv, config = None):
00332         parser = OptionParser(usage="usage: rosws update [localname]*",
00333                               formatter = IndentedHelpFormatterWithNL(),
00334                               description=__MULTIPRO_CMD_DICT__["update"] + """
00335 
00336 This command calls the SCM provider to pull changes from remote to
00337 your local filesystem. In case the url has changed, the command will
00338 ask whether to delete or backup the folder.
00339 
00340 Examples:
00341 $ rosws update -t ~/fuerte
00342 $ rosws update robot_model geometry
00343 """,
00344                               epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
00345         parser.add_option("--delete-changed-uris", dest="delete_changed", default=False,
00346                           help="Delete the local copy of a directory before changing uri.",
00347                           action="store_true")
00348         parser.add_option("--abort-changed-uris", dest="abort_changed", default=False,
00349                           help="Abort if changed uri detected",
00350                           action="store_true")
00351         parser.add_option("--continue-on-error", dest="robust", default=False,
00352                           help="Continue despite checkout errors",
00353                           action="store_true")
00354         parser.add_option("--backup-changed-uris", dest="backup_changed", default='',
00355                           help="backup the local copy of a directory before changing uri to this directory.",
00356                           action="store")
00357         parser.add_option("-j", "--parallel", dest="jobs", default=1,
00358                           help="How many parallel threads to use for installing",
00359                           action="store")
00360         parser.add_option("-v", "--verbose", dest="verbose", default=False,
00361                           help="Whether to print out more information",
00362                           action="store_true")
00363         # -t option required here for help but used one layer above, see cli_common
00364         parser.add_option("-t", "--target-workspace", dest="workspace", default=None,
00365                           help="which workspace to use",
00366                           action="store")
00367         (options, args) = parser.parse_args(argv)
00368 
00369         if config == None:
00370             config = multiproject_cmd.get_config(target_path, [], config_filename = self.config_filename)
00371         elif config.get_base_path() != target_path:
00372             raise MultiProjectException("Config path does not match %s %s "%(config.get_base_path(), target_path))
00373         success = True
00374         mode = _get_mode_from_options(parser, options)
00375         if args == []:
00376             # None means no filter, [] means filter all
00377             args = None
00378         if success:
00379             install_success = multiproject_cmd.cmd_install_or_update(
00380               config,
00381               localnames = args,
00382               backup_path = options.backup_changed,
00383               mode = mode,
00384               robust = options.robust,
00385               num_threads = int(options.jobs),
00386               verbose = options.verbose)
00387             if install_success or options.robust:
00388                 return 0
00389         return 1
00390 
00391 
00392     def cmd_remove(self, target_path, argv, config = None):
00393         parser = OptionParser(usage="usage: rosws remove [localname]*",
00394                               formatter = IndentedHelpFormatterWithNL(),
00395                               description=__MULTIPRO_CMD_DICT__["remove"] + """
00396 The command removes entries from your configuration file, it does not affect your filesystem.
00397 """,
00398                               epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
00399         (options, args) = parser.parse_args(argv)
00400         if len(args) < 1:
00401             print("Error: Too few arguments.")
00402             print(parser.usage)
00403             return -1
00404 
00405         if config == None:
00406             config = multiproject_cmd.get_config(target_path, [], config_filename = self.config_filename)
00407         elif config.get_base_path() != target_path:
00408             raise MultiProjectException("Config path does not match %s %s "%(config.get_base_path(), target_path))
00409         success = True
00410         elements = select_elements(config, args)
00411         for element in elements:
00412             if not config.remove_element(element.get_local_name()):
00413                 success = False
00414                 print("Bug: No such element %s in config, aborting without changes"%(element.get_local_name()))
00415                 break
00416         if success:
00417             print("Overwriting %s"%os.path.join(config.get_base_path(), self.config_filename))
00418             shutil.move(os.path.join(config.get_base_path(), self.config_filename), "%s.bak"%os.path.join(config.get_base_path(), self.config_filename))
00419             multiproject_cmd.cmd_persist_config(config, self.config_filename)
00420             print("Removed entries %s"%args)
00421             
00422         return 0
00423 
00424     # def cmd_info(self, target_path, argv, reverse = False, config = None):
00425     #     """
00426     #     :param target_path: where to look for config
00427     #     :param config: config to use instead of parsing file anew
00428     #     """
00429     #     __pychecker__ = 'unusednames=reverse'
00430     #     raise Exception("Not implemented yet")
00431     # TODO enable when making multiproject an independent CLI
00432 
00433 
00434     def _get_element_diff(self, new_path_spec, config_old, extra_verbose = False):
00435         """
00436         :returns: a string telling what changed for element compared to old config
00437         """
00438         if new_path_spec is None or config_old is None:
00439             return ''
00440         output = ' %s'%new_path_spec.get_local_name()
00441         if extra_verbose:
00442             old_element = None
00443             if config_old is not None:
00444                 old_element = select_element(config_old.get_config_elements(), new_path_spec.get_local_name())
00445 
00446             if old_element is None:
00447                 if new_path_spec.get_scmtype() is not None:
00448                   output += "   \t%s  %s   %s"%(new_path_spec.get_scmtype(), new_path_spec.get_uri(), new_path_spec.get_version() or '')
00449             else:
00450                 old_path_spec = old_element.get_path_spec()
00451                 for attr in new_path_spec.__dict__:
00452                     if (attr in old_path_spec.__dict__
00453                         and old_path_spec.__dict__[attr] != new_path_spec.__dict__[attr]
00454                         and attr[1:] not in ['local_name', 'path']):
00455                       old_val = old_path_spec.__dict__[attr]
00456                       new_val = new_path_spec.__dict__[attr]
00457                       
00458                       commonprefix = new_val[:[x[0]==x[1] for x in zip(str(new_val), str(old_val))].index(0)]
00459                       if len(commonprefix) > 11:
00460                           new_val = "...%s"%new_val[len(commonprefix)-7:]
00461                       output += "  \t%s: %s -> %s;"%(attr[1:], old_val, new_val)
00462                     else:
00463                       if (attr not in old_path_spec.__dict__
00464                           and new_path_spec.__dict__[attr] is not None
00465                           and new_path_spec.__dict__[attr] != ""
00466                           and new_path_spec.__dict__[attr] != []
00467                           and attr[1:] not in ['local_name', 'path']):
00468                         output += "  %s = %s"%(attr[1:], new_path_spec.__dict__[attr])
00469         return output
00470           
00471 
00472     def prompt_merge(self,
00473                      target_path,
00474                      additional_uris,
00475                      additional_specs,
00476                      path_change_message = None,
00477                      merge_strategy = 'KillAppend',
00478                      confirmed = False,
00479                      config = None):
00480         """
00481         Prompts the user for the resolution of a merge
00482         
00483         :param target_path: Location of the config workspace
00484         :param additional_uris: what needs merging in
00485         :returns: tupel (Config or None if no change, bool path_changed)
00486         """
00487         if config == None:
00488             config = multiproject_cmd.get_config(target_path, additional_uris = [], config_filename = self.config_filename)
00489         elif config.get_base_path() != target_path:
00490             raise MultiProjectException("Config path does not match %s %s "%(config.get_base_path(), target_path))
00491         local_names_old = [x.get_local_name() for x in config.get_config_elements()]
00492 
00493         extra_verbose = confirmed
00494         abort = None
00495         last_merge_strategy = None
00496         while abort == None:
00497 
00498             if (last_merge_strategy is None
00499                 or last_merge_strategy != merge_strategy):
00500               newconfig = multiproject_cmd.get_config(target_path, additional_uris = [], config_filename = self.config_filename)
00501               config_actions = multiproject_cmd.add_uris(newconfig,
00502                                                          additional_uris = additional_uris,
00503                                                          merge_strategy = merge_strategy)
00504               for path_spec in additional_specs:
00505                   action = newconfig.add_path_spec(path_spec, merge_strategy)
00506                   config_actions[path_spec.get_local_name()] = (action, path_spec)
00507               last_merge_strategy = merge_strategy
00508             
00509             local_names_new = [x.get_local_name() for x in newconfig.get_config_elements()]
00510             
00511             path_changed = False
00512             ask_user = False
00513             output = ""
00514             new_elements = []
00515             changed_elements = []
00516             discard_elements = []
00517             for localname, (action, new_path_spec) in config_actions.items():
00518                 index = -1
00519                 if localname in local_names_old:
00520                     index = local_names_old.index(localname)
00521                 if action == 'KillAppend':
00522                     ask_user = True
00523                     if (index > -1 and local_names_old[:index+1] == local_names_new[:index+1]):
00524                         action = 'MergeReplace'
00525                     else:
00526                         changed_elements.append(self._get_element_diff(new_path_spec, config, extra_verbose))
00527                         path_changed = True
00528      
00529                 if action == 'Append':
00530                     path_changed = True
00531                     new_elements.append(self._get_element_diff(new_path_spec, config, extra_verbose))
00532                 elif action == 'MergeReplace':
00533                     changed_elements.append(self._get_element_diff(new_path_spec, config, extra_verbose))
00534                     ask_user = True
00535                 elif action == 'MergeKeep':
00536                     discard_elements.append(self._get_element_diff(new_path_spec, config, extra_verbose))
00537                     ask_user = True
00538             if len(changed_elements) > 0:
00539               output += "\n     Change details of element (Use --merge-keep or --merge-replace to change):\n"
00540               if extra_verbose:
00541                 output += " %s\n"%"\n".join(changed_elements)
00542               else:
00543                 output += " %s\n"%", ".join(changed_elements)
00544             if len(new_elements) > 0:
00545                 output += "\n     Add new elements:\n"
00546                 if extra_verbose:
00547                   output += " %s\n"%"\n".join(new_elements)
00548                 else:
00549                   output += " %s\n"%", ".join(new_elements)
00550                 
00551      
00552             if local_names_old != local_names_new[:len(local_names_old)]:
00553                 old_order = ' '.join(reversed(local_names_old))
00554                 new_order = ' '.join(reversed(local_names_new))
00555                 output += "\n     %s (Use --merge-keep or --merge-replace to prevent) from\n %s\n     to\n %s\n\n"%(path_change_message or "Element Order change", old_order, new_order)
00556                 ask_user = True
00557      
00558             if output == "":
00559                 return (None, False)
00560             if confirmed or not ask_user:
00561                 print("     Performing actions: ")
00562                 print(output)
00563                 return (newconfig, path_changed)
00564             else:
00565                 print(output)
00566                 showhelp = True
00567                 while(showhelp):
00568                     showhelp = False
00569                     prompt = "Continue: (y)es, (n)o, (v)erbosity, (a)dvance options: "
00570                     mode_input = raw_input(prompt)
00571                     if mode_input == 'y':
00572                         return (newconfig, path_changed)
00573                     elif mode_input == 'n':
00574                         abort = True
00575                     elif mode_input == 'a':
00576                         strategies = {'MergeKeep': "(k)eep",
00577                                       'MergeReplace': "(s)witch in",
00578                                       'KillAppend': "(a)ppending"}
00579                         unselected = [strategies[x] for x in strategies if x != merge_strategy]
00580                         print( """New entries will just be appended to the config and
00581 appear at the beginning of your ROS_PACKAGE_PATH. The merge strategy
00582 decides how to deal with entries having a duplicate localname or path.
00583 
00584 "(k)eep" means the existing entry will stay as it is, the new one will
00585 be discarded. Useful for getting additional elements from other
00586 workspaces without affecting your setup.
00587 
00588 "(s)witch in" means that the new entry will replace the old in the
00589 same position. Useful for upgrading/downgrading.
00590 
00591 "switch (a)ppend" means that the existing entry will be removed, and
00592 the new entry appended to the end of the list. This maintains order
00593 of elements in the order they were given.
00594 
00595 Switch append is the default.
00596 """)
00597                         prompt = """Change Strategy %s: """%", ".join(unselected)
00598                         mode_input = raw_input(prompt)
00599                         if mode_input == 's':
00600                             merge_strategy = 'MergeReplace'
00601                         elif mode_input == 'k':
00602                             merge_strategy = 'MergeKeep'
00603                         elif mode_input == 'a':
00604                             merge_strategy = 'KillAppend'
00605                         
00606                     elif mode_input == 'v':
00607                       extra_verbose = not extra_verbose
00608             if abort:
00609                 print("No changes made.")
00610                 return (None, False)
00611             print('==========================================')
00612           


win_rosinstall
Author(s): Daniel Stonier
autogenerated on Fri Jan 3 2014 12:16:33