200 lines
7.2 KiB
Python
Executable File
200 lines
7.2 KiB
Python
Executable File
#!/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.'
|
|
|
|
|