[go: up one dir, main page]

Menu

[r6120]: / trunk / bin / rossync  Maximize  Restore  History

Download this file

200 lines (174 with data), 7.4 kB

#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2009, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following
#    disclaimer in the documentation and/or other materials provided
#    with the distribution.
#  * Neither the name of the Willow Garage nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Revision $Id$

import os
import sys

import roslib.packages
import roslib.scriptutil as s
from subprocess import Popen, PIPE

from optparse import OptionParser
parser = OptionParser(usage="usage: %prog machine package(s)", prog='rossync')
parser.add_option("-3", "--include-3rdparty",
                  dest="thirdparty", default=False, action="store_true",
                  help="include 3rdparty package in sync")
parser.add_option("-n", "--dry-run",
                  dest="dryrun", default=False, action="store_true",
                  help="dry run; print what would be done")
parser.add_option("-v", "--verbose",
                  dest="verbose", default=False, action="store_true",
                  help="verbose output")
parser.add_option("-s", "--no-svn",
                  dest="nosvn", default=False, action="store_true",
                  help="don't include .svn directories in intermediate directories")
parser.add_option("-f", "--force",
                  dest="force", default=False, action="store_true",
                  help="overwrite files on the remote machine, even if they are newer than those on the local machine (DANGEROUS)")
parser.add_option("-p", "--progress",
                  dest="progress", default=False, action="store_true",
                  help="show progress during rsync (same as --progress option to rsync)")
parser.add_option("-d", "--no-deps",
                  dest="nodeps", default=False, action="store_true",
                  help="sync only the named packages, without their dependencies")

options, args = parser.parse_args()

# TODO: Expose a convenient way to extend and/or replace this pattern list
ignore_patterns = ['.*.swp', '.svn/lock']

if len(args) < 2:
    parser.error("please specify a machine and target package(s)")
machine = args[0]
targets = args[1:]

# Does the user have rsync installed?
command = ['which', 'rsync']
res = Popen(command, stdout=PIPE).communicate()[0]
if res == '':
  print 'ERROR: Cannot execute rsync.  Please install it.'
  print '       On Ubuntu / Debian systems:'
  print '         sudo apt-get install rsync'
  sys.exit(-1)

# load up the package directory cache
roslib.packages.list_pkgs()

# Gather the roots that we may need to create on the other side
roots = [os.environ['ROS_ROOT']]
for d in os.environ['ROS_PACKAGE_PATH'].split(':'):
  if d != '':
    roots.append(d)

if options.nodeps:
  pkgs = targets
  pkgs = list(set(pkgs)) #uniq
  pkg_dirs = [roslib.packages.get_pkg_dir(p) for p in pkgs]
else:
  print "[rossync] Computing dependencies"
  # Start with packages that everyone needs, but noone depends on
  pkgs = ['rospack', 'rosbash', 'rostopic', 'rosout', 'mk', 'rosbuild', 'rosviz', 'rosparam']
  # For now we must have all client libraries installed, because rosbuild
  # fails if it doesn't find any of them; this will be fixed.
  pkgs.extend(['roscpp', 'rospy', 'roslisp', 'rosoct'])
  for t in targets:
      pkgs.append(t)
      pkgs.extend(s.rospack_depends(t))

  pkgs = list(set(pkgs)) #uniq

  pkg_dirs = [roslib.packages.get_pkg_dir(p) for p in pkgs]
  # Also throw in non-package directories that everyone needs 
  pkg_dirs.extend([os.path.join(os.environ['ROS_ROOT'],'config'),
                   os.path.join(os.environ['ROS_ROOT'],'bin')])
        
sync_dirs = []
dir_map = {}
for p in pkg_dirs:
  # exclude thirdparty by default
  if not options.thirdparty and '3rdparty' in p:
    if options.verbose:
      print "[rossync] Excluding thirdparty package", p
    continue
  sync_dirs.append(p)

  newp = None
  for r in roots:
    nr = os.path.normpath(r)
    # Should really use os.path facilities here, but they don't seem to
    # offer a convenient way to split a path into a list of its component
    # pieces.  So for now we'll be UNIX-specific.
    if '/'.join(p.split('/')[0:len(nr.split('/'))]) == nr:
      newp = ['/'.join(p.split('/')[len(nr.split('/')):])]

      if not options.nosvn:
        d = os.path.dirname(p)
        while len(d) >= len(nr):
          svn = os.path.join(d,'.svn')
          if os.path.exists(svn):
            newsvn = '/'.join(svn.split('/')[len(nr.split('/')):])
            newp.append(newsvn)
            sync_dirs.append(newsvn)
          d = os.path.dirname(d)

      break
  
  if newp is None:
    print '[rossync] Failed to find valid root path for %s. Aborting' % (p)
    sys.exit(-1)


  if nr in dir_map:
    dir_map[nr].extend(newp)
  else:
    dir_map[nr] = newp

print "[rossync] Creating the following directories on %s" %(machine)
for r in roots:
  print "[rossync]    %s"%(r)
  command = ['ssh', machine, 'mkdir', '-p', r]
  command = ' '.join(command)
  if options.verbose:
    print "[rossync] Executing command", command
  if not options.dryrun:
    Popen(command, shell=True).communicate()[0]

if options.verbose:
  print "[rossync] Will sync %d directories: "%len(sync_dirs)+' '.join(sync_dirs)
else:
  print "[rossync] Will sync %d directories"%len(sync_dirs)

print "[rossync] This will require %d rsync command(s)"%len(dir_map.keys())
for parent_dir in dir_map:
  if options.verbose:
    print '[rossync] Changing directory to %s' % (parent_dir)
  os.chdir(parent_dir)
  command = ['rsync']
  if options.force:
    command.append('-aRze')
  else:
    command.append('-aRuze')
  command.append('ssh')
  if options.progress:
    command.append('--progress')
  for p in ignore_patterns:
    command.append('--exclude=%s'%(p))
  command = command + dir_map[parent_dir] + ["%s:%s"%(machine, parent_dir)]
  command = ' '.join(command)
  if options.verbose:
    print "[rossync] Executing command", command
  else:
    print "[rossync] Syncing %s"%(parent_dir)
  if not options.dryrun:
    Popen(command, shell=True).communicate()[0]

print 'Done.'