pax_global_header 0000666 0000000 0000000 00000000064 13542237257 0014524 g ustar 00root root 0000000 0000000 52 comment=42ce038de31a8d456982c2bbd1f31725484e73a5
wstool-0.1.18/ 0000775 0000000 0000000 00000000000 13542237257 0013142 5 ustar 00root root 0000000 0000000 wstool-0.1.18/.gitignore 0000664 0000000 0000000 00000000213 13542237257 0015126 0 ustar 00root root 0000000 0000000 *#
*.DS_Store
*.coverage
*.deb
*.egg-info
*.eggs
*.log
*.orig
*.pyc
*.swp
*.tgz
*~
.tox
build/*
description-pak
dist
doc-pak
nosetests.xml
wstool-0.1.18/.travis.yml 0000664 0000000 0000000 00000001137 13542237257 0015255 0 ustar 00root root 0000000 0000000 language: python
python:
- "2.7"
- "3.5"
- "3.6"
# Add test requirements; travis will do the rest
before_install:
- cat requirements-test.txt >> requirements.txt
install:
- sudo apt-get update
- pip install -U setuptools tox tox-travis
- echo $PYTHONPATH
- python -c 'import sys; print(sys.path)'
- python setup.py install
- python -c 'import coverage'
# command to run tests
script:
- tox
# Install rosinstall from pip and perform a sanity check
- pip install --upgrade rosinstall wstool
- rosws
- wstool --help
notifications:
email: false
after_success:
- coveralls
wstool-0.1.18/CONTRIBUTING.rst 0000664 0000000 0000000 00000004617 13542237257 0015613 0 ustar 00root root 0000000 0000000 Contributing guide
==================
Thanks for your interest in contributing to wstool.
Any kinds of contributions are welcome: Bug reports, Documentation, Patches.
The core functionality of abstracting over different version control systems is contained in the library project https://github.com/vcstools/vcstools.
Developer Environment
---------------------
For many tasks, it is okay to just develop using a single installed python version. But if you need to test/debug the project in multiple python versions, you need to install those versions::
1. (Optional) Install multiple python versions
1. (Optional) Install [pyenv](https://github.com/pyenv/pyenv-installer) to manage python versions
2. (Optional) Using pyenv, install the python versions used in testing::
pyenv install 2.7.16
pyenv install 3.6.8
It may be okay to run and test python against locally installed libraries, but if you need to have a consistent build, it is recommended to manage your environment using `virtualenv `_::
$ virtualenv ~/wstool_venv
$ source ~/wstool_venv/bin/activate
Testing
-------
Prerequisites:
* The tests require git, mercurial, bazaar and subversion to be installed.
Also you need to install python test support libraries::
# install python dependencies
$ pip install .[test]
# optional also use local vcstools sources directly
$ pip install --editable /path/to/vcstools_source
Then you can use different commands to run various test scopes::
# run all tests using nose
$ nosetests
# run one test using nose
$ nosetests {testname}
# run all tests with coverage check
$ python setup.py test
# run all tests using python3
$ python3 setup.py test
# run all tests against multiple python versions (same as in travis)
$ tox
Releasing
---------
* Upgrade vcstools dependency version in `requirements.txt`
* Update `src/vcstools/__version__.py`
* Check `doc/changelog` is up to date
* Check `stdeb.cfg` is up to date with OSRF buildfarm distros
* prepare release dependencies::
pip install --upgrade setuptools wheel twine
* Upload to testpypi::
python3 setup.py sdist bdist_wheel
twine upload --repository testpypi dist/*
* Check testpypi download files and documentation look ok
* Actually release::
twine upload dist/*
* Create and push tag::
git tag x.y.z
git push
git push --tags
wstool-0.1.18/LICENSE 0000664 0000000 0000000 00000003105 13542237257 0014146 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
wstool-0.1.18/MANIFEST.in 0000664 0000000 0000000 00000000475 13542237257 0014706 0 ustar 00root root 0000000 0000000 include *.py
include LICENSE
recursive-include completion *
recursive-include doc *
include requirements.txt
include requirements-test.txt
global-exclude .tox
global-exclude *~
global-exclude __pycache__
global-exclude .coverage
global-exclude *.py[co]
global-exclude *.db
global-exclude .git*
global-exclude *.orig
wstool-0.1.18/Makefile 0000664 0000000 0000000 00000001063 13542237257 0014602 0 ustar 00root root 0000000 0000000 .PHONY: all setup clean_dist distro clean install testsetup test
NAME='wstool'
VERSION=$(shell grep version ./src/wstool/__version__.py | sed 's,version = ,,')
OUTPUT_DIR=deb_dist
all:
echo "noop for debbuild"
setup:
echo "building version ${VERSION}"
clean_dist:
-rm -rf src/wstool.egg-info
-rm -rf dist
-rm -rf deb_dist
distro: setup clean_dist
python setup.py sdist
clean: clean_dist
install: distro
sudo checkinstall python setup.py install
testsetup:
echo "running tests"
test: testsetup
nosetests --with-coverage --cover-package=wstool
wstool-0.1.18/README.rst 0000664 0000000 0000000 00000002322 13542237257 0014630 0 ustar 00root root 0000000 0000000 wstool
==========
.. image:: https://travis-ci.org/vcstools/wstool.svg?branch=master
:target: https://travis-ci.org/vcstools/wstool
.. image:: https://coveralls.io/repos/github/wstool/wstool/badge.svg?branch=master
:target: https://coveralls.io/github/wstool/wstool?branch=master
.. image:: https://img.shields.io/pypi/v/wstool.svg
:target: https://pypi.python.org/pypi/wstool
.. image:: https://img.shields.io/pypi/pyversions/wstool.svg
:target: https://pypi.python.org/pypi/wstool
.. image:: https://img.shields.io/pypi/status/wstool.svg
:target: https://pypi.python.org/pypi/wstool
.. image:: https://img.shields.io/pypi/l/wstool.svg
:target: https://pypi.python.org/pypi/wstool
.. image:: https://img.shields.io/pypi/dd/wstool.svg
:target: https://pypi.python.org/pypi/wstool
.. image:: https://img.shields.io/pypi/dw/wstool.svg
:target: https://pypi.python.org/pypi/wstool
.. image:: https://img.shields.io/pypi/dm/wstool.svg
:target: https://pypi.python.org/pypi/wstool
Command-line tools for maintaining a workspace of projects from multiple version-control systems.
Also see http://wiki.ros.org/wstool.
Installing
----------
Using the pypi package::
$ pip install -U wstool
wstool-0.1.18/completion/ 0000775 0000000 0000000 00000000000 13542237257 0015313 5 ustar 00root root 0000000 0000000 wstool-0.1.18/completion/_wstool 0000664 0000000 0000000 00000003320 13542237257 0016722 0 ustar 00root root 0000000 0000000 #compdef wstool
# Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
_wstool () {
local e
e=$(dirname ${funcsourcetrace[1]%:*})/wstool-completion.bash
if [ -f $e ]; then
. $e
fi
}
wstool-0.1.18/completion/wstool-completion.bash 0000664 0000000 0000000 00000017105 13542237257 0021654 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
# Programmable completion for the wstool command under bash. Source
# this file (or on some systems add it to ~/.bash_completion and start a new
# shell)
# ZSH support
if [[ -n ${ZSH_VERSION-} ]]; then
autoload -U +X bashcompinit && bashcompinit
fi
# put here to be extendable
if [ -z "$WSTOOL_BASE_COMMANDS" ]; then
_WSTOOL_BASE_COMMANDS="help init set merge info remove diff status update export --version"
fi
# Based originally on the bzr/svn bash completition scripts.
_wstool_complete()
{
local cur cmds cmdOpts opt helpCmds optBase i
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
cmds=$_WSTOOL_BASE_COMMANDS
if [[ $COMP_CWORD -eq 1 ]] ; then
COMPREPLY=( $( compgen -W "$cmds" -- $cur ) )
return 0
fi
# if not typing an option, or if the previous option required a
# parameter, then fallback on ordinary filename expansion
helpCmds='help|--help|h|\?'
if [[ ${COMP_WORDS[1]} != @@($helpCmds) ]] && \
[[ "$cur" != -* ]] ; then
case ${COMP_WORDS[1]} in
info|diff|di|status|st|remove|rm|update|up)
cmdOpts=`wstool info --only=localname 2> /dev/null | sed 's,:, ,g'`
COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
;;
set)
if [[ $COMP_CWORD -eq 2 ]]; then
cmdOpts=`wstool info --only=localname 2> /dev/null | sed 's,:, ,g'`
COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
elif [[ $COMP_CWORD -eq 3 ]]; then
cmdOpts=`wstool info ${COMP_WORDS[2]} --only=uri 2> /dev/null`
COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
else
if [[ ${COMP_WORDS[$(( $COMP_CWORD - 1 ))]} == "--version-new" ]]; then
cmdOpts=`wstool info ${COMP_WORDS[2]} --only=version 2> /dev/null|sed 's/,$//'`
COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
fi
fi
;;
esac
return 0
fi
cmdOpts=
case ${COMP_WORDS[1]} in
status|st)
cmdOpts="-t --target-workspace --untracked"
;;
diff|di)
cmdOpts="-t --target-workspace"
;;
init)
cmdOpts="-t --target-workspace --continue-on-error"
;;
merge)
cmdOpts="-t --target-workspace -y --confirm-all -r --merge-replace -k --merge-keep -a --merge-kill-append"
;;
set)
cmdOpts="-t --target-workspace --git --svn --bzr --hg --uri -v --version-new --detached -y --confirm"
;;
remove|rm)
cmdOpts="-t --target-workspace"
;;
update|up)
cmdOpts="-t --target-workspace --delete-changed-uris --abort-changed-uris --backup-changed-uris"
;;
export)
cmdOpts="-t --target-workspace -o --output --exact --spec"
;;
info)
cmdOpts="-t --target-workspace --data-only --no-pkg-path --pkg-path-only --only --yaml -u --untracked --fetch -s --short --root -m --managed-only"
;;
*)
;;
esac
cmdOpts="$cmdOpts --help -h"
# take out options already given
for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do
opt=${COMP_WORDS[$i]}
case $opt in
--*) optBase=${opt/=*/} ;;
-*) optBase=${opt:0:2} ;;
esac
cmdOpts=" $cmdOpts "
cmdOpts=${cmdOpts/ ${optBase} / }
# take out alternatives
case $optBase in
-h) cmdOpts=${cmdOpts/ --help / } ;;
--help) cmdOpts=${cmdOpts/ -h / } ;;
-t) cmdOpts=${cmdOpts/ --target-workspace / } ;;
--target-workspace) cmdOpts=${cmdOpts/ -t / } ;;
--delete-changed-uris)
cmdOpts=${cmdOpts/ --abort-changed-uris / }
cmdOpts=${cmdOpts/ --backup-changed-uris / }
;;
--abort-changed-uris)
cmdOpts=${cmdOpts/ --delete-changed-uris / }
cmdOpts=${cmdOpts/ --backup-changed-uris / }
;;
--backup-changed-uris)
cmdOpts=${cmdOpts/ --delete-changed-uris / }
cmdOpts=${cmdOpts/ --abort-changed-uris / }
;;
# scm options
--svn)
cmdOpts=${cmdOpts/ --git / }
cmdOpts=${cmdOpts/ --hg / }
cmdOpts=${cmdOpts/ --bzr / }
cmdOpts=${cmdOpts/ --detached / }
;;
--git)
cmdOpts=${cmdOpts/ --svn / }
cmdOpts=${cmdOpts/ --hg / }
cmdOpts=${cmdOpts/ --bzr / }
cmdOpts=${cmdOpts/ --detached / }
;;
--hg)
cmdOpts=${cmdOpts/ --git / }
cmdOpts=${cmdOpts/ --svn / }
cmdOpts=${cmdOpts/ --bzr / }
cmdOpts=${cmdOpts/ --detached / }
;;
--bzr)
cmdOpts=${cmdOpts/ --git / }
cmdOpts=${cmdOpts/ --hg / }
cmdOpts=${cmdOpts/ --svn / }
cmdOpts=${cmdOpts/ --detached / }
;;
--detached)
cmdOpts=${cmdOpts/ --git / }
cmdOpts=${cmdOpts/ --hg / }
cmdOpts=${cmdOpts/ --bzr / }
cmdOpts=${cmdOpts/ --svn / }
;;
# merge options
--merge-replace)
cmdOpts=${cmdOpts/ --merge-keep / }
cmdOpts=${cmdOpts/ --merge-kill-append / }
cmdOpts=${cmdOpts/ -r / }
cmdOpts=${cmdOpts/ -a / }
cmdOpts=${cmdOpts/ -k / }
;;
--merge-keep)
cmdOpts=${cmdOpts/ --merge-replace / }
cmdOpts=${cmdOpts/ --merge-kill-append / }
cmdOpts=${cmdOpts/ -r / }
cmdOpts=${cmdOpts/ -a / }
cmdOpts=${cmdOpts/ -k / }
;;
--merge-kill-append)
cmdOpts=${cmdOpts/ --merge-keep / }
cmdOpts=${cmdOpts/ --merge-replace / }
cmdOpts=${cmdOpts/ -r / }
cmdOpts=${cmdOpts/ -a / }
cmdOpts=${cmdOpts/ -k / }
;;
-r)
cmdOpts=${cmdOpts/ --merge-keep / }
cmdOpts=${cmdOpts/ --merge-kill-append / }
cmdOpts=${cmdOpts/ --merge-replace / }
cmdOpts=${cmdOpts/ -a / }
cmdOpts=${cmdOpts/ -k / }
;;
-a)
cmdOpts=${cmdOpts/ --merge-keep / }
cmdOpts=${cmdOpts/ --merge-kill-append / }
cmdOpts=${cmdOpts/ --merge-replace / }
cmdOpts=${cmdOpts/ -r / }
cmdOpts=${cmdOpts/ -k / }
;;
-k)
cmdOpts=${cmdOpts/ --merge-keep / }
cmdOpts=${cmdOpts/ --merge-kill-append / }
cmdOpts=${cmdOpts/ --merge-replace / }
cmdOpts=${cmdOpts/ -a / }
cmdOpts=${cmdOpts/ -r / }
;;
esac
# skip next option if this one requires a parameter
if [[ $opt == @@($optsParam) ]] ; then
((++i))
fi
done
COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) )
return 0
}
complete -F _wstool_complete -o default wstool
wstool-0.1.18/doc/ 0000775 0000000 0000000 00000000000 13542237257 0013707 5 ustar 00root root 0000000 0000000 wstool-0.1.18/doc/Makefile 0000664 0000000 0000000 00000015512 13542237257 0015353 0 ustar 00root root 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wstool.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wstool.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/wstool"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wstool"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
upload: html
# set write permission for group so that everybody can overwrite existing files on the webserver
chmod -R g+w _build/html/
scp -pr _build/html/ rosbot@ros.osuosl.org:/home/rosbot/docs/independent/api/wstool
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
wstool-0.1.18/doc/changelog.rst 0000664 0000000 0000000 00000005622 13542237257 0016375 0 ustar 00root root 0000000 0000000 Changelog
=========
0.1.18
------
- fix warnings by replacing yaml.load() with safe_load()
- Re-add snapshot command called 'export' (#117, #120)
- fix '-t' option not working for wstool remove
- upgrade vcstools library version to 0.1.41, with new fixes:
- fix git submodule errors
- fix export_upstream for git submodules
- fix python3 incompatibility
- fix git fast-forward failures
- fix get_affected_files
0.1.17
------
- Reverted the snapshot command since it was breaking ``rosws`` until it can be fixed.
0.1.16
------
- Fixed some issues with new ``requirements.txt`` usage during the release process.
0.1.15
------
- Fixed an issue with the conditional dependency on ``ordereddict`` in the ``setup.py`` file.
0.1.14
------
- Fixed an issue which caused a traceback with invalid command line options.
- Added a feature to "snapshot" the current commit hashes in the workspace as a rosinstall file.
- Fixed option handling and documentation of the ``--untracked`` option.
- Added ``--shallow`` option to ``wstool init`` for shallow checkouts with git.
- Contributors: @cbandera, @rsinnet, @amiller27, @jayvdb, @at-wat
0.1.13
------
- Fix to avoid errors due to installing man pages with OS X's 10.11's new SIP settings.
- Added option to show a simplified version info table.
- Added the -m (timeout), -v (verbose), and -j (parallel jobs) options to each command.
- Contributors: @NikolausDemmel, @wkentaro
0.1.12
------
- Fix command line arguments of ``wstool scrape``.
0.1.11
------
- Changed the way ``.bak`` files are created when overwriting existing configurations.
- Added the Scrape command.
- Added default git branch and status to ``wstool fetch --info``.
- Added versioned dependency on vcstools ``0.1.38`` to make use of new API features.
0.1.10
------
- Fix regression which broke the -j option.
- Enable pretty printing of the ``.rosinstall`` file's YAML.
0.1.9
-----
- Fix for zsh completion.
- Fixed version dependency on vcstools for debian.
0.1.8
-----
- Fix for installation issue.
0.1.7
-----
- Added installation of generated man pages.
- Added installation of shell completion for wstool.
- Improved output of wstool info with the new get_current_version_label in vcstools.
- Added a foreach command.
- Added a ``--root`` option to wstool info.
- Enhanced the ``--update`` option for wstool set.
- Now uses multiple threads for network operations by default.
- Some other minor fixes and improvements and docs.
0.1.5
-----
- Releasing to allow changes for new platform vivid.
- Fix svn diff for change in output with svn 1.7.9.
- info command shows information about unmanaged paths.
0.1.4
-----
- Fix detection of path conflicts #24 (https://github.com/vcstools/wstool/pull/24).
0.0.3
-----
- not using ROS_WORKSPACE anymore
- fix to "wstool cmd --help"
0.0.2
-----
- fix #2 creating "wstool2 file instaed of ".rosinstall"
0.0.1
-----
- Initial creation based on functions inrosinstall
wstool-0.1.18/doc/conf.py 0000664 0000000 0000000 00000020674 13542237257 0015217 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# wstool documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 11 08:44:18 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import imp
file = None
try:
file, pathname, description = imp.find_module('__version__', ['../src/wstool'])
vermod = imp.load_module('__version__', file, pathname, description)
version = vermod.version
finally:
if file is not None:
file.close()
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'wstool'
copyright = u'2011, Willow Garage'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = version
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'haiku'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
# html_use_index = True
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'wstooldoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'wstool.tex', u'wstool Documentation',
u'Tully Foote, Thibault Kruse, Ken Conley, Brian Gerkey', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Additional stuff for the LaTeX preamble.
# latex_preamble = ''
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'wstool', u'wstool Documentation',
[u'Tully Foote, Thibault Kruse, Ken Conley, Brian Gerkey'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'wstool', u'wstool Documentation',
u'Tully Foote, Thibault Kruse, Ken Conley, Brian Gerkey', 'wstool', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}
wstool-0.1.18/doc/developers_guide.rst 0000664 0000000 0000000 00000003265 13542237257 0017774 0 ustar 00root root 0000000 0000000 Developer's Guide
=================
Changelog
---------
.. toctree::
:maxdepth: 1
changelog
Bug reports and feature requests
--------------------------------
- `Submit a bug report `_
Developer Setup
---------------
The wstool source can be downloaded using Mercurial::
$ git clone https://github.com/vcstools/wstool.git
You will also need vcstools, which you can either install using pip or download using::
$ git clone https://github.com/vcstools/vcstools.git
$ cd vcstools
$ python develop
wstool uses `setuptools `_,
which you will need to download and install in order to run the
packaging. We use setuptools instead of distutils in order to be able
use ``setup()`` keys like ``install_requires``.
Configure your environment:
$ cd wstool
$ python develop
Testing
-------
Install test dependencies
::
$ pip install nose
$ pip install mock
wstool uses `Python nose
`_ for testing, which is
a fairly simple and straightforward test framework. The wstool
mainly use :mod:`unittest` to construct test fixtures, but with nose
you can also just write a function that starts with the name ``test``
and use normal ``assert`` statements.
wstool also uses `mock `_
to create mocks for testing.
You can run the tests, including coverage, as follows:
::
$ cd wstool
$ make test
Documentation
-------------
Sphinx is used to provide API documentation for wstool. The documents
are stored in the ``doc`` sub-directory.
You can build the docs as follows:
::
$ cd wstool/doc
$ make html
wstool-0.1.18/doc/index.rst 0000664 0000000 0000000 00000002552 13542237257 0015554 0 ustar 00root root 0000000 0000000 wstool
======
.. module:: wstool
.. moduleauthor:: Tully Foote , Thibault Kruse , Ken Conley
Using wstool you can update several folders using a variety
of SCMs (SVN, Mercurial, git, Bazaar) with just one command.
That way you can more effectively manage source code workspaces.
The wstool package provides a Python API for interacting with a
source code workspace as well as a group of command line tools.
Rosinstall leverages the :mod:`vcstools` package for source control and
stores its state in .rosinstall files.
Command Line Tools:
===================
.. toctree::
:maxdepth: 2
wstool_usage
Installation
============
Ubuntu
------
On Ubuntu the recommended way to install rosinstall is to use apt.
::
sudo apt-get install python-wstool
Other Platforms
---------------
On other platforms rosinstall is available on pypi and can be installed via ``pip``
::
pip install -U wstool
or ``easy_install``:
::
easy_install -U wstool vcstools
Rosinstall File Format:
=======================
.. toctree::
:maxdepth: 2
rosinstall_file_format
Advanced: rosinstall developers/contributors
============================================
.. toctree::
:maxdepth: 2
developers_guide
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
wstool-0.1.18/doc/make.bat 0000664 0000000 0000000 00000015066 13542237257 0015324 0 ustar 00root root 0000000 0000000 @ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
set I18NSPHINXOPTS=%SPHINXOPTS% source
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^` where ^ is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. xml to make Docutils-native XML files
echo. pseudoxml to make pseudoxml-XML files for display purposes
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
%SPHINXBUILD% 2> nul
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\wstool.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\wstool.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf
cd %BUILDDIR%/..
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdfja" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf-ja
cd %BUILDDIR%/..
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
:end
wstool-0.1.18/doc/rosinstall_file_format.rst 0000664 0000000 0000000 00000002506 13542237257 0021205 0 ustar 00root root 0000000 0000000 rosinstall file format
======================
Format
------
The rosinstall file format is a yaml document. It is a list of
top level dictionaries. Each top level dictionary is expected to have one of the vcs type keys and no other keys.
Inside every top level dictionary there is one required key, ``local-name`` this represents the path where to install files. It will support both workspace relative paths as well as absolute paths.
Each of the vcs type keys requires a ``uri`` key, and optionally takes a ``version`` key.
Top Level Keys
--------------
The valid keys are ``svn``, ``hg``, ``git``, ``bzr``.
Each key represents a form of version control system to use. These are supported from the vcstools module.
Example rosinstall syntax:
--------------------------
Below is an example rosinstall syntax with examples of most of the
possible permutations:
::
- svn: {local-name: some/local/path2, uri: /some/local/uri}
- hg: {local-name: some/local/path3, uri: http://some/uri, version: 123}
- git: {local-name: /some/local/aboslute/path, uri: http://some/uri, version: 123}
- bzr: {local-name: some/local/path4, uri: http://some/uri, version: 123}
Things to note are:
- ``version`` is optional though recommended.
- Absolute or relative paths are valid for ``local-name``
- ``uri`` can be a local file path to a repository.
wstool-0.1.18/doc/wstool_usage.rst 0000664 0000000 0000000 00000017663 13542237257 0017171 0 ustar 00root root 0000000 0000000 wstool: A tool for managing source code workspaces
==================================================
wstool allows manipulation of a set of version-controlled folders as
specified in a workspace definition file.
.. contents:: Contents
:depth: 3
Usage
-----
::
wstool is a command to manipulate multiple version controlled folders.
Official usage:
wstool CMD [ARGS] [OPTIONS]
wstool will try to infer install path from context
Type 'wstool help' for usage.
Options:
help provide help for commands
init set up a directory as workspace
set add or changes one entry from your workspace config
merge merges your workspace with another config set
remove (rm) remove an entry from your workspace config, without deleting files
update (up) update or check out some of your config elements
info Overview of some entries
status (st) print the change status of files in some SCM controlled entries
diff (di) print a diff over some SCM controlled entries
init
~~~~
set up a directory as workspace
wstool init does the following:
1. Reads folder/file/web-uri SOURCE_PATH looking for a rosinstall yaml
2. Creates new .rosinstall file at TARGET-PATH configured
SOURCE_PATH can e.g. be a folder like /opt/ros/electric
If PATH is not given, uses current folder.
::
Usage: wstool init [TARGET_PATH [SOURCE_PATH]]?
Options::
-h, --help show this help message and exit
--continue-on-error Continue despite checkout errors
-j JOBS, --parallel=JOBS
How many parallel threads to use for installing
Examples::
$ wstool init ~/jade /opt/ros/jade
set
~~~
add or changes one entry from your workspace config
The command will infer whether you want to add or modify an entry. If
you modify, it will only change the details you provide, keeping
those you did not provide. if you only provide a uri, will use the
basename of it as localname unless such an element already exists.
The command only changes the configuration, to checkout or update
the element, run wstool update afterwards.
::
Usage: wstool set [localname] [SCM-URI]? [--(detached|svn|hg|git|bzr)] [--version=VERSION]]
Options:
-h, --help show this help message and exit
--detached make an entry unmanaged (default for new element)
-v VERSION, --version-new=VERSION
point SCM to this version
--git make an entry a git entry
--svn make an entry a subversion entry
--hg make an entry a mercurial entry
--bzr make an entry a bazaar entry
-y, --confirm Do not ask for confirmation
-u, --update update repository after set
-t WORKSPACE, --target-workspace=WORKSPACE
which workspace to use
Examples::
$ wstool set robot_model --hg https://kforge.ros.org/robotmodel/robot_model
$ wstool set robot_model --version robot_model-1.7.1
$ wstool set robot_model --detached
merge
~~~~~
The command merges config with given other rosinstall element sets, from files
or web uris.
The default workspace will be inferred from context, you can specify one using
-t.
By default, when an element in an additional URI has the same
local-name as an existing element, the existing element will be
replaced. In order to ensure the ordering of elements is as
provided in the URI, use the option ``--merge-kill-append``.
::
Usage: wstool merge [URI] [OPTIONS]
Options:
-h, --help show this help message and exit
-a, --merge-kill-append
merge by deleting given entry and appending new one
-k, --merge-keep (default) merge by keeping existing entry and
discarding new one
-r, --merge-replace merge by replacing given entry with new one
maintaining ordering
-y, --confirm-all do not ask for confirmation unless strictly necessary
-t WORKSPACE, --target-workspace=WORKSPACE
which workspace to use
Examples::
$ wstool merge someother.rosinstall
You can use '-' to pipe in input, as an example::
$ roslocate info robot_mode | wstool merge -
update
~~~~~~
update or check out some of your config elements
This command calls the SCM provider to pull changes from remote to
your local filesystem. In case the url has changed, the command will
ask whether to delete or backup the folder.
::
Usage: wstool update [localname]*
Options:
-h, --help show this help message and exit
--delete-changed-uris
Delete the local copy of a directory before changing
uri.
--abort-changed-uris Abort if changed uri detected
--continue-on-error Continue despite checkout errors
--backup-changed-uris=BACKUP_CHANGED
backup the local copy of a directory before changing
uri to this directory.
-j JOBS, --parallel=JOBS
How many parallel threads to use for installing
-v, --verbose Whether to print out more information
-t WORKSPACE, --target-workspace=WORKSPACE
which workspace to use
Examples::
$ wstool update -t ~/jade
$ wstool update robot_model geometry
info
~~~~
Overview of some entries
The Status (S) column shows
x for missing
L for uncommited (local) changes
V for difference in version and/or remote URI
C for difference in local and remote versions
The 'Version-Spec' column shows what tag, branch or revision was given
in the .rosinstall file. The 'UID' column shows the unique ID of the
current (and specified) version. The 'URI' column shows the configured
URL of the repo.
If status is V, the difference between what was specified and what is
real is shown in the respective column. For SVN entries, the url is
split up according to standard layout (trunk/tags/branches). The
ROS_PACKAGE_PATH follows the order of the table, earlier entries
overlay later entries.
When given one localname, just show the data of one element in list
form.
This also has the generic properties element which is usually empty.
The ``--only`` option accepts keywords: ['path', 'localname', 'version',
'revision', 'cur_revision', 'uri', 'cur_uri', 'scmtype']
::
Usage: wstool info [localname]* [OPTIONS]
Options:
-h, --help show this help message and exit
--root Show workspace root path
--data-only Does not provide explanations
--only=ONLY Shows comma-separated lists of only given comma-
separated attribute(s).
--yaml Shows only version of single entry. Intended for
scripting.
--fetch When used, retrieves version information from remote
(takes longer).
-u, --untracked Also show untracked files as modifications
-t WORKSPACE, --target-workspace=WORKSPACE
which workspace to use
-m, --managed-only only show managed elements
Examples::
$ wstool info -t ~/ros/jade
$ wstool info robot_model
$ wstool info --yaml
$ wstool info --only=path,cur_uri,cur_revision robot_model geometry
status
~~~~~~
print the change status of files in some SCM controlled entries. The status
columns meanings are as the respective SCM defines them.
::
Usage: wstool status [localname]*
Options:
-h, --help show this help message and exit
-u, --untracked Also shows untracked files
-t WORKSPACE, --target-workspace=WORKSPACE
which workspace to use
diff
~~~~
print a diff over some SCM controlled entries
::
Usage: wstool diff [localname]*
Options:
-h, --help show this help message and exit
-t WORKSPACE, --target-workspace=WORKSPACE
which workspace to use
wstool-0.1.18/requirements-test.txt 0000664 0000000 0000000 00000000134 13542237257 0017401 0 ustar 00root root 0000000 0000000 nose
mock
pep8
# run checks in multiple environments
tox
tox-pyenv
coverage~=4.5
coveralls
wstool-0.1.18/requirements.txt 0000664 0000000 0000000 00000000030 13542237257 0016417 0 ustar 00root root 0000000 0000000 vcstools>=0.1.42
pyyaml
wstool-0.1.18/scripts/ 0000775 0000000 0000000 00000000000 13542237257 0014631 5 ustar 00root root 0000000 0000000 wstool-0.1.18/scripts/wstool 0000775 0000000 0000000 00000004740 13542237257 0016113 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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: rosws 14389 2011-07-20 18:38:40Z tfoote $
# $Author: tfoote $
"""%(prog)s is a command to manipulate ROS workspaces.
Official usage:
%(prog)s CMD [ARGS] [OPTIONS]
%(prog)s will try to infer install path from context
"%(prog)s init" replaces the rosinstall command
Type '%(prog)s --help' for usage.
"""
from __future__ import print_function
import sys
try:
import wstool.wstool_cli
from wstool.common import MultiProjectException
except ImportError as exc:
sys.exit("ERROR: Cannot find required rosinstall library version, \
check your installation (also of vcstools) is up-to-date. One frequent cause \
is that rosinstall 0.5 is still installed in /usr/local/lib.\n%s" % exc)
if __name__ == "__main__":
try:
sys.exit(wstool.wstool_cli.wstool_main(sys.argv))
except MultiProjectException as mpe:
sys.exit("ERROR in config: %s" % str(mpe))
wstool-0.1.18/setup.cfg 0000664 0000000 0000000 00000000206 13542237257 0014761 0 ustar 00root root 0000000 0000000 [bdist_wheel]
universal = 1
[aliases]
test = nosetests --with-coverage --cover-package=wstool --where=test --cover-min-percentage=80
wstool-0.1.18/setup.py 0000664 0000000 0000000 00000011261 13542237257 0014655 0 ustar 00root root 0000000 0000000 from setuptools import setup
from distutils.command.build import build
from distutils.command.build_py import build_py
import os
import sys
import imp
import argparse
with open('README.rst') as readme_file:
README = readme_file.read()
def get_version():
ver_file = None
try:
ver_file, pathname, description = imp.find_module('__version__', ['src/wstool'])
vermod = imp.load_module('__version__', ver_file, pathname, description)
version = vermod.version
return version
finally:
if ver_file is not None:
ver_file.close()
def _resolve_prefix(prefix, type):
# install to outside of system if OS X to avoid issues caused by
# System Integrity Protection on El Caption
# issue: https://github.com/vcstools/wstool/issues/81
osx_system_prefix = '/System/Library/Frameworks/Python.framework/Versions'
if type == 'man':
if prefix == '/usr':
return '/usr/share'
if sys.prefix.startswith(osx_system_prefix):
return '/usr/local/share'
elif type == 'bash_comp':
if prefix == '/usr':
return '/'
if sys.prefix.startswith(osx_system_prefix):
return '/usr/local'
elif type == 'zsh_comp':
if sys.prefix.startswith(osx_system_prefix):
return '/usr/local'
else:
raise ValueError('not supported type')
return prefix
def get_data_files(prefix):
data_files = []
bash_comp_dest = os.path.join(_resolve_prefix(prefix, 'bash_comp'),
'etc/bash_completion.d')
data_files.append((bash_comp_dest, ['completion/wstool-completion.bash']))
zsh_comp_dest = os.path.join(_resolve_prefix(prefix, 'zsh_comp'),
'share/zsh/site-functions')
data_files.append((zsh_comp_dest, ['completion/_wstool',
'completion/wstool-completion.bash']))
return data_files
parser = argparse.ArgumentParser()
parser.add_argument('--prefix', default=None,
help='prefix to install data files')
opts, _ = parser.parse_known_args(sys.argv)
prefix = opts.prefix or sys.prefix
data_files = get_data_files(prefix)
# At present setuptools has no methods to resolve dependencies at build time,
# so we need to check if sphinx is installed.
# See: https://github.com/pypa/pip/issues/2381
try:
from sphinx.setup_command import BuildDoc
HAVE_SPHINX = True
except:
HAVE_SPHINX = False
if HAVE_SPHINX:
class WstoolBuildMan(BuildDoc):
def initialize_options(self):
BuildDoc.initialize_options(self)
self.builder = 'man'
class WstoolBuild(build):
"""Run additional commands before build command"""
def run(self):
self.run_command('build_man')
build.run(self)
class WstoolBuildPy(build_py):
"""Run additional commands before build_py command"""
def run(self):
self.run_command('build_man')
build_py.run(self)
cmdclass = dict(
build=WstoolBuild,
build_py=WstoolBuildPy,
build_man=WstoolBuildMan,
)
man_dest = os.path.join(_resolve_prefix(prefix, 'man'), 'man/man1')
data_files.append((man_dest, ['build/sphinx/man/wstool.1']))
else:
cmdclass = {}
with open(os.path.join(os.path.dirname(__file__), 'requirements.txt')) as requirements:
install_requires = requirements.read().splitlines()
with open(os.path.join(os.path.dirname(__file__), 'requirements-test.txt')) as requirements:
test_required = requirements.read().splitlines()
setup(name='wstool',
version=get_version(),
packages=['wstool'],
package_dir={'': 'src'},
data_files=data_files,
cmdclass=cmdclass,
# rosinstall dependency to be kept in order not to break ros hydro install instructions
install_requires=install_requires,
# extras_require allow pip install .[test]
extras_require={
'test': test_required
},
# tests_require automatically installed when running python setup.py test
tests_require=test_required,
scripts=["scripts/wstool"],
author="Tully Foote",
author_email="tfoote@osrfoundation.org",
url="http://wiki.ros.org/wstool",
keywords=["ROS"],
classifiers=["Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Development Status :: 7 - Inactive",
"License :: OSI Approved :: BSD License",
"Topic :: Software Development :: Version Control"
],
description="workspace multi-SCM commands",
long_description=README,
license="BSD")
wstool-0.1.18/src/ 0000775 0000000 0000000 00000000000 13542237257 0013731 5 ustar 00root root 0000000 0000000 wstool-0.1.18/src/wstool/ 0000775 0000000 0000000 00000000000 13542237257 0015260 5 ustar 00root root 0000000 0000000 wstool-0.1.18/src/wstool/__init__.py 0000664 0000000 0000000 00000000000 13542237257 0017357 0 ustar 00root root 0000000 0000000 wstool-0.1.18/src/wstool/__version__.py 0000664 0000000 0000000 00000000023 13542237257 0020106 0 ustar 00root root 0000000 0000000 version = '0.1.18'
wstool-0.1.18/src/wstool/cli_common.py 0000664 0000000 0000000 00000042412 13542237257 0017754 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
"Support for any command line interface (CLI) for wstool"
try:
from collections import OrderedDict
except:
from ordereddict import OrderedDict
import os
import re
from optparse import OptionParser
from wstool.common import samefile, MultiProjectException, select_elements
ONLY_OPTION_VALID_ATTRS = ['path', 'localname', 'version',
'revision', 'cur_revision', 'uri', 'cur_uri', 'scmtype']
def get_workspace(argv, shell_path, config_filename=None, varname=None):
"""
If target option -t is given return value of that one. Else, if varname
is given and exists, considers that one, plus,
if config_filename is given, searches for a file named in config_filename
in 'shell_path' and ancestors.
In that case, if two solutions are found, asks the user.
:param shell_path: where to look for relevant config_filename
:param config_filename: optional, filename for files defining workspaces
:param varname: optional, env var to be used as workspace folder
:returns: abspath if a .rosinstall was found, error and exist else.
"""
parser = OptionParser()
parser.add_option(
"-t", "--target-workspace",
dest="workspace", default=None,
help="which workspace to use",
action="store")
# suppress errors based on any other options this parser is agnostic about
argv2 = [x for x in argv if ((not x.startswith('-')) or
x.startswith('--target-workspace=') or
x.startswith('-t') or
x == '--target-workspace')]
(options, _) = parser.parse_args(argv2)
if options.workspace is not None:
if (config_filename is not None and
not os.path.isfile(os.path.join(options.workspace, config_filename))):
raise MultiProjectException("%s has no workspace configuration file '%s'" % (os.path.abspath(options.workspace), config_filename))
return os.path.abspath(options.workspace)
varname_path = None
if varname is not None and varname in os.environ:
# workspace could be relative, maybe confusing,
# but that's the users fault
varname_path = os.environ[varname]
if varname_path.strip() == '' or not os.path.isdir(varname_path):
varname_path = None
# use current dir
current_path = None
if config_filename is not None:
while shell_path is not None and not shell_path == os.path.dirname(shell_path):
if os.path.exists(os.path.join(shell_path, config_filename)):
current_path = shell_path
break
shell_path = os.path.dirname(shell_path)
if current_path is not None and varname_path is not None and not samefile(current_path, varname_path):
raise MultiProjectException("Ambiguous workspace: %s=%s, %s" % (varname, varname_path, os.path.abspath(config_filename)))
if current_path is None and varname_path is None:
raise MultiProjectException("Command requires a target workspace.")
if current_path is not None:
return current_path
else:
return varname_path
def _uris_match(basepath, uri1, uri2):
"""
True if uri2 is None or not None and same folder or equal string
as uri1. Relative folders resolved using basepath
"""
if uri1 is None:
uri1 = ''
if uri2 is None:
return True
if ((uri1 == uri2) or
(basepath is not None and
os.path.isdir(os.path.join(basepath, uri1)) and
os.path.realpath(os.path.join(basepath, uri2)) == os.path.realpath(os.path.join(basepath, uri1)))):
return True
return False
def _get_svn_version_from_uri(uri):
"""
in case of SVN, we can use the final part of
standard uri as spec version, if it follows canonical SVN layout
:param uri: uri to extract version from
:returns changed_uri: str, version extracted uri
:returns version: str, extracted version
:returns: (None, None), for empty uri or when there is no regex match for version info
"""
if uri is None:
return None, None
match = re.match('(.*/)((tags|branches|trunk)(/.*)*)', uri)
if (match is not None and
len(match.groups()) > 1 and
uri == ''.join(match.groups()[0:2])):
changed_uri = match.groups()[0]
version = match.groups()[1]
return changed_uri, version
return None, None
def _get_status_flags(basepath, elt_dict):
"""
returns a string where each char conveys status information about
a config element entry
:param basepath: path in which element lies
:param elt_dict: a dict representing one elt_dict in a table
:returns: str
"""
if 'exists' in elt_dict and elt_dict['exists'] is False:
return 'x'
mflag = ''
if 'modified' in elt_dict and elt_dict['modified'] is True:
mflag = 'M'
if (('curr_uri' in elt_dict and
not _uris_match(basepath, elt_dict['uri'], elt_dict['curr_uri'])) or
('specversion' in elt_dict and
elt_dict['specversion'] is not None and
elt_dict['actualversion'] is not None and
elt_dict['specversion'] != elt_dict['actualversion'])):
mflag += 'V'
if (('remote_revision' in elt_dict and
elt_dict['remote_revision'] != '' and
elt_dict['remote_revision'] is not None and
'actualversion' in elt_dict and
elt_dict['actualversion'] is not None and
elt_dict['remote_revision'] != elt_dict['actualversion']) or
(('version' not in elt_dict or
elt_dict['version'] is None) and
'default_remote_label' in elt_dict and
elt_dict['default_remote_label'] is not None and
('curr_version' not in elt_dict or
elt_dict['curr_version'] != elt_dict['default_remote_label']))):
mflag += 'C'
return mflag
def get_info_table_elements(basepath, entries, unmanaged=False):
"""returns a list of dict with refined information from entries"""
outputs = []
for line in entries:
if not 'curr_uri' in line:
line['curr_uri'] = None
if not 'specversion' in line:
line['specversion'] = None
if not 'actualversion' in line:
line['actualversion'] = None
if not 'curr_version' in line:
line['curr_version'] = None
if not 'version' in line:
line['version'] = None
if not 'remote_revision' in line:
line['remote_revision'] = None
if not 'curr_version_label' in line:
line['curr_version_label'] = None
output_dict = {'scm': line['scm'],
'uri': line['uri'],
'curr_uri': None,
'version': line['version'],
'localname': line['localname']}
if line is None:
print("Bug Warning, an element is missing")
continue
if line['scm'] == 'git':
if (line['specversion'] is not None and len(line['specversion']) > 12):
line['specversion'] = line['specversion'][0:12]
if (line['actualversion'] is not None and len(line['actualversion']) > 12):
line['actualversion'] = line['actualversion'][0:12]
if (line['remote_revision'] is not None and len(line['remote_revision']) > 12):
line['remote_revision'] = line['remote_revision'][0:12]
if line['scm'] is not None:
if line['scm'] == 'svn':
(line['uri'],
line['version']) = _get_svn_version_from_uri(uri=line['uri'])
if line['curr_uri'] is not None:
(line['curr_uri'],
line['curr_version_label']) = _get_svn_version_from_uri(
uri=line['curr_uri'])
if line['scm'] in ['git', 'svn', 'hg']:
line['curr_version'] = line['curr_version_label']
if line['curr_version'] is not None:
output_dict['version'] = line['curr_version']
if output_dict['version'] is not None:
if line['version'] != output_dict['version']:
if line['version']:
output_dict['version'] += " (%s)" % line['version']
else:
if line['default_remote_label']:
if output_dict['version'] == line['default_remote_label']:
output_dict['version'] += " (=)"
else:
output_dict['version'] += " (%s)" % line['default_remote_label']
else:
output_dict['version'] += " (-)"
if (line['specversion'] is not None and
line['specversion'] != '' and
line['actualversion'] != line['specversion']):
output_dict['matching'] = "%s (%s)" % (line['actualversion'], line['specversion'])
else:
output_dict['matching'] = line['actualversion']
common_prefixes = ["https://", "http://"]
if line['uri'] is not None and unmanaged is False:
for pre in common_prefixes:
if line['uri'].startswith(pre):
line['uri'] = line['uri'][len(pre):]
break
output_dict['uri'] = line['uri']
if line['curr_uri'] is not None:
for pre in common_prefixes:
if line['curr_uri'].startswith(pre):
line['curr_uri'] = line['curr_uri'][len(pre):]
break
if (not _uris_match(basepath, line['uri'], line['curr_uri'])):
output_dict['uri'] = "%s (%s)" % (line[
'curr_uri'], line['uri'])
else:
output_dict['matching'] = " "
output_dict['status'] = _get_status_flags(basepath, line)
outputs.append(output_dict)
return outputs
def get_info_table(basepath, entries, data_only=False, reverse=False,
unmanaged=False, selected_headers=None):
"""
return a refined textual representation of the entries. Provides
column headers and processes data.
"""
headers = OrderedDict([
('localname', "Localname"),
('status', "S"),
('scm', "SCM"),
('version', "Version (Spec)"),
('matching', "UID (Spec)"),
('uri', "URI (Spec) [http(s)://...]"),
])
# table design
if unmanaged:
selected_headers = ['localname', 'scm', 'uri']
elif selected_headers is None:
selected_headers = headers.keys()
# validate selected_headers
invalid_headers = [h for h in selected_headers if h not in headers.keys()]
if invalid_headers:
raise ValueError('Invalid headers are passed: %s' % invalid_headers)
outputs = get_info_table_elements(
basepath=basepath,
entries=entries,
unmanaged=unmanaged)
# adjust column width
column_length = {}
for header in list(headers.keys()):
column_length[header] = len(headers[header])
for entry in outputs:
if entry[header] is not None:
column_length[header] = max(column_length[header],
len(entry[header]))
resultlines = []
if not data_only and len(outputs) > 0:
header_line = ' '
for i, header in enumerate(selected_headers):
output = headers[header]
if i < len(selected_headers) - 1:
output = output.ljust(column_length[header]) + " "
header_line += output
resultlines.append(header_line)
header_line = ' '
for i, header in enumerate(selected_headers):
output = '-' * len(headers[header])
if i < len(selected_headers) - 1:
output = output.ljust(column_length[header]) + " "
header_line += output
resultlines.append(header_line)
if reverse:
outputs = reversed(outputs)
for entry in outputs:
if entry is None:
print("Bug Warning, an element is missing")
continue
data_line = ' '
for i, header in enumerate(selected_headers):
output = entry[header]
if output is None:
output = ''
if i < len(selected_headers) - 1:
output = output.ljust(column_length[header]) + " "
data_line += output
resultlines.append(data_line)
return "\n".join(resultlines)
def get_info_list(basepath, line, data_only=False):
"""
Info for a single config entry
"""
assert line is not None, "Bug Warning, an element is missing"
headers = {
'uri': "URI:",
'curr_uri': "Current URI:",
'scm': "SCM:",
'localname': "Localname:",
'path': "Path",
'version': "Spec-Version:",
'curr_version_label': "Current-Version:",
'status': "Status:",
'specversion': "Spec-Revision:",
'actualversion': "Current-Revision:",
'properties': "Other Properties:"}
# table design
selected_headers = ['localname', 'path', 'status',
'scm', 'uri', 'curr_uri',
'version', 'curr_version_label', 'specversion',
'actualversion', 'properties']
line['status'] = _get_status_flags(basepath, line)
header_length = 0
for header in list(headers.keys()):
header_length = max(header_length, len(headers[header]))
result = ''
for header in selected_headers:
if not data_only:
title = "%s " % (headers[header].ljust(header_length))
else:
title = ''
if header in line:
output = line[header]
if output is None:
output = ''
result += "%s%s\n" % (title, output)
return result
def get_info_table_raw_csv(config, parser, properties, localnames):
"""
returns raw data without decorations in comma-separated value format.
allows to select properties.
Given a config, collects all elements, and prints a line of each,
with selected properties in the output
:param parser: OptionParser used to throw option errors
:param properties: list of property ids to display
:param localnames: which config elements to show
:return: list of str, each a csv line
"""
lookup_required = False
for attr in properties:
if not attr in ONLY_OPTION_VALID_ATTRS:
parser.error("Invalid --only option '%s', valids are %s" %
(attr, ONLY_OPTION_VALID_ATTRS))
if attr in ['cur_revision', 'cur_uri', 'revision']:
lookup_required = True
elements = select_elements(config, localnames)
result=[]
for element in elements:
if lookup_required and element.is_vcs_element():
spec = element.get_versioned_path_spec()
else:
spec = element.get_path_spec()
output = []
for attr in properties:
if 'localname' == attr:
output.append(spec.get_local_name() or '')
if 'path' == attr:
output.append(spec.get_path() or '')
if 'scmtype' == attr:
output.append(spec.get_scmtype() or '')
if 'uri' == attr:
output.append(spec.get_uri() or '')
if 'version' == attr:
output.append(spec.get_version() or '')
if 'revision' == attr:
output.append(spec.get_revision() or '')
if 'cur_uri' == attr:
output.append(spec.get_curr_uri() or '')
if 'cur_revision' == attr:
output.append(spec.get_current_revision() or '')
result.append(','.join(output))
return result
wstool-0.1.18/src/wstool/common.py 0000664 0000000 0000000 00000034336 13542237257 0017133 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import sys
import traceback
import os
import copy
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
# choosing multiprocessing over threading for clean Control-C
# interrupts (provides terminate())
from multiprocessing import Process, Manager
from vcstools.vcs_base import VcsError
class MultiProjectException(Exception):
pass
def samefile(file1, file2):
"""
Test whether two pathnames reference the same actual file
This is a workaround for the fact that some platforms
do not have os.path.samefile (particularly windows). This
is the patch that was integrated in python 3.0 (at which
time we can probably remove this workaround).
"""
try:
return os.path.samefile(file1, file2)
except AttributeError:
try:
from nt import _getfinalpathname
return _getfinalpathname(file1) == _getfinalpathname(file2)
except (NotImplementedError, ImportError):
# On Windows XP and earlier, two files are the same if their
# absolute pathnames are the same.
# Also, on other operating systems, fake this method with a
# Windows-XP approximation.
return os.path.abspath(file1) == os.path.abspath(file2)
def conditional_abspath(uri):
"""
@param uri: The uri to check
@return: abspath(uri) if local path otherwise pass through uri
"""
uri2 = urlparse(uri)
# maybe it's a local file?
if uri2.scheme == '':
return os.path.abspath(uri)
else:
return uri
def is_web_uri(source_uri):
"""
Uses heuristics to check whether uri is a web uri (as opposed to a file path)
:param source_uri: string representing web uri or file path
:returns: bool
"""
if source_uri is None or source_uri == '':
return False
parsed_uri = urlparse(source_uri)
if (parsed_uri.scheme == '' and
parsed_uri.netloc == '' and
not '@' in parsed_uri.path.split('/')[0]):
return False
return True
def normalize_uri(source_uri, base_path):
"""
If source_uri is none or a web uri, return it.
If source_uri is a relative path, make it an absolute path.
Else return it normalized
:param source_uri: some uri to a file, folder, or web resource
:param base_path: path to use to make relative paths absolute
:returns: normalized string
"""
if source_uri is not None and not is_web_uri(source_uri):
if os.path.isabs(source_uri):
source_uri = os.path.normpath(source_uri)
else:
source_uri2 = os.path.normpath(os.path.join(base_path, source_uri))
# sys.stderr.write("Warning: Converted relative uri path %s to abspath %s\n" %
# (source_uri, source_uri2))
source_uri = source_uri2
return source_uri
def string_diff(str1_orig, str2_orig, maxlen=11, backtrack=7):
"""
Compares strings, returns a part of str2 depending on how many
chars into the string the first difference can be found. If the
difference is after maxlen, a prefix of str is removed so that
only the 'backtrack'-last letters of the common prefix remain in str2.
This only makes sense if str1 != str2, really.
The purpose is to print str1 -> str2 without repeating a same long prefix
:returns: a representation of where str2 differs from str1.
"""
result = str2_orig or ''
if str1_orig is not None and str2_orig is not None:
# we cannot be sure we have strings, might be lists,
# gracefully fail convert to string
str1 = str(str1_orig)
str2 = str(str2_orig)
result = str2
if len(str2) > len(str1):
str1 = str1.ljust(len(str2))
charcompare = [x[0] == x[1] for x in zip(str(str2), str(str1))]
if False in charcompare:
commonprefix = str2[:charcompare.index(False)]
if len(commonprefix) > maxlen:
result = "...%s" % str2[len(commonprefix) - backtrack:]
return result
def normabspath(localname, path):
"""
if localname is absolute, return it normalized. If relative,
return normalized join of path and localname
"""
# do not use realpath here as we want to keep symlinked path as such
if os.path.isabs(localname) or path is None:
return os.path.normpath(localname)
abs_path = os.path.normpath(os.path.join(path, localname))
return abs_path
def _is_parent_path(parent, child):
"""Return true if child is subdirectory of parent.
Assumes both paths are absolute and don't contain symlinks.
"""
parent = os.path.normpath(parent)
child = os.path.normpath(child)
prefix = os.path.commonprefix([parent, child])
if prefix == parent:
# Note: os.path.commonprefix operates on character basis, so
# take extra care of situations like '/foo/ba' and '/foo/bar/baz'
child_suffix = child[len(prefix):]
child_suffix = child_suffix.lstrip(os.sep)
if child == os.path.join(prefix, child_suffix):
return True
return False
def realpath_relation(abspath1, abspath2):
"""
Computes the relationship abspath1 to abspath2
:returns: None, 'SAME_AS', 'PARENT_OF', 'CHILD_OF'
"""
assert os.path.isabs(abspath1), "Bug, %s is not absolute path" % abspath1
assert os.path.isabs(abspath2), "Bug, %s is not absolute path" % abspath2
realpath1 = os.path.realpath(abspath1)
realpath2 = os.path.realpath(abspath2)
if os.path.dirname(realpath1) == os.path.dirname(realpath2):
if os.path.basename(realpath1) == os.path.basename(realpath2):
return 'SAME_AS'
return None
else:
if _is_parent_path(realpath1, realpath2):
return 'PARENT_OF'
if _is_parent_path(realpath2, realpath1):
return 'CHILD_OF'
return None
def select_element(elements, localname):
"""
selects entry among elements where path or localname matches.
Prefers localname matches in case of ambiguity.
"""
path_candidate = None
if localname is not None:
realpath = os.path.realpath(localname)
for element in elements:
if localname == element.get_local_name():
path_candidate = element
break
elif realpath == os.path.realpath(element.get_path()):
path_candidate = element
return path_candidate
def select_elements(config, localnames):
"""
selects config elements with given localnames, returns in the
order given in config If localnames has one element which is path
of the config, return all elements
"""
if config is None:
return []
if localnames is None:
return config.get_config_elements()
elements = config.get_config_elements()
selected = []
notfound = []
for localname in localnames:
element = select_element(elements, localname)
if element is not None:
selected.append(element)
else:
notfound.append(localname)
if notfound != []:
# if we just passed workspace path, return all workspace entries
if (len(localnames) == 1 and
os.path.realpath(localnames[0]) == os.path.realpath(config.get_base_path())):
return config.get_config_elements()
raise MultiProjectException("Unknown elements '%s'" % notfound)
result = []
# select in order and remove duplicates
for element in config.get_config_elements():
if element in selected:
result.append(element)
return result
## Multithreading The following classes help with distributing work
## over several instances, providing wrapping for starting, joining,
## collecting results, and catching Exceptions. Also they provide
## support for running groups of threads sequentially, for the case
## that some library is not thread-safe.
class WorkerThread(Process):
def __init__(self, worker, outlist, index):
Process.__init__(self)
self.worker = worker
if worker is None or worker.element is None:
raise MultiProjectException("Bug: Invalid Worker")
self.outlist = outlist
self.index = index
def run(self):
result = {}
try:
result = {'entry': self.worker.element.get_path_spec()}
result_dict = self.worker.do_work()
if result_dict is not None:
result.update(result_dict)
else:
result.update(
{'error': MultiProjectException("worker returned None")})
except MultiProjectException as mpe:
result.update({'error': mpe})
except VcsError as vcse:
result.update({'error': vcse})
except OSError as ose:
result.update({'error': ose})
except Exception as exc:
# this would be a bug, and we need trace to find them in
# multithreaded cases.
traceback.print_exc(file=sys.stderr)
result.update({'error': exc})
self.outlist[self.index] = result
class DistributedWork():
def __init__(self, capacity, num_threads=10, silent=True):
# need managed array since we need the results later
man = Manager()
self.outputs = man.list([None for _ in range(capacity)])
self.threads = []
self.sequentializers = {}
self.index = 0
self.num_threads = capacity if num_threads <= 0 else min(num_threads, capacity)
self.silent = silent
def add_thread(self, worker):
thread = WorkerThread(worker, self.outputs, self.index)
if self.index >= len(self.outputs):
raise MultiProjectException(
"Bug: Declared capacity exceeded %s >= %s" % (self.index,
len(self.outputs)))
self.index += 1
self.threads.append(thread)
def run(self):
"""
Execute all collected workers, terminate all on KeyboardInterrupt
"""
if self.threads == []:
return []
if (self.num_threads == 1):
for thread in self.threads:
thread.run()
else:
# The following code is rather delicate and may behave differently
# using threading or multiprocessing. running_threads is
# intentionally not used as a shrinking list because of al the
# possible multithreading / interruption corner cases
# Not using Pool because of KeyboardInterrupt cases
try:
waiting_index = 0
maxthreads = self.num_threads
running_threads = []
missing_threads = copy.copy(self.threads)
# we are done if all threads have finished
while len(missing_threads) > 0:
# we spawn more threads whenever some threads have finished
if len(running_threads) < maxthreads:
to_index = min(
waiting_index + maxthreads - len(running_threads),
len(self.threads))
for i in range(waiting_index, to_index):
self.threads[i].start()
running_threads.append(self.threads[i])
waiting_index = to_index
# threads have exitcode only once they terminated
missing_threads = [t for t in missing_threads if t.exitcode is None]
running_threads = [t for t in running_threads if t.exitcode is None]
if (not self.silent
and len(running_threads) > 0):
print("[%s] still active" % ",".join([th.worker.element.get_local_name() for th in running_threads]))
for thread in running_threads:
# this should prevent busy waiting
thread.join(1)
except KeyboardInterrupt as k:
for thread in self.threads:
if thread is not None and thread.is_alive():
print("[%s] terminated while active" % thread.worker.element.get_local_name())
thread.terminate()
raise k
self.outputs = [x for x in self.outputs if x is not None]
message = ''
for output in self.outputs:
if "error" in output:
if 'entry' in output:
message += "Error processing '%s' : %s\n" % (
output['entry'].get_local_name(), output["error"])
else:
message += "%s\n" % output["error"]
if message != '':
raise MultiProjectException(message)
return self.outputs
wstool-0.1.18/src/wstool/config.py 0000664 0000000 0000000 00000030365 13542237257 0017106 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
from wstool.config_elements import AVCSConfigElement, OtherConfigElement, SetupConfigElement
from wstool.common import MultiProjectException, normabspath, realpath_relation, normalize_uri
class Config:
"""
A config is a set of config elements, each of which defines a folder or file
and possibly a VCS from which to update the folder.
"""
def __init__(self, path_specs, install_path, config_filename=None, extended_types=None, merge_strategy='KillAppend'):
"""
:param config_source_dict: A list (e.g. from yaml) describing the config, list of dict, each dict describing one element.
:param config_filename: When given a folder, Config
:param merge_strategy: how to deal with entries with equivalent path. See insert_element
will look in folder for file of that name for more config source, str.
"""
assert install_path is not None, "Install path is None"
if path_specs is None:
raise MultiProjectException("Passed empty source to create config")
# All API operations must grant that elements in trees have unique local_name and paths
# Also managed (VCS) entries must be disjunct (meaning one cannot be in a child folder of another managed one)
# The idea is that managed entries can safely be concurrently modified
self.trees = []
self.base_path = os.path.abspath(install_path)
self.config_filename = None
if config_filename is not None:
self.config_filename = os.path.basename(config_filename)
# using a registry primarily for unit test design
self.registry = {'svn': AVCSConfigElement,
'git': AVCSConfigElement,
'hg': AVCSConfigElement,
'bzr': AVCSConfigElement,
'tar': AVCSConfigElement}
if extended_types is not None:
self.registry = dict(list(self.registry.items()) + list(extended_types.items()))
for path_spec in path_specs:
action = self.add_path_spec(path_spec, merge_strategy)
# Usual action in init should be 'Append', anything else is unusual
if action == 'KillAppend':
print("Replace existing entry %s by appending." % path_spec.get_local_name())
elif action == 'MergeReplace':
print("Replace existing entry %s" % path_spec.get_local_name())
elif action == 'MergeKeep':
print("Keep existing entry %s, discard later one" % path_spec.get_local_name())
def __str__(self):
return str([str(x) for x in self.trees])
def add_path_spec(self, path_spec, merge_strategy='KillAppend'):
"""
add new element to this config with information provided in path spec
:param merge_strategy: see insert_element
:param path_specs: PathSpec objects
:returns: merge action taken, see insert_element
"""
# compute the local_path for the config element
local_path = normabspath(
path_spec.get_local_name(), self.get_base_path())
if os.path.isfile(local_path):
if path_spec.get_tags() is not None and 'setup-file' in path_spec.get_tags():
elem = SetupConfigElement(local_path,
os.path.normpath(path_spec.get_local_name()),
properties=path_spec.get_tags())
return self.insert_element(elem, merge_strategy)
else:
print("!!!!! Warning: Not adding file %s" % local_path)
return None
else:
# scmtype == None kept for historic reasons here
if (path_spec.get_scmtype() == None and
self.config_filename is not None and
os.path.exists(os.path.join(local_path, self.config_filename))):
print("!!!!! Warning: Not recursing into other config folder %s containing file %s" % (local_path, self.config_filename))
return None
if path_spec.get_scmtype() != None:
return self._insert_vcs_path_spec(path_spec, local_path, merge_strategy)
else:
# keep the given local name (e.g. relative path) for other
# elements, but normalize
local_name = os.path.normpath(path_spec.get_local_name())
elem = OtherConfigElement(local_path,
local_name,
path_spec.get_uri(),
path_spec.get_version(),
properties=path_spec.get_tags())
return self.insert_element(elem, merge_strategy)
def _insert_vcs_path_spec(self, path_spec, local_path, merge_strategy='KillAppend'):
# Get the version and source_uri elements
source_uri = normalize_uri(path_spec.get_uri(), self.get_base_path())
version = path_spec.get_version()
try:
local_name = os.path.normpath(path_spec.get_local_name())
elem = self._create_vcs_config_element(
path_spec.get_scmtype(),
local_path,
local_name,
source_uri,
version,
properties=path_spec.get_tags())
return self.insert_element(elem, merge_strategy)
except LookupError as ex:
raise MultiProjectException(
"Abstracted VCS Config failed. Exception: %s" % ex)
def insert_element(self, new_config_elt, merge_strategy='KillAppend'):
"""
Insert ConfigElement to self.trees, checking for duplicate
local-name or path first. In case local_name matches, follow
given strategy
- KillAppend (default): remove old element, append new at the end
- MergeReplace: remove first hit, insert new at that position.
- MergeKeep: Discard new element
In case local path matches but local name does not, raise Exception
:returns: the action performed None, 'Append', 'KillAppend',
'MergeReplace', 'MergeKeep'
"""
removals = []
replaced = False
for index, loop_elt in enumerate(self.trees):
# if paths are os.path.realpath, no symlink problems.
relationship = realpath_relation(loop_elt.get_path(),
new_config_elt.get_path())
if relationship == 'SAME_AS':
if os.path.normpath(loop_elt.get_local_name()) != os.path.normpath(new_config_elt.get_local_name()):
raise MultiProjectException("Elements with different local_name target the same path: %s, %s" % (loop_elt, new_config_elt))
else:
if (loop_elt == new_config_elt):
return None
if (merge_strategy == 'MergeReplace' or
(merge_strategy == 'KillAppend' and
index == len(self.trees) - 1)):
self.trees[index] = new_config_elt
# keep looping to check for overlap when replacing non-
# scm with scm entry
replaced = True
if (loop_elt.is_vcs_element or not new_config_elt.is_vcs_element):
return 'MergeReplace'
elif merge_strategy == 'KillAppend':
removals.append(loop_elt)
elif merge_strategy == 'MergeKeep':
return 'MergeKeep'
else:
raise LookupError(
"No such merge strategy: %s" % str(merge_strategy))
elif ((relationship == 'CHILD_OF' and new_config_elt.is_vcs_element())
or (relationship == 'PARENT_OF' and loop_elt.is_vcs_element())):
# we do not allow any elements to be children of scm elements
# to allow for parallel updates and because wstool may
# delete scm folders on update, and thus subfolders can be
# deleted with their parents
raise MultiProjectException(
"Managed Element paths overlap: %s, %s" % (loop_elt,
new_config_elt))
if replaced:
return 'MergeReplace'
for loop_elt in removals:
self.trees.remove(loop_elt)
self.trees.append(new_config_elt)
if len(removals) > 0:
return 'KillAppend'
return 'Append'
def remove_element(self, local_name):
"""
Removes element in the tree with the given local name (should be only one)
:returns: True if such an element was found
"""
removals = []
for tree_el in self.trees:
if tree_el.get_local_name() == local_name:
removals.append(tree_el)
if len(removals) > 0:
for tree_el in removals:
self.trees.remove(tree_el)
return True
return False
def _create_vcs_config_element(self, scmtype, path, local_name, uri, version='', properties=None):
try:
eclass = self.registry[scmtype]
except LookupError:
raise MultiProjectException(
"No VCS client registered for vcs type %s" % scmtype)
return eclass(scmtype=scmtype,
path=path,
local_name=local_name,
uri=uri,
version=version,
properties=properties)
def get_base_path(self):
return self.base_path
def get_config_filename(self):
return self.config_filename
def get_source(self, versioned=False, vcs_only=False):
"""
:param versioned: True returns sources as versioned PathSpec, False
returns sources as simple PathSpec
:param vcs_only: True returns only version-controlled sources, False
returns all sources
:returns: all elements that got added by user keystrokes
(CLI and changed .rosinstall)
"""
source_aggregate = []
for tree_el in self.trees:
if vcs_only and not tree_el.is_vcs_element():
continue
if not versioned or not tree_el.is_vcs_element():
source_aggregate.append(tree_el.get_path_spec())
else:
source_aggregate.append(tree_el.get_versioned_path_spec())
return source_aggregate
def get_config_elements(self):
source_aggregate = []
for tree_el in self.trees:
source_aggregate.append(tree_el)
return source_aggregate
wstool-0.1.18/src/wstool/config_elements.py 0000664 0000000 0000000 00000047205 13542237257 0021003 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import sys
import shutil
import datetime
from vcstools.vcs_abstraction import get_vcs_client
from vcstools.vcs_base import VcsError
from wstool.common import samefile, MultiProjectException
from wstool.config_yaml import PathSpec
from wstool.ui import Ui
# helper class
class PreparationReport(object):
"""
Specifies after user interaction of how to perform install / update
"""
def __init__(self, element):
self.config_element = element
self.abort = False # abort ALL operations
self.skip = False # skip this tree
self.error = None # message
self.verbose = False # verbosity
self.checkout = True # checkout vs update
self.backup = False # backup vs delete
self.backup_path = None # where to move tree to
self.inplace = False # whether to follow symlink or just delete
self.timeout = None # maximum time for each checkout/update
## Each Config element provides actions on a local folder
class ConfigElement(object):
"""
Base class for Config provides methods with not implemented
exceptions. Also a few shared methods.
"""
def __init__(self, path, local_name, properties=None):
self.path = path
if path is None:
raise MultiProjectException("Invalid empty path")
self.local_name = local_name
self.properties = properties
def get_path(self):
"""A normalized absolute path"""
return self.path
def get_local_name(self):
"""What the user specified in his config"""
return self.local_name
def prepare_install(self, backup_path=None, arg_mode='abort', robust=False):
"""
Check whether install can be performed, asking user for
decision if necessary.
:param arg_mode: one of prompt, backup, delete, skip.
Determines how to handle error cases
:param backup_path: if arg_mode==backup, determines where to backup to
:param robust: if true, operation will be aborted without
changes to the filesystem and without user interaction
:returns: A preparation_report instance,
telling whether to checkout or to update,
how to deal with existing tree, and where to backup to.
"""
preparation_report = PreparationReport(self)
preparation_report.skip = True
return preparation_report
def install(self, checkout=True, backup=False, backup_path=None,
inplace=False, verbose=False, timeout=None, shallow=False):
"""
Attempt to make it so that self.path is the result of checking
out / updating from remote repo.
No user Interaction allowed here (for concurrent mode).
:param checkout: whether to checkout or update
:param backup: if checking out, what to do if path exists.
If true, backup_path must be set.
"""
raise NotImplementedError("ConfigElement install unimplemented")
def get_path_spec(self):
"""PathSpec object with values as specified in file"""
raise NotImplementedError("ConfigElement get_path_spec unimplemented")
def get_properties(self):
"""Any meta information attached"""
return self.properties
def get_versioned_path_spec(self):
"""PathSpec where VCS elements have the version looked up"""
raise NotImplementedError(
"ConfigElement get_versioned_path_spec unimplemented")
def is_vcs_element(self):
# subclasses to override when appropriate
return False
def get_diff(self, basepath=None):
raise NotImplementedError("ConfigElement get_diff unimplemented")
def get_status(self, basepath=None, untracked=False):
raise NotImplementedError("ConfigElement get_status unimplemented")
def backup(self, backup_path):
if not backup_path:
raise MultiProjectException(
"[%s] Cannot install %s. backup disabled." % (self.get_local_name(),
self.get_path()))
backup_path = os.path.join(
backup_path,
"%s_%s" % (os.path.basename(self.path),
datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")))
print("[%s] Backing up %s to %s" % (self.get_local_name(),
self.get_path(),
backup_path))
shutil.move(self.path, backup_path)
def __str__(self):
return str(self.get_path_spec().get_legacy_yaml())
def __repr__(self):
return str(self.get_path_spec().get_legacy_yaml())
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.get_path_spec() == other.get_path_spec()
else:
return False
class OtherConfigElement(ConfigElement):
def __init__(self, path, local_name, uri=None, version='', properties=None):
super(OtherConfigElement, self).__init__(path, local_name, properties)
self.uri = uri
self.version = version
def install(self, checkout=True, backup=False, backup_path=None,
inplace=False, verbose=False, shallow=False):
return True
def get_versioned_path_spec(self):
raise MultiProjectException(
"Cannot generate versioned outputs with non source types")
def get_path_spec(self):
"yaml as from source"
version = self.version
if version == '':
version = None
return PathSpec(local_name=self.get_local_name(),
path=self.get_path(),
scmtype=None,
uri=self.uri,
version=version,
tags=self.get_properties())
def get_diff(self, basepath=None):
return ''
def get_status(self, basepath=None, untracked=False):
return ''
class SetupConfigElement(ConfigElement):
"""
A setup config element specifies a single file containing
configuration data for a config.
"""
def install(self, checkout=True, backup=False, backup_path=None,
inplace=False, verbose=False, shallow=False):
return True
def get_versioned_path_spec(self):
raise MultiProjectException(
"Cannot generate versioned outputs with non source types")
def get_path_spec(self):
return PathSpec(local_name=self.get_local_name(),
path=self.get_path(),
tags=['setup-file'] + (self.get_properties() or []))
def get_diff(self, basepath=None):
return ''
def get_status(self, basepath=None, untracked=False):
return ''
class VCSConfigElement(ConfigElement):
def __init__(self, path, local_name, uri, version='', properties=None):
"""
Creates a config element for a VCS repository.
:param path: absolute or relative path, str
:param vcs_client: Object compatible with vcstools.VcsClientBase
:param local_name: display name for the element, str
:param uri: VCS uri to checkout/pull from, str
:param version: optional revision spec (tagname, SHAID, ..., str)
"""
super(VCSConfigElement, self).__init__(path, local_name, properties)
if uri is None:
raise MultiProjectException(
"Invalid scm entry having no uri attribute for path %s" % path)
# strip trailing slashes if defined to not be too strict #3061
self.uri = uri.rstrip('/')
self.version = version
def _get_vcsc(self):
raise NotImplementedError("VCSConfigElement _get_vcsc() unimplemented")
def is_vcs_element(self):
return True
def get_vcs_type_name(self):
# also see override in AVCSConfigElement
return self._get_vcsc().get_vcs_type_name()
def detect_presence(self):
# also see override in AVCSConfigElement
return self._get_vcsc().detect_presence()
def path_exists(self):
# we could use _get_vcsc().path_exit(), but this causes a time
# penalty for initializing this is crucial for bash tab
# completion
return os.path.isdir(self.path)
def prepare_install(self, backup_path=None, arg_mode='abort', robust=False):
preparation_report = PreparationReport(self)
present = self.detect_presence()
if present or self.path_exists():
is_link = os.path.islink(self.path)
# Directory exists see what we need to do
error_message = None
if not present:
error_message = "Failed to detect %s presence at %s." % (
self.get_vcs_type_name(), self.path)
if is_link:
error_message += " Path is symlink, only symlink will be removed."
else:
cur_url = self._get_vcsc().get_url()
if cur_url is not None:
# strip trailing slashes for #3269
cur_url = cur_url.rstrip('/')
if not cur_url or cur_url != self.uri.rstrip('/'):
# local repositories get absolute pathnames
if not (os.path.isdir(self.uri) and
os.path.isdir(cur_url) and
samefile(cur_url, self.uri)):
if not self._get_vcsc().url_matches(cur_url, self.uri):
error_message = "Url %s does not match %s requested." % (
cur_url, self.uri)
if error_message is None:
# update should be possible
preparation_report.checkout = False
else:
# If robust ala continue-on-error, just error now and
# it will be continued at a higher level
if robust:
raise MultiProjectException("Update Failed of %s: %s" %
(self.path, error_message))
# prompt the user based on the error code
if arg_mode == 'prompt':
print("Prepare updating %s (version %s) to %s" %
(self.uri, self.version, self.path))
mode = Ui.get_ui().prompt_del_abort_retry(
error_message,
allow_skip=True,
allow_inplace=is_link)
else:
mode = arg_mode
if mode == 'backup':
preparation_report.backup = True
if backup_path is None:
print("Prepare updating %s (version %s) to %s" %
(self.uri, self.version, self.path))
preparation_report.backup_path = \
Ui.get_ui().get_backup_path()
else:
preparation_report.backup_path = backup_path
elif mode == 'abort':
preparation_report.abort = True
preparation_report.error = error_message
elif mode == 'skip':
preparation_report.skip = True
preparation_report.error = error_message
elif mode == 'delete':
preparation_report.backup = False
elif mode == 'inplace':
preparation_report.inplace = True
else:
raise RuntimeError(
'Bug: Unknown option "%s" selected' % mode)
return preparation_report
def install(self,
checkout=True,
backup=True,
backup_path=None,
inplace=False,
timeout=None,
verbose=False,
shallow=False):
"""
Runs the equivalent of SCM checkout for new local repos or
update for existing.
:param checkout: whether to use an update command or
a checkout/clone command
:param backup: if checkout is True and folder exists,
if backup is false folder will be DELETED.
:param backup_path: if checkout is true and backup is true,
move folder to this location
:param inplace: for symlinks, allows to delete contents
at target location and checkout to there.
"""
if checkout is True:
print("[%s] Fetching %s (version %s) to %s" % (
self.get_local_name(), self.uri, self.version, self.get_path()))
if self.path_exists():
if os.path.islink(self.path):
if inplace is False:
# remove same as unlink
os.remove(self.path)
else:
shutil.rmtree(os.path.realpath(self.path))
else:
if backup is False:
shutil.rmtree(self.path)
else:
self.backup(backup_path)
if not self._get_vcsc().checkout(self.uri,
self.version,
timeout=timeout,
verbose=verbose,
shallow=shallow):
raise MultiProjectException(
"[%s] Checkout of %s version %s into %s failed." % (
self.get_local_name(),
self.uri,
self.version,
self.get_path()))
else:
print("[%s] Updating %s" %
(self.get_local_name(), self.get_path()))
if not self._get_vcsc().update(self.version, verbose=verbose,
timeout=timeout):
raise MultiProjectException(
"[%s] Update Failed of %s" % (self.get_local_name(),
self.get_path()))
print("[%s] Done." % self.get_local_name())
def get_path_spec(self):
"yaml as from source"
version = self.version
if version == '':
version = None
return PathSpec(local_name=self.get_local_name(),
path=self.get_path(),
scmtype=self.get_vcs_type_name(),
uri=self.uri,
version=version,
tags=self.get_properties())
def get_versioned_path_spec(self, fetch=False):
"yaml looking up current version"
version = self.version
if version == '':
version = None
revision = None
if version is not None:
# revision is the UID of the version spec, can be them same
revision = self._get_vcsc().get_version(self.version)
if revision is None:
sys.stderr.write("Warning: version '%s' not found for '%s'\n"
% (self.version, self.local_name))
currevision = self._get_vcsc().get_version()
remote_revision = self._get_vcsc().get_remote_version(fetch=fetch)
curr_version = self._get_vcsc().get_current_version_label()
uri = self.uri
curr_uri = self._get_vcsc().get_url()
# uri might be a shorthand notation equivalent to curr_uri
if self._get_vcsc().url_matches(curr_uri, uri):
curr_uri = uri
return PathSpec(local_name=self.get_local_name(),
path=self.get_path(),
scmtype=self.get_vcs_type_name(),
uri=self.uri,
version=version,
curr_version=curr_version,
revision=revision,
currevision=currevision,
remote_revision=remote_revision,
curr_uri=curr_uri,
tags=self.get_properties())
def get_default_remote_label(self):
"""
check remote for e.g. default git branch
"""
return self._get_vcsc().get_default_remote_version_label()
def get_diff(self, basepath=None):
return self._get_vcsc().get_diff(basepath)
def get_status(self, basepath=None, untracked=False):
return self._get_vcsc().get_status(basepath, untracked)
class AVCSConfigElement(VCSConfigElement):
"""
Implementation using vcstools vcsclient, works for types svn, git, hg, bzr, tar
:raises: Lookup Exception for unknown types
"""
def __init__(self, scmtype, path, local_name, uri,
version='', vcsc=None, properties=None):
super(AVCSConfigElement, self).__init__(path,
local_name=local_name,
uri=uri,
version=version,
properties=properties)
self.vcsc = vcsc
self._scmtype = scmtype
def get_vcs_type_name(self):
return self._scmtype
def _get_vcsc(self):
# lazy initializer
if self.vcsc is None:
try:
self.vcsc = get_vcs_client(self._scmtype, self.get_path())
except VcsError as exc:
raise MultiProjectException(
"Unable to create vcs client of type %s for %s: %s" % (
self._scmtype, self.get_path(), exc))
return self.vcsc
def detect_presence(self):
# to make more use of lazy initializer, do not instantiate
# client for just this this is crucial for bash tab completion
if self.get_vcs_type_name() == 'git':
return os.path.exists(os.path.join(self.path, '.git'))
elif self.get_vcs_type_name() == 'svn':
return os.path.isdir(os.path.join(self.path, '.svn'))
elif self.get_vcs_type_name() == 'hg':
return os.path.isdir(os.path.join(self.path, '.hg'))
elif self.get_vcs_type_name() == 'bzr':
return os.path.isdir(os.path.join(self.path, '.bzr'))
else:
return self._get_vcsc().detect_presence()
wstool-0.1.18/src/wstool/config_yaml.py 0000664 0000000 0000000 00000042054 13542237257 0020126 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import yaml
from vcstools.common import urlopen_netrc
from wstool.common import MultiProjectException
__REPOTYPES__ = ['svn', 'bzr', 'hg', 'git', 'tar']
__ALLTYPES__ = __REPOTYPES__ + ['other', 'setup-file']
## The Path spec is a lightweight object to transport the
## specification of a config element between functions,
## independently of yaml structure.
## Specifications are persisted in yaml, this file deals
## with manipulations of any such structures representing configs as
## yaml.
## get_path_spec_from_yaml turns yaml into path_spec, and pathspec
## get_legacy_yaml returns yaml.
def get_yaml_from_uri(uri):
"""reads and parses yaml from a local file or remote uri"""
stream = None
try:
try:
if os.path.isfile(uri):
try:
stream = open(uri, 'r')
except IOError as ioe:
raise MultiProjectException(
"Unable open file [%s]: %s" % (uri, ioe))
else:
try:
stream = urlopen_netrc(uri)
except IOError as ioe2:
raise MultiProjectException(
"Unable to download URL [%s]: %s" % (uri, ioe2))
except ValueError as vae:
raise MultiProjectException(
"Is not a local file, nor a valid URL [%s] : %s" % (uri, vae))
if not stream:
raise MultiProjectException("couldn't load config uri %s" % uri)
try:
yamldata = yaml.safe_load(stream)
except yaml.YAMLError as yame:
raise MultiProjectException(
"Invalid multiproject yaml format in [%s]: %s" % (uri, yame))
# we want a list or a dict, but pyyaml parses xml as string
if type(yamldata) == 'str':
raise MultiProjectException(
"Invalid multiproject yaml format in [%s]: %s" % (uri, yamldata))
finally:
if stream is not None:
stream.close()
return yamldata
def get_path_specs_from_uri(uri, config_filename=None, as_is=False):
"""
Builds a list of PathSpec elements from several types of input
locations, "uris".
The function treats other workspace folders/files as special uris
to prevent mutual conflicts.
:param uri: a folder, a file, or a web url
:param config_filename: name for files to be treated special
as other workspaces
:param as_is: do not rewrite, used for loading the current
workspace config without rewriting
"""
if os.path.isdir(uri):
if (config_filename is not None and
os.path.isfile(os.path.join(uri, config_filename))):
uri = os.path.join(uri, config_filename)
else:
# plain folders returned as themselves
return [PathSpec(local_name=uri)]
yaml_spec = get_yaml_from_uri(uri)
if yaml_spec is None:
return []
specs = [get_path_spec_from_yaml(x) for x in yaml_spec]
if (config_filename is not None and
not as_is and
os.path.isfile(uri) and
os.path.basename(uri) == config_filename):
# treat config files and folders with such files special
# to prevent 2 workspaces from interacting
specs = rewrite_included_source(specs, os.path.dirname(uri))
return specs
def rewrite_included_source(source_path_specs, source_dir):
"""
assumes source_path_specs is the contents of a config file in
another directory source dir. It rewrites all elements, by changing
any relative path relative to source dir and changing vcs
types to non-vcs types types, to prevent two environments from
conflicting
"""
for index, pathspec in enumerate(source_path_specs):
local_name = os.path.normpath(os.path.join(source_dir,
pathspec.get_local_name()))
pathspec.set_local_name(local_name)
if pathspec.get_path() is not None:
path = os.path.normpath(
os.path.join(source_dir, pathspec.get_path()))
pathspec.set_path(path)
pathspec.detach_vcs_info()
source_path_specs[index] = pathspec
return source_path_specs
def aggregate_from_uris(config_uris, config_filename=None, allow_other_element=True):
"""
Builds a List of PathSpec from a list of location strings (uri,
paths). If locations is a folder, attempts to find config_filename
in it, and use "folder/config_filename" instead(rewriting element
path and stripping scm nature), else add folder as PathSpec.
Anything else, parse yaml at location, and add a PathSpec for each
element.
:param config_uris: source of yaml
:param config_filename: file to use when given a folder
:param allow_other_element: if False, discards elements
to be added without SCM information
"""
aggregate_source_yaml = []
# build up a merged list of config elements from all given config_uris
if config_uris is None:
return []
for loop_uri in config_uris:
source_path_specs = get_path_specs_from_uri(
loop_uri, config_filename)
# allow duplicates, dealt with in Config class
if not allow_other_element:
for spec in source_path_specs:
if not spec.get_scmtype():
raise MultiProjectException(
"Forbidden non-SCM element: %s (%s)" %
(spec.get_local_name(), spec.get_legacy_type()))
aggregate_source_yaml.extend(source_path_specs)
return aggregate_source_yaml
class PathSpec:
def __init__(self,
# localname is used as ID, currently also is used as path
local_name,
scmtype=None,
uri=None,
version=None,
curr_version=None,
tags=None,
revision=None,
currevision=None,
remote_revision=None,
path=None,
curr_uri=None):
"""
Fills in local properties based on dict, unifies different syntaxes
:param local-name: to be unique within config, filesystem path to folder
:param scmtype: one of __ALLTYPES__
:param uri: uri from config file
:param version: version label from config file (branchname, tagname, sha-id)
:param cur_version: version information label(s) from VCS (branchname, remote, tracking branch)
:param tags: arbirtrary meta-information (used for ROS package indexing)
:param revision: unique id of label stored in version
:param currrevision: unique id of actual version in file system
:param path: path to folder (currently equivalent to local_name)
:param curr_uri: actual remote uri used in local checkout
"""
self._local_name = local_name
self._path = path
self._uri = uri
self._curr_uri = curr_uri
self._version = version
self._curr_version = curr_version
self._scmtype = scmtype
self._tags = tags or []
self._revision = revision
self._currevision = currevision
self._remote_revision = remote_revision
def __str__(self):
return str(self.get_legacy_yaml())
def __repr__(self):
return "PathSpec(%s)" % self.__str__()
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
def detach_vcs_info(self):
"""if wrapper has VCS information, remove it to make it a plain folder"""
if self._scmtype is not None:
self._scmtype = None
self._uri = None
self._version = None
self._curr_version = None
self._revision = None
self._currevision = None
self._remote_revision = None
def get_legacy_type(self):
"""return one of __ALLTYPES__"""
if self._scmtype is not None:
return self._scmtype
elif self._tags is not None and 'setup-file' in self._tags:
return 'setup-file'
return 'other'
def get_legacy_yaml(self, spec=True, exact=False):
"""
:param spec: If True, the version information will come from the
workspace .rosinstall. If False, the version information will come
from the current work trees.
:param exact: If True, the versions will be set to the exact commit
UUIDs. If False, the version name will be used, which might be a
branch name aut cetera.
return something like
{hg: {local-name: common,
version: common-1.0.2,
uri: https://kforge.org/common/}}
"""
# TODO switch to new syntax
properties = {'local-name': self._local_name}
if spec:
if self._uri is not None:
properties['uri'] = self._uri
if exact:
if self._revision is not None:
properties['version'] = self._revision
else:
if self._version is not None:
properties['version'] = self._version
else:
if self._curr_uri is not None:
properties['uri'] = self._curr_uri
if exact:
if self._currevision is not None:
properties['version'] = self._currevision
else:
if self._curr_version is not None:
properties['version'] = self._curr_version
if self._tags is not None:
for tag in self._tags:
if tag != 'setup-file' and tag != []:
if type(tag) == dict:
properties.update(tag)
else:
properties[tag] = None
yaml_dict = {self.get_legacy_type(): properties}
return yaml_dict
def get_local_name(self):
return self._local_name
def set_local_name(self, local_name):
self._local_name = local_name
def get_path(self):
return self._path
def set_path(self, path):
self._path = path
def get_tags(self):
return self._tags
def get_scmtype(self):
return self._scmtype
def get_version(self):
return self._version
def get_curr_version(self):
return self._curr_version
def get_revision(self):
return self._revision
def get_current_revision(self):
return self._currevision
def get_remote_revision(self):
return self._remote_revision
def get_uri(self):
return self._uri
def get_curr_uri(self):
return self._curr_uri
def get_path_spec_from_yaml(yaml_dict):
"""
Fills in local properties based on dict, unifies different syntaxes
"""
local_name = None
uri = None
version = None
scmtype = None
tags = []
if type(yaml_dict) != dict:
raise MultiProjectException(
"Yaml for each element must be in YAML dict form: %s " % yaml_dict)
# old syntax:
# - hg: {local-name: common_rosdeps,
# version: common_rosdeps-1.0.2,
# uri: https://kforge.ros.org/common/rosdepcore}
# - setup-file: {local-name: /opt/ros/fuerte/setup.sh}
# - other: {local-name: /opt/ros/fuerte/share/ros}
# - other: {local-name: /opt/ros/fuerte/share}
# - other: {local-name: /opt/ros/fuerte/stacks}
if yaml_dict is None or len(yaml_dict) == 0:
raise MultiProjectException("no element in yaml dict.")
if len(yaml_dict) > 1:
raise MultiProjectException(
"too many keys in element dict %s" % (list(yaml_dict.keys())))
if not list(yaml_dict.keys())[0] in __ALLTYPES__:
raise MultiProjectException(
"Unknown element type '%s'" % (list(yaml_dict.keys())[0]))
firstkey = list(yaml_dict.keys())[0]
if firstkey in __REPOTYPES__:
scmtype = list(yaml_dict.keys())[0]
if firstkey == 'setup-file':
tags.append('setup-file')
values = yaml_dict[firstkey]
if values is not None:
for key, value in list(values.items()):
if key == "local-name":
local_name = value
elif key == "meta":
tags.append({key: value})
elif key == "uri":
uri = value
elif key == "version":
version = value
else:
raise MultiProjectException(
"Unknown key %s in %s" % (key, yaml_dict))
# global validation
if local_name is None:
raise MultiProjectException(
"Config element without a local-name: %s" % (yaml_dict))
if scmtype != None:
if uri is None:
raise MultiProjectException(
"scm type without declared uri in %s" % (values))
# local_name is fixed, path may be normalized, made absolute, etc.
path = local_name
return PathSpec(local_name=local_name,
path=path,
scmtype=scmtype,
uri=uri,
version=version,
tags=tags)
def generate_config_yaml(config, filename, header, pretty=False,
sort_with_localname=False, spec=True,
exact=False, vcs_only=False):
"""
Writes file filename with header first and then the config as YAML.
:param config: The configuration containing all the entries to be included
in the generated YAML.
:param filename: If filename is not an absolute path, it will be assumed to
be relative to config.get_base_path(). If filename is None, the output will
be sent to stdout instead of a file.
:param header: A header to be included with the generated config YAML.
:param pretty: If True, the generated config YAML will be printed in
long-form YAML. If false, the default flow style will be used instead.
:param sort_with_localname: If true, config entries will be sorted by their
localname fields. If false, the order will be as passed in through config.
:param spec: If True, the version information will come from the workspace
.rosinstall. If False, the version information will come from the current
work trees.
:param exact: If True, the versions will be set to the exact commit UUIDs.
If False, the version name will be used, which might be a branch name
aut cetera.
:param vcs_only: If True, the generated config YAML will include only
version-controlled entries. If False, all entries in current workspace will
be included.
"""
if not os.path.exists(config.get_base_path()):
os.makedirs(config.get_base_path())
content = ""
if header:
content += header
# Do a pass-through if just pulling versioning information straight from
# the .rosinstall
passthrough = spec and not exact
items = config.get_source(not passthrough, vcs_only)
if sort_with_localname:
items = sorted(items, key=lambda x: x.get_local_name())
items = [x.get_legacy_yaml(spec, exact) for x in items]
if items:
if pretty:
content += yaml.safe_dump(items, allow_unicode=True,
default_flow_style=False)
else:
content += yaml.safe_dump(items, default_flow_style=None)
if filename:
config_filepath = filename if os.path.isabs(filename) else \
os.path.realpath(os.path.join(config.get_base_path(), filename))
with open(config_filepath, 'w+b') as f:
f.write(content.encode('UTF-8'))
else:
print(content)
wstool-0.1.18/src/wstool/helpers.py 0000664 0000000 0000000 00000003764 13542237257 0017306 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
import os
import sys
import codecs
import subprocess
from wstool.config_elements import SetupConfigElement
ROSINSTALL_FILENAME = ".rosinstall"
def get_ros_package_path(config):
""" Return the simplifed ROS_PACKAGE_PATH """
code_trees = []
for tree_el in reversed(config.get_config_elements()):
if not os.path.isfile(tree_el.get_path()):
code_trees.append(tree_el.get_path())
return code_trees
wstool-0.1.18/src/wstool/multiproject_cli.py 0000664 0000000 0000000 00000170051 13542237257 0021206 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
from __future__ import print_function
import os
import sys
import textwrap
import shutil
import datetime
import yaml
from optparse import OptionParser, IndentedHelpFormatter
from wstool.cli_common import get_info_list, get_info_table, \
get_info_table_raw_csv, ONLY_OPTION_VALID_ATTRS
from wstool.common import samefile, select_element, select_elements, \
MultiProjectException, normalize_uri, string_diff
from wstool.config_yaml import PathSpec, get_path_spec_from_yaml
import wstool.multiproject_cmd as multiproject_cmd
from wstool.ui import Ui
# implementation of single CLI commands (extracted for use in several
# overlapping scripts)
# usage help
__MULTIPRO_CMD_DICT__ = {
"help": "provide help for commands",
"init": "set up a directory as workspace",
"info": "Overview of some entries",
"merge": "merges your workspace with another config set",
"set": "add or changes one entry from your workspace config",
"update": "update or check out some of your config elements",
"remove": "remove an entry from your workspace config, without deleting files",
"export": "export a snapshot of the workspace",
"diff": "print a diff over some SCM controlled entries",
"foreach": "run shell command in given entries",
"status": "print the change status of files in some SCM controlled entries",
"scrape": "interactively add all found unmanaged VCS subfolders to workspace"
}
# usage help ordering and sections
__MULTIPRO_CMD_HELP_LIST__ = ['help', 'init',
None, 'set', 'merge', 'remove', 'scrape',
None, 'update',
None, 'info', 'export', 'status', 'diff', 'foreach']
# command aliases
__MULTIPRO_CMD_ALIASES__ = {'update': 'up',
'remove': 'rm',
'status': 'st',
'diff': 'di'}
def get_header(progname):
config_header = ("# THIS IS AN AUTOGENERATED FILE, LAST GENERATED USING %s ON %s\n"
% (progname, datetime.date.today().isoformat()))
return config_header
class IndentedHelpFormatterWithNL(IndentedHelpFormatter):
def format_description(self, description):
if not description:
return ""
desc_width = self.width - self.current_indent
indent = " " * self.current_indent
# the above is still the same
bits = description.split('\n')
formatted_bits = [
textwrap.fill(bit,
desc_width,
initial_indent=indent,
subsequent_indent=indent)
for bit in bits]
result = "\n".join(formatted_bits) + "\n"
return result
def _get_mode_from_options(parser, options):
mode = 'prompt'
if options.delete_changed:
mode = 'delete'
if options.abort_changed:
if mode == 'delete':
parser.error("delete-changed-uris is mutually exclusive with abort-changed-uris")
mode = 'abort'
if options.backup_changed != '':
if mode == 'delete':
parser.error("delete-changed-uris is mutually exclusive with backup-changed-uris")
if mode == 'abort':
parser.error("abort-changed-uris is mutually exclusive with backup-changed-uris")
mode = 'backup'
return mode
def _get_element_diff(new_path_spec, config_old, extra_verbose=False):
"""
:returns: a string telling what changed for element compared to old config
"""
if new_path_spec is None or config_old is None:
return ''
output = [' %s' % new_path_spec.get_local_name()]
if extra_verbose:
old_element = None
if config_old is not None:
old_element = select_element(config_old.get_config_elements(),
new_path_spec.get_local_name())
if old_element is None:
if new_path_spec.get_scmtype() is not None:
output.append(
" \t%s %s %s" % (new_path_spec.get_scmtype(),
new_path_spec.get_uri(),
new_path_spec.get_version() or ''))
else:
old_path_spec = old_element.get_path_spec()
accessor_map = {PathSpec.get_scmtype: 'scmtype',
PathSpec.get_version: 'version',
PathSpec.get_revision: 'revision',
PathSpec.get_current_revision: 'current revision',
PathSpec.get_curr_uri: 'current_uri',
PathSpec.get_uri: 'specified uri'}
for accessor, label in list(accessor_map.items()):
old_val = accessor(old_path_spec)
new_val = accessor(new_path_spec)
if old_val is not None and\
old_val != new_val:
diff = string_diff(old_val, new_val)
output.append(" \t%s: %s -> %s;" % (label, old_val, diff))
elif old_val is None and\
new_val is not None and\
new_val != "" and\
new_val != []:
output.append(" %s = %s" % (label,
new_val))
return ''.join(output)
def prompt_merge(target_path,
additional_uris,
additional_specs,
path_change_message=None,
merge_strategy='KillAppend',
confirmed=False,
confirm=False,
show_advanced=True,
show_verbosity=True,
config_filename=None,
config=None,
allow_other_element=True):
"""
Prompts the user for the resolution of a merge. Without
further options, will prompt only if elements change. New
elements are just added without prompt.
:param target_path: Location of the config workspace
:param additional_uris: uris from which to load more elements
:param additional_specs: path specs for additional elements
:param path_change_message: Something to tell the user about elements order
:param merge_strategy: See Config.insert_element
:param confirmed: Never ask
:param confirm: Always ask, supercedes confirmed
:param config: None or a Config object for target path if available
:param show_advanced: if true allow to change merge strategy
:param show_verbosity: if true allows to change verbosity
:param allow_other_element: if False merge fails hwen it could cause other elements
:returns: tupel (Config or None if no change, bool path_changed)
"""
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=config_filename)
elif config.get_base_path() != target_path:
msg = "Config path does not match %s %s " % (config.get_base_path(),
target_path)
raise MultiProjectException(msg)
local_names_old = [x.get_local_name() for x in config.get_config_elements()]
extra_verbose = confirmed or confirm
abort = False
last_merge_strategy = None
while not abort:
if (last_merge_strategy is None
or last_merge_strategy != merge_strategy):
if not config_filename:
# should never happen right now with rosinstall/rosws/wstool
# TODO Need a better way to work with clones of original config
raise ValueError('Cannot merge when no config filename is set')
newconfig = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=config_filename)
config_actions = multiproject_cmd.add_uris(
config=newconfig,
additional_uris=additional_uris,
config_filename=None,
merge_strategy=merge_strategy,
allow_other_element=allow_other_element)
for path_spec in additional_specs:
action = newconfig.add_path_spec(path_spec, merge_strategy)
config_actions[path_spec.get_local_name()] = (action, path_spec)
last_merge_strategy = merge_strategy
local_names_new = [x.get_local_name() for x in newconfig.get_config_elements()]
path_changed = False
ask_user = False
output = ""
new_elements = []
changed_elements = []
discard_elements = []
for localname, (action, new_path_spec) in list(config_actions.items()):
index = -1
if localname in local_names_old:
index = local_names_old.index(localname)
if action == 'KillAppend':
ask_user = True
if (index > -1 and local_names_old[:index + 1] == local_names_new[:index + 1]):
action = 'MergeReplace'
else:
changed_elements.append(_get_element_diff(new_path_spec, config, extra_verbose))
path_changed = True
if action == 'Append':
path_changed = True
new_elements.append(_get_element_diff(new_path_spec,
config,
extra_verbose))
elif action == 'MergeReplace':
changed_elements.append(_get_element_diff(new_path_spec,
config,
extra_verbose))
ask_user = True
elif action == 'MergeKeep':
discard_elements.append(_get_element_diff(new_path_spec,
config,
extra_verbose))
ask_user = True
if len(changed_elements) > 0:
output += "\n Change details of element (Use --merge-keep or --merge-replace to change):\n"
if extra_verbose:
output += " %s\n" % ("\n".join(sorted(changed_elements)))
else:
output += " %s\n" % (", ".join(sorted(changed_elements)))
if len(new_elements) > 0:
output += "\n Add new elements:\n"
if extra_verbose:
output += " %s\n" % ("\n".join(sorted(new_elements)))
else:
output += " %s\n" % (", ".join(sorted(new_elements)))
if local_names_old != local_names_new[:len(local_names_old)]:
old_order = ' '.join(reversed(local_names_old))
new_order = ' '.join(reversed(local_names_new))
output += "\n %s " % path_change_message or "Element order change"
output += "(Use --merge-keep or --merge-replace to prevent) "
output += "from\n %s\n to\n %s\n\n" % (old_order, new_order)
ask_user = True
if output == "":
return (None, False)
if not confirm and (confirmed or not ask_user):
print(" Performing actions: ")
print(output)
return (newconfig, path_changed)
else:
print(output)
showhelp = True
while(showhelp):
showhelp = False
prompt = "Continue: (y)es, (n)o"
if show_verbosity:
prompt += ", (v)erbosity"
if show_advanced:
prompt += ", (a)dvanced options"
prompt += ": "
mode_input = Ui.get_ui().get_input(prompt)
if mode_input == 'y':
return (newconfig, path_changed)
elif mode_input == 'n':
abort = True
elif show_advanced and mode_input == 'a':
strategies = {'MergeKeep': "(k)eep",
'MergeReplace': "(s)witch in",
'KillAppend': "(a)ppending"}
unselected = [v for k, v in
list(strategies.items())
if k != merge_strategy]
print("""New entries will just be appended to the config and
appear at the beginning of your ROS_PACKAGE_PATH. The merge strategy
decides how to deal with entries having a duplicate localname or path.
"(k)eep" means the existing entry will stay as it is, the new one will
be discarded. Useful for getting additional elements from other
workspaces without affecting your setup.
"(s)witch in" means that the new entry will replace the old in the
same position. Useful for upgrading/downgrading.
"switch (a)ppend" means that the existing entry will be removed, and
the new entry appended to the end of the list. This maintains order
of elements in the order they were given.
Switch append is the default.
""")
prompt = "Change Strategy %s: " % (", ".join(unselected))
mode_input = Ui.get_ui().get_input(prompt)
if mode_input == 's':
merge_strategy = 'MergeReplace'
elif mode_input == 'k':
merge_strategy = 'MergeKeep'
elif mode_input == 'a':
merge_strategy = 'KillAppend'
elif show_verbosity and mode_input == 'v':
extra_verbose = not extra_verbose
if abort:
print("No changes made.")
print('==========================================')
return (None, False)
def list_usage(progname, description, command_keys, command_helps, command_aliases):
"""
Constructs program usage for a list of commands with help and aliases.
Contructs in the order given in command keys. Newlines can be used for
command sections by adding None entries to command_keys list.
Only one alias allowed per command.
:param command_keys: list of keys or None to print help or empty lines
:param command_helps: dict{key: help}
:param command_aliases: dict{key: alias}
:returns: usage string (multiline)
"""
dvars = {'prog': progname}
dvars.update(vars())
result = []
result.append(description % dvars)
for key in command_keys:
if key in command_aliases:
alias = ' (%s)' % command_aliases[key]
else:
alias = ''
if key is not None:
result.append(("%s%s" % (key, alias)).ljust(10) + ' \t' + command_helps[key])
else:
result.append('')
return '\n'.join(result)
class MultiprojectCLI:
def __init__(self,
progname,
config_filename=None,
allow_other_element=False,
config_generator=None):
'''
creates the instance. Historically, rosinstall allowed "other"
elements that went into the ROS_PACKAGE_PATH, but were ignored
for vcs operations. A pure vcs tool has no use for such
elements.
:param progname: name to diplay in help
:param config_filename: filename of files maintaining workspaces (.rosinstall)
:param allow_other_element: bool, if True rosinstall semantics for "other" apply
:param config_generator: function that writes config file
'''
self.config_filename = config_filename
self.config_generator = config_generator or multiproject_cmd.cmd_persist_config
self.progname = progname
self.allow_other_element = allow_other_element
def cmd_init(self, argv):
if self.config_filename is None:
print('Error: Bug: config filename required for init')
return 1
parser = OptionParser(
usage="""usage: %s init [TARGET_PATH [SOURCE_PATH]]?""" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["init"] + """
%(prog)s init does the following:
1. Reads folder/file/web-uri SOURCE_PATH looking for a rosinstall yaml
2. Creates new %(cfg_file)s file at TARGET-PATH
SOURCE_PATH can e.g. be a web uri or a rosinstall file with vcs entries only
If PATH is not given, uses current dir.
Examples:
$ %(prog)s init ~/fuerte /opt/ros/fuerte
""" % {'cfg_file': self.config_filename, 'prog': self.progname},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
parser.add_option("--continue-on-error", dest="robust", default=False,
help="Continue despite checkout errors",
action="store_true")
parser.add_option("-j", "--parallel", dest="jobs", default=1,
help="How many parallel threads to use for installing",
action="store")
parser.add_option("--shallow", dest="shallow", default=False,
help="Checkout only latest revision if possible",
action="store_true")
(options, args) = parser.parse_args(argv)
if len(args) < 1:
target_path = '.'
else:
target_path = args[0]
if not os.path.isdir(target_path):
if not os.path.exists(target_path):
os.mkdir(target_path)
else:
print('Error: Cannot create in target path %s ' % target_path)
if os.path.exists(os.path.join(target_path, self.config_filename)):
print('Error: There already is a workspace config file %s at "%s". Use %s install/modify.' %
(self.config_filename, target_path, self.progname))
return 1
if len(args) > 2:
parser.error('Too many arguments')
if len(args) == 2:
print('Using initial elements from: %s' % args[1])
config_uris = [args[1]]
else:
config_uris = []
config = multiproject_cmd.get_config(
basepath=target_path,
additional_uris=config_uris,
# catkin workspaces have no reasonable chaining semantics
# config_filename=self.config_filename
)
if config_uris and len(config.get_config_elements()) == 0:
sys.stderr.write('WARNING: Not using any element from %s\n' % config_uris[0])
for element in config.get_config_elements():
if not element.is_vcs_element():
raise MultiProjectException("wstool does not allow elements without vcs information. %s" % element)
# includes ROS specific files
if self.config_filename:
print("Writing %s" % os.path.join(config.get_base_path(), self.config_filename))
self.config_generator(config, self.config_filename, get_header(self.progname))
## install or update each element
install_success = multiproject_cmd.cmd_install_or_update(
config,
robust=False,
shallow=options.shallow,
num_threads=int(options.jobs))
if not install_success:
print("Warning: installation encountered errors, but --continue-on-error was requested. Look above for warnings.")
print("\nupdate complete.")
return 0
def cmd_merge(self, target_path, argv, config=None):
parser = OptionParser(
usage="usage: %s merge [URI] [OPTIONS]" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["merge"] + """.
The command merges config with given other rosinstall element sets, from files or web uris.
The default workspace will be inferred from context, you can specify one using -t.
By default, when an element in an additional URI has the same
local-name as an existing element, the existing element will be
replaced. In order to ensure the ordering of elements is as
provided in the URI, use the option --merge-kill-append.
Examples:
$ %(prog)s merge someother.rosinstall
You can use '-' to pipe in input, as an example:
$ roslocate info robot_model | %(prog)s merge -
""" % {'prog': self.progname},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
# same options as for multiproject
parser.add_option(
"-a", "--merge-kill-append", dest="merge_kill_append",
default=False,
help="merge by deleting given entry and appending new one",
action="store_true")
parser.add_option("-k", "--merge-keep", dest="merge_keep",
default=False,
help="merge by keeping existing entry and discarding new one",
action="store_true")
parser.add_option("-r", "--merge-replace", dest="merge_replace",
default=False,
help="(default) merge by replacing given entry with new one maintaining ordering",
action="store_true")
parser.add_option("-y", "--confirm-all", dest="confirm_all",
default='',
help="do not ask for confirmation unless strictly necessary",
action="store_true")
# required here but used one layer above
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
(options, args) = parser.parse_args(argv)
if len(args) > 1:
print("Error: Too many arguments.")
print(parser.usage)
return -1
if len(args) == 0:
print("Error: Too few arguments.")
print(parser.usage)
return -1
config_uris = args
specs = []
if config_uris[0] == '-':
pipedata = "".join(sys.stdin.readlines())
try:
yamldicts = yaml.safe_load(pipedata)
except yaml.YAMLError as e:
raise MultiProjectException(
"Invalid yaml format: \n%s \n%s" % (pipedata, e))
if yamldicts is None:
parser.error("No Input read from stdin")
# cant have user interaction and piped input
options.confirm_all = True
specs.extend([get_path_spec_from_yaml(x) for x in yamldicts])
config_uris = []
merge_strategy = None
count_mergeoptions = 0
if options.merge_kill_append:
merge_strategy = 'KillAppend'
count_mergeoptions += 1
if options.merge_keep:
merge_strategy = 'MergeKeep'
count_mergeoptions += 1
if options.merge_replace:
merge_strategy = 'MergeReplace'
count_mergeoptions += 1
if count_mergeoptions > 1:
parser.error("You can only provide one merge-strategy")
# default option
if count_mergeoptions == 0:
merge_strategy = 'MergeReplace'
(newconfig, _) = prompt_merge(
target_path,
additional_uris=config_uris,
additional_specs=specs,
path_change_message="element order changed",
merge_strategy=merge_strategy,
confirmed=options.confirm_all,
config_filename=self.config_filename,
config=config,
allow_other_element=self.allow_other_element)
if newconfig is not None:
print("Config changed, maybe you need run %s update to update SCM entries." % self.progname)
print("Overwriting %s" % os.path.join(newconfig.get_base_path(), self.config_filename))
shutil.copy(os.path.join(newconfig.get_base_path(), self.config_filename), "%s.bak" % os.path.join(newconfig.get_base_path(), self.config_filename))
self.config_generator(newconfig, self.config_filename, get_header(self.progname))
print("\nupdate complete.")
else:
print("Merge caused no change, no new elements found")
return 0
def cmd_diff(self, target_path, argv, config=None):
parser = OptionParser(usage="usage: %s diff [localname]* " % self.progname,
description=__MULTIPRO_CMD_DICT__["diff"],
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
# required here but used one layer above
parser.add_option("-t", "--target-workspace", dest="workspace",
default=None,
help="which workspace to use",
action="store")
(_, args) = parser.parse_args(argv)
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException(
"Config path does not match %s %s " % (config.get_base_path(),
target_path))
if len(args) > 0:
difflist = multiproject_cmd.cmd_diff(config, localnames=args)
else:
difflist = multiproject_cmd.cmd_diff(config)
alldiff = []
for entrydiff in difflist:
if entrydiff['diff'] is not None and entrydiff['diff'] != '':
alldiff.append(entrydiff['diff'])
result = '\n'.join(alldiff)
# result has no newline at end
if result:
print(result)
return False
def cmd_foreach(self, target_path, argv, config=None):
"""Run shell commands in each repository."""
parser = OptionParser(
usage=('usage: %s foreach [[localname]* | [VCSFILTER]*]'
' [command] [OPTIONS]' % self.progname),
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__['foreach'] + """.
Example:
$ %(progname)s foreach --git 'git status'
""" % { 'progname': self.progname},
epilog='See: http://www.ros.org/wiki/rosinstall for details')
parser.add_option('--shell', default=False,
help='use the shell as the program to execute',
action='store_true')
parser.add_option('--no-stdout', dest='show_stdout',
default=True,
help='do not show stdout',
action='store_false')
parser.add_option('--no-stderr', dest='show_stderr',
default=True,
help='do not show stderr',
action='store_false')
parser.add_option("--git", dest="git", default=False,
help="run command in git entries",
action="store_true")
parser.add_option("--svn", dest="svn", default=False,
help="run command in svn entries",
action="store_true")
parser.add_option("--hg", dest="hg", default=False,
help="run command in hg entries",
action="store_true")
parser.add_option("--bzr", dest="bzr", default=False,
help="run command in bzr entries",
action="store_true")
parser.add_option("-m", "--timeout", dest="timeout",
default=None,
help="How long to wait for each repo before failing [seconds]",
action="store", type=float)
parser.add_option("-j", "--parallel", dest="jobs",
default=1,
help="How many parallel threads to use for running the custom commands",
action="store")
parser.add_option("-v", "--verbose", dest="verbose",
default=False,
help="Whether to print out more information",
action="store_true")
# -t option required here for help but used one layer above
# see cli_common
parser.add_option("-t", "--target-workspace", dest="workspace",
default=None,
help="which workspace to use",
action="store")
(options, args) = parser.parse_args(argv)
if args:
localnames, command = args[:-1], args[-1]
localnames = localnames if localnames else None
else:
print("Error: Too few arguments.")
print(parser.usage)
return -1
scm_types = []
if options.git:
scm_types.append('git')
if options.svn:
scm_types.append('svn')
if options.hg:
scm_types.append('hg')
if options.bzr:
scm_types.append('bzr')
if not scm_types:
scm_types = None
if localnames and scm_types:
sys.stderr.write("Error: Either localnames or scm-filters"
" [--(git|svn|hg|bzr)] should be specified.\n")
return -1
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException('Config path does not match %s %s' %
(config.get_base_path(), target_path))
# run shell command
outputs = multiproject_cmd.cmd_foreach(config,
command=command,
localnames=localnames,
num_threads=int(options.jobs),
timeout=options.timeout,
scm_types=scm_types,
shell=options.shell,
verbose=options.verbose)
def add_localname_prefix(localname, lines):
return ['[%s] %s' % (localname, line) for line in lines]
for output in outputs:
localname = output['entry'].get_local_name()
rc = output['returncode']
if options.show_stdout:
if output['stdout'] is None:
continue
lines = output['stdout'].strip().split('\n')
lines = add_localname_prefix(localname, lines)
sys.stdout.write('\n'.join(lines))
sys.stdout.write('\n')
if options.show_stderr:
lines = []
if output['stderr'] is not None:
lines += output['stderr'].strip().split('\n')
if rc != 0:
lines += ['Command failed with return code [%s]' % rc]
if not lines:
continue
lines = add_localname_prefix(localname, lines)
sys.stderr.write('\n'.join(lines))
sys.stderr.write('\n')
return 0 if all([o['returncode'] == 0 for o in outputs]) else 1
def cmd_status(self, target_path, argv, config=None):
parser = OptionParser(usage="usage: %s status [localname]* " % self.progname,
description=__MULTIPRO_CMD_DICT__["status"] +
". The status columns meanings are as the respective SCM defines them.",
epilog="""See: http://www.ros.org/wiki/rosinstall for details""")
parser.add_option("-u", "--untracked", dest="untracked",
default=False,
help="Also shows untracked files",
action="store_true")
# -t option required here for help but used one layer above, see cli_common
parser.add_option("-t", "--target-workspace", dest="workspace",
default=None,
help="which workspace to use",
action="store")
(options, args) = parser.parse_args(argv)
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException(
"Config path does not match %s %s " % (config.get_base_path(),
target_path))
if len(args) > 0:
statuslist = multiproject_cmd.cmd_status(config,
localnames=args,
untracked=options.untracked)
else:
statuslist = multiproject_cmd.cmd_status(config,
untracked=options.untracked)
allstatus = []
for entrystatus in statuslist:
if entrystatus['status'] is not None:
allstatus.append(entrystatus['status'])
print(''.join(allstatus), end='')
return 0
def cmd_set(self, target_path, argv, config=None):
"""
command for modifying/adding a single entry
:param target_path: where to look for config
:param config: config to use instead of parsing file anew
"""
usage = ("usage: %s set [localname] [[SCM-URI] --(%ssvn|hg|git|bzr) [--version=VERSION]?]?" %
(self.progname, 'detached|' if self.allow_other_element else ''))
parser = OptionParser(
usage=usage,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["set"] + """
The command will infer whether you want to add or modify an entry. If
you modify, it will only change the details you provide, keeping
those you did not provide. if you only provide a uri, will use the
basename of it as localname unless such an element already exists.
The command only changes the configuration, to checkout or update
the element, run %(progname)s update afterwards.
Examples:
$ %(progname)s set robot_model --hg https://kforge.ros.org/robotmodel/robot_model
$ %(progname)s set robot_model --version-new robot_model-1.7.1
%(detached)s
""" % { 'progname': self.progname,
'detached': '$ %s set robot_model --detached' % (self.progname
if self.allow_other_element
else '')},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
if self.allow_other_element:
parser.add_option("--detached", dest="detach", default=False,
help="make an entry unmanaged (default for new element)",
action="store_true")
parser.add_option("-v", "--version-new", dest="version", default=None,
help="point SCM to this version",
action="store")
parser.add_option("--git", dest="git", default=False,
help="make an entry a git entry",
action="store_true")
parser.add_option("--svn", dest="svn", default=False,
help="make an entry a subversion entry",
action="store_true")
parser.add_option("--hg", dest="hg", default=False,
help="make an entry a mercurial entry",
action="store_true")
parser.add_option("--bzr", dest="bzr", default=False,
help="make an entry a bazaar entry",
action="store_true")
parser.add_option("-y", "--confirm", dest="confirm", default='',
help="Do not ask for confirmation",
action="store_true")
parser.add_option("-u", "--update", dest="do_update", default=False,
help="update repository after set",
action="store_true")
# -t option required here for help but used one layer above, see cli_common
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
(options, args) = parser.parse_args(argv)
if not self.allow_other_element:
options.detach = False
if len(args) > 2:
print("Error: Too many arguments.")
print(parser.usage)
return -1
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException(
"Config path does not match %s %s " % (config.get_base_path(),
target_path))
scmtype = None
count_scms = 0
if options.git:
scmtype = 'git'
count_scms += 1
if options.svn:
scmtype = 'svn'
count_scms += 1
if options.hg:
scmtype = 'hg'
count_scms += 1
if options.bzr:
scmtype = 'bzr'
count_scms += 1
if options.detach:
count_scms += 1
if count_scms > 1:
parser.error(
"You cannot provide more than one scm provider option")
if len(args) == 0:
parser.error("Must provide a localname")
element = select_element(config.get_config_elements(), args[0])
uri = None
if len(args) == 2:
uri = args[1]
version = None
if options.version is not None:
version = options.version.strip("'\"")
# create spec object
if element is None:
if scmtype is None and not self.allow_other_element:
# for modification, not re-stating the scm type is
# okay, for new elements not
parser.error("You have to provide one scm provider option")
# asssume is insert, choose localname
localname = os.path.normpath(args[0])
rel_path = os.path.relpath(os.path.realpath(localname),
os.path.realpath(config.get_base_path()))
if os.path.isabs(localname):
# use shorter localname for folders inside workspace
if not rel_path.startswith('..'):
localname = rel_path
else:
# got a relative path as localname, could point to a dir or be
# meant relative to workspace
if not samefile(os.getcwd(), config.get_base_path()):
if os.path.isdir(localname):
parser.error(
"Cannot decide which one you want to add:\n%s\n%s" % (
os.path.abspath(localname),
os.path.join(config.get_base_path(), localname)))
if not rel_path.startswith('..'):
localname = rel_path
spec = PathSpec(local_name=localname,
uri=normalize_uri(uri, config.get_base_path()),
version=version,
scmtype=scmtype)
else:
# modify
localname = element.get_local_name()
old_spec = element.get_path_spec()
if options.detach:
spec = PathSpec(local_name=localname)
else:
# '' evals to False, we do not want that
if version is None:
version = old_spec.get_version()
spec = PathSpec(local_name=localname,
uri=normalize_uri(uri or old_spec.get_uri(),
config.get_base_path()),
version=version,
scmtype=scmtype or old_spec.get_scmtype(),
path=old_spec.get_path())
if spec.get_legacy_yaml() == old_spec.get_legacy_yaml():
if not options.detach and spec.get_scmtype() is not None:
parser.error(
"Element %s already exists, did you mean --detached ?" % spec)
parser.error("Element %s already exists" % spec)
(newconfig, path_changed) = prompt_merge(
target_path,
additional_uris=[],
additional_specs=[spec],
merge_strategy='MergeReplace',
confirmed=options.confirm,
confirm=not options.confirm,
show_verbosity=False,
show_advanced=False,
config_filename=self.config_filename,
config=config,
allow_other_element=self.allow_other_element)
if newconfig is not None:
print("Overwriting %s" % os.path.join(
newconfig.get_base_path(), self.config_filename))
shutil.copy(
os.path.join(newconfig.get_base_path(), self.config_filename),
"%s.bak" % os.path.join(newconfig.get_base_path(), self.config_filename))
self.config_generator(newconfig, self.config_filename)
if options.do_update:
install_success = multiproject_cmd.cmd_install_or_update(
newconfig, localnames=[localname])
if not install_success:
print("Warning: installation encountered errors.")
print("\nupdate complete.")
elif (spec.get_scmtype() is not None):
print("Config changed, remember to run '%s update %s' to update the folder from %s" %
(self.progname, spec.get_local_name(), spec.get_scmtype()))
else:
print("New element %s could not be added, " % spec)
return 1
# auto-install not a good feature, maybe make an option
# for element in config.get_config_elements():
# if element.get_local_name() == spec.get_local_name():
# if element.is_vcs_element():
# element.install(checkout=not os.path.exists(os.path.join(config.get_base_path(), spec.get_local_name())))
# break
return 0
def cmd_update(self, target_path, argv, config=None):
parser = OptionParser(usage="usage: %s update [localname]*" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["update"] + """
This command calls the SCM provider to pull changes from remote to
your local filesystem. In case the url has changed, the command will
ask whether to delete or backup the folder.
Examples:
$ %(progname)s update -t ~/fuerte
$ %(progname)s update robot_model geometry
""" % {'progname': self.progname},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
parser.add_option("--delete-changed-uris", dest="delete_changed",
default=False,
help="Delete the local copy of a directory before changing uri.",
action="store_true")
parser.add_option("--abort-changed-uris", dest="abort_changed",
default=False,
help="Abort if changed uri detected",
action="store_true")
parser.add_option("--continue-on-error", dest="robust",
default=False,
help="Continue despite checkout errors",
action="store_true")
parser.add_option("--backup-changed-uris", dest="backup_changed",
default='',
help="backup the local copy of a directory before changing uri to this directory.",
action="store")
parser.add_option("-m", "--timeout", dest="timeout",
default=None,
help="How long to wait for each repo before failing [seconds]",
action="store", type=float)
parser.add_option("-j", "--parallel", dest="jobs",
default=1,
help="How many parallel threads to use for installing",
action="store")
parser.add_option("-v", "--verbose", dest="verbose",
default=False,
help="Whether to print out more information",
action="store_true")
# -t option required here for help but used one layer above, see cli_common
parser.add_option("-t", "--target-workspace", dest="workspace",
default=None,
help="which workspace to use",
action="store")
(options, args) = parser.parse_args(argv)
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException("Config path does not match %s %s " % (
config.get_base_path(),
target_path))
success = True
mode = _get_mode_from_options(parser, options)
if args == []:
# None means no filter, [] means filter all
args = None
if success:
install_success = multiproject_cmd.cmd_install_or_update(
config,
localnames=args,
backup_path=options.backup_changed,
mode=mode,
robust=options.robust,
num_threads=int(options.jobs),
timeout=options.timeout,
verbose=options.verbose)
if install_success or options.robust:
return 0
return 1
def cmd_remove(self, target_path, argv, config=None):
parser = OptionParser(usage="usage: %s remove [localname]*" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["remove"] + """
The command removes entries from your configuration file, it does not affect your filesystem.
""",
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
# -t option required here for help but used one layer above, see cli_common
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
(_, args) = parser.parse_args(argv)
if len(args) < 1:
print("Error: Too few arguments.")
print(parser.usage)
return -1
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException(
"Config path does not match %s %s " % (config.get_base_path(),
target_path))
success = True
elements = select_elements(config, args)
for element in elements:
if not config.remove_element(element.get_local_name()):
success = False
print("Bug: No such element %s in config, aborting without changes" %
(element.get_local_name()))
break
if success:
print("Overwriting %s" % os.path.join(config.get_base_path(),
self.config_filename))
shutil.copy(os.path.join(config.get_base_path(),
self.config_filename),
"%s.bak" % os.path.join(config.get_base_path(),
self.config_filename))
self.config_generator(config, self.config_filename)
print("Removed entries %s" % args)
return 0
def cmd_snapshot(self, target_path, argv, config=None):
parser = OptionParser(
usage="usage: %s info [localname]* [OPTIONS]" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["export"] + """
Exports the current workspace.
The --exact option will cause the output to contain the exact commit uuid for
each version-controlled entry. The --spec option tells wstool to look at the
workspace .rosinstall for versioning info instead of the workspace.
Examples:
$ %(prog)s export
$ %(prog)s export -t ~/ros/fuerte
$ %(prog)s export --exact
""" % {'prog': self.progname, 'opts': ONLY_OPTION_VALID_ATTRS},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
parser.add_option(
"-o", "--output", dest="output_filename", default=None,
help="Write the .rosinstall export to the specified file",
action="store")
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
parser.add_option(
"--exact", dest="exact", default=False, action="store_true",
help="export exact commit hashes instead of branch names")
parser.add_option(
"--spec", dest="spec", default=False, action="store_true",
help="export version from workspace spec instead of current")
(options, _) = parser.parse_args(argv)
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException("Config path does not match %s %s " %
(config.get_base_path(), target_path))
# TODO: Check for workspace differences and issue warnings?
fname = options.output_filename
if fname:
fname = os.path.abspath(fname)
print("Writing %s" % fname)
self.config_generator(config, fname, get_header(self.progname),
spec=options.spec, exact=options.exact,
vcs_only=True)
return 0
def cmd_info(self, target_path, argv, reverse=True, config=None):
parser = OptionParser(
usage="usage: %s info [localname]* [OPTIONS]" % self.progname,
formatter=IndentedHelpFormatterWithNL(),
description=__MULTIPRO_CMD_DICT__["info"] + """
The Status (S) column shows
x for missing
L for uncommited (local) changes
V for difference in version and/or remote URI
C for difference in local and remote versions
The 'Version-Spec' column shows what tag, branch or revision was given
in the .rosinstall file. The 'UID' column shows the unique ID of the
current (and specified) version. The 'URI' column shows the configured
URL of the repo.
If status is V, the difference between what was specified and what is
real is shown in the respective column. For SVN entries, the url is
split up according to standard layout (trunk/tags/branches).
When given one localname, just show the data of one element in list form.
This also has the generic properties element which is usually empty.
The --only option accepts keywords: %(opts)s
Examples:
$ %(prog)s info -t ~/ros/fuerte
$ %(prog)s info robot_model
$ %(prog)s info --yaml
$ %(prog)s info --only=path,cur_uri,cur_revision robot_model geometry
""" % {'prog': self.progname, 'opts': ONLY_OPTION_VALID_ATTRS},
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
parser.add_option(
"--root", dest="show_ws_root", default=False,
help="Show workspace root path",
action="store_true")
parser.add_option(
"--data-only", dest="data_only", default=False,
help="Does not provide explanations",
action="store_true")
parser.add_option(
"-s", "--short", dest="short", default=False,
help="Shows simplified version info table.",
action="store_true")
parser.add_option(
"--only", dest="only", default=False,
help="Shows comma-separated lists of only given comma-separated attribute(s).",
action="store")
parser.add_option(
"--yaml", dest="yaml", default=False,
help="Shows only version of single entry. Intended for scripting.",
action="store_true")
parser.add_option(
"--fetch", dest="fetch", default=False,
help="When used, retrieves version information from remote (takes longer).",
action="store_true")
parser.add_option(
"-u", "--untracked", dest="untracked",
default=False,
help="Also show untracked files as modifications",
action="store_true")
# -t option required here for help but used one layer above, see cli_common
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
parser.add_option(
"-m", "--managed-only", dest="unmanaged", default=True,
help="only show managed elements",
action="store_false")
(options, args) = parser.parse_args(argv)
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException("Config path does not match %s %s " %
(config.get_base_path(), target_path))
if options.show_ws_root:
print(config.get_base_path())
return 0
if args == []:
args = None
if options.only:
only_options = options.only.split(",")
if only_options == '':
parser.error('No valid options given')
lines = get_info_table_raw_csv(config,
parser,
properties=only_options,
localnames=args)
print('\n'.join(lines))
return 0
elif options.yaml:
# TODO: Not sure what this does, used to be cmd_snapshot,
# but that command was not implemented.
source_aggregate = multiproject_cmd.cmd_snapshot(config,
localnames=args)
print(yaml.safe_dump(source_aggregate, default_flow_style=None), end='')
return 0
# this call takes long, as it invokes scms.
outputs = multiproject_cmd.cmd_info(config,
localnames=args,
untracked=options.untracked,
fetch=options.fetch)
if args and len(args) == 1:
# if only one element selected, print just one line
print(get_info_list(config.get_base_path(),
outputs[0],
options.data_only))
return 0
columns = None
if options.short:
columns = ['localname', 'status', 'version']
header = 'workspace: %s' % (target_path)
print(header)
table = get_info_table(config.get_base_path(),
outputs,
options.data_only,
reverse=reverse,
selected_headers=columns)
if table is not None and table != '':
print("\n%s" % table)
if options.unmanaged:
outputs2 = multiproject_cmd.cmd_find_unmanaged_repos(config)
table2 = get_info_table(config.get_base_path(),
outputs2,
options.data_only,
reverse=reverse,
unmanaged=True,
selected_headers=columns)
if table2 is not None and table2 != '':
print("\nAlso detected these repositories in the workspace, add using '%s scrape' or '%s set':\n\n%s" % (self.progname, self.progname, table2))
return 0
def cmd_scrape(self, target_path, argv, config=None):
"""
command for adding yet unamanaged repos under workspace root to managed repos.
:param target_path: where to look for config
:param config: config to use instead of parsing file anew
"""
usage = ("usage: %s scrape [OPTIONS]" % self.progname)
parser = OptionParser(
usage=usage,
description=__MULTIPRO_CMD_DICT__["scrape"],
epilog="See: http://www.ros.org/wiki/rosinstall for details\n")
parser.add_option("-y", "--confirm", dest="confirm", default='',
help="Do not ask for confirmation",
action="store_true")
# -t option required here for help but used one layer above, see cli_common
parser.add_option(
"-t", "--target-workspace", dest="workspace", default=None,
help="which workspace to use",
action="store")
(options, args) = parser.parse_args(argv)
if config is None:
config = multiproject_cmd.get_config(
target_path,
additional_uris=[],
config_filename=self.config_filename)
elif config.get_base_path() != target_path:
raise MultiProjectException(
"Config path does not match %s %s " % (config.get_base_path(),
target_path))
elems = multiproject_cmd.cmd_find_unmanaged_repos(config)
if not elems:
raise MultiProjectException(
"No unmanaged repos found below '%s'" % (config.get_base_path()))
for elem in elems:
elem_abs_path = os.path.join(config.get_base_path(), elem['localname'])
if os.path.isdir(elem_abs_path):
args = [elem_abs_path, elem['scm'], elem['uri']]
if (options.confirm):
args.append('-y')
self.cmd_set(target_path, args)
return 0
wstool-0.1.18/src/wstool/multiproject_cmd.py 0000664 0000000 0000000 00000056564 13542237257 0021216 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
"""
The _cmd python files attempt to provide a reasonably
complete level of abstraction to multiproject functionality.
Client code will need to pass the Config element through,
and may use the ConfigElement API in places.
There are no guarantees at this time for the API to
remain stable, but the cmd API probably will change least.
A change to expect is abstraction of user interaction.
"""
import sys
import os
import shlex
from wstool.common import MultiProjectException, DistributedWork, \
select_elements, normabspath
from wstool.config import Config, realpath_relation
from wstool.config_elements import AVCSConfigElement
from wstool.config_yaml import aggregate_from_uris, generate_config_yaml, \
get_path_specs_from_uri, PathSpec
import vcstools
import vcstools.__version__
from vcstools.common import run_shell_command
from vcstools.vcs_abstraction import get_vcs_client
from vcstools.git import GitClient
from vcstools.hg import HgClient
from vcstools.bzr import BzrClient
from vcstools.svn import SvnClient
def get_config(basepath,
additional_uris=None,
config_filename=None,
merge_strategy='KillAppend'):
"""
Create a Config element necessary for all other commands. The
command will look at the uris in sequence, each can be a web
resource, a filename or a folder. In case it is a folder, when a
config_filename is provided, the folder will be searched for a
file of that name, and that one will be used. Else the folder
will be considered a target location for the config. All files
will be parsed for config elements, thus conceptually the input to
Config is an expanded list of config elements. Config takes this
list and consolidates duplicate paths by keeping the last one in
the list.
:param basepath: where relative paths shall be resolved against
:param additional_uris: the location of config specifications or folders
:param config_filename: name of files which may be looked at for config information
:param merge_strategy: One of 'KillAppend, 'MergeKeep', 'MergeReplace'
:returns: a Config object
:raises MultiProjectException: on plenty of errors
"""
if basepath is None:
raise MultiProjectException("Need to provide a basepath for Config.")
# print("source...........................", path_specs)
## Generate the config class with the uri and path
if (config_filename is not None
and basepath is not None
and os.path.isfile(os.path.join(basepath, config_filename))):
base_path_specs = get_path_specs_from_uri(os.path.join(basepath,
config_filename),
as_is=True)
else:
base_path_specs = []
config = Config(base_path_specs, basepath,
config_filename=config_filename,
merge_strategy=merge_strategy)
add_uris(config=config,
additional_uris=additional_uris,
config_filename=config.get_config_filename(),
merge_strategy=merge_strategy)
return config
def add_uris(config,
additional_uris,
# config_filename is not redundant with config.get_config_filename()
# because in some cases a different config_filename is required
config_filename=None,
merge_strategy="KillAppend",
allow_other_element=True):
"""
changes the given config by merging with the additional_uris
:param config: a Config objects
:param additional_uris: the location of config specifications or folders
:param config_filename: name of files which may be looked at for config
information
:param merge_strategy: One of 'KillAppend, 'MergeKeep', 'MergeReplace'
:param allow_other_element: if False, discards elements to be added with
no SCM information
:returns: a dict {: (, ), : ...}
determined by the merge_strategy
:raises MultiProjectException: on plenty of errors
"""
if config is None:
raise MultiProjectException("Need to provide a Config.")
if not additional_uris:
return {}
if config_filename is None:
added_uris = additional_uris
else:
added_uris = []
# reject if the additional uri points to the same file as our
# config is based on
for uri in additional_uris:
# check whether we try to merge with other workspace
comp_uri = None
if (os.path.isfile(uri)
and os.path.basename(uri) == config_filename):
# add from other workspace by file
comp_uri = os.path.dirname(uri)
if (os.path.isdir(uri)
and os.path.isfile(os.path.join(uri, config_filename))):
# add from other workspace by dir
comp_uri = uri
if (comp_uri is not None and
realpath_relation(os.path.abspath(comp_uri),
os.path.abspath(config.get_base_path())) == 'SAME_AS'):
print('Warning: Discarding config basepath as additional uri: %s' % uri)
continue
added_uris.append(uri)
actions = {}
if len(added_uris) > 0:
path_specs = aggregate_from_uris(added_uris,
config_filename,
allow_other_element)
for path_spec in path_specs:
action = config.add_path_spec(path_spec, merge_strategy)
actions[path_spec.get_local_name()] = (action, path_spec)
return actions
def cmd_persist_config(config, filename, header=None,
pretty=False, sort_with_localname=False,
spec=True, exact=False, vcs_only=False):
"""writes config to given file in yaml syntax"""
generate_config_yaml(config, filename, header,
pretty, sort_with_localname,
spec, exact, vcs_only)
def cmd_version():
"""Returns extensive version information"""
def prettyversion(vdict):
version = vdict.pop("version")
return "%s; %s" % (version, ",".join(list(vdict.values())))
return """vcstools: %s
SVN: %s
Mercurial: %s
Git: %s
Tar: %s
Bzr: %s
""" % (vcstools.__version__.version,
prettyversion(vcstools.SvnClient.get_environment_metadata()),
prettyversion(vcstools.HgClient.get_environment_metadata()),
prettyversion(vcstools.GitClient.get_environment_metadata()),
prettyversion(vcstools.TarClient.get_environment_metadata()),
prettyversion(vcstools.BzrClient.get_environment_metadata()))
def cmd_status(config, localnames=None, untracked=False):
"""
calls SCM status for all SCM entries in config, relative to path
:returns: List of dict {element: ConfigElement, diff: diffstring}
:param untracked: also show files not added to the SCM
:raises MultiProjectException: on plenty of errors
"""
class StatusRetriever():
def __init__(self, element, path, untracked):
self.element = element
self.path = path
self.untracked = untracked
def do_work(self):
path_spec = self.element.get_path_spec()
scmtype = path_spec.get_scmtype()
status = self.element.get_status(self.path, self.untracked)
# align other scm output to svn
columns = -1
if scmtype == "git":
columns = 3
elif scmtype == "hg":
columns = 2
elif scmtype == "bzr":
columns = 4
if columns > -1 and status is not None:
status_aligned = ''
for line in status.splitlines():
status_aligned = "%s%s%s\n" % (status_aligned,
line[:columns].ljust(8),
line[columns:])
status = status_aligned
return {'status': status}
path = config.get_base_path()
# call SCM info in separate threads
elements = config.get_config_elements()
work = DistributedWork(capacity=len(elements), num_threads=-1)
elements = select_elements(config, localnames)
for element in elements:
if element.is_vcs_element():
work.add_thread(StatusRetriever(element, path, untracked))
outputs = work.run()
return outputs
def cmd_diff(config, localnames=None):
"""
calls SCM diff for all SCM entries in config, relative to path
:returns: List of dict {element: ConfigElement, diff: diffstring}
:raises MultiProjectException: on plenty of errors
"""
class DiffRetriever():
def __init__(self, element, path):
self.element = element
self.path = path
def do_work(self):
return {'diff': self.element.get_diff(self.path)}
path = config.get_base_path()
elements = config.get_config_elements()
work = DistributedWork(capacity=len(elements), num_threads=-1)
elements = select_elements(config, localnames)
for element in elements:
if element.is_vcs_element():
work.add_thread(DiffRetriever(element, path))
outputs = work.run()
return outputs
def cmd_foreach(
config,
command,
localnames=None,
num_threads=1,
timeout=None,
scm_types=None,
shell=False,
verbose=False):
"""Run command in all SCM entries in config, relative to path"""
class ForeachRetriever(object):
def __init__(self, element, command, timeout, shell, verbose):
self.element = element
self.command = command
self.timeout = timeout
self.shell = shell
self.verbose = verbose
def do_work(self):
command = self.command
if not self.shell:
command = shlex.split(command)
returncode, stdout, stderr = run_shell_command(
command,
cwd=self.element.path,
timeout=self.timeout,
shell=self.shell,
show_stdout=self.verbose)
return {'returncode': returncode,
'stdout': stdout,
'stderr': stderr}
elements = select_elements(config, localnames)
work = DistributedWork(capacity=len(elements),
num_threads=num_threads)
for element in elements:
if ((scm_types is not None) and
(element.get_vcs_type_name() not in scm_types)):
continue
work.add_thread(ForeachRetriever(element,
command,
timeout,
shell,
verbose))
outputs = work.run()
return outputs
def cmd_install_or_update(
config,
backup_path=None,
mode='abort',
robust=False,
localnames=None,
num_threads=1,
timeout=None,
verbose=False,
shallow=False):
"""
performs many things, generally attempting to make
the local filesystem look like what the config specifies,
pulling from remote sources the most recent changes.
The command may have stdin user interaction (TODO abstract)
:param backup_path: if and where to backup trees before deleting them
:param robust: proceed to next element even when one element fails
:returns: True on Success
:raises MultiProjectException: on plenty of errors
"""
success = True
if not os.path.exists(config.get_base_path()):
os.mkdir(config.get_base_path())
# Prepare install operation check filesystem and ask user
preparation_reports = []
elements = select_elements(config, localnames)
for tree_el in elements:
abs_backup_path = None
if backup_path is not None:
abs_backup_path = os.path.join(config.get_base_path(), backup_path)
try:
preparation_report = tree_el.prepare_install(
backup_path=abs_backup_path,
arg_mode=mode,
robust=robust)
if preparation_report is not None:
if preparation_report.abort:
raise MultiProjectException(
"Aborting install because of %s" % preparation_report.error)
if not preparation_report.skip:
preparation_reports.append(preparation_report)
else:
if preparation_report.error is not None:
print("Skipping install of %s because: %s" %
(preparation_report.config_element.get_local_name(),
preparation_report.error))
except MultiProjectException as exc:
fail_str = ("Failed to install tree '%s'\n %s" %
(tree_el.get_path(), exc))
if robust:
success = False
print("Continuing despite %s" % fail_str)
else:
raise MultiProjectException(fail_str)
class Installer():
def __init__(self, report):
self.element = report.config_element
self.report = report
def do_work(self):
self.element.install(checkout=self.report.checkout,
backup=self.report.backup,
backup_path=self.report.backup_path,
inplace=self.report.inplace,
timeout=self.report.timeout,
verbose=self.report.verbose,
shallow=self.report.shallow)
return {}
work = DistributedWork(capacity=len(preparation_reports),
num_threads=num_threads,
silent=False)
for report in preparation_reports:
report.verbose = verbose
report.timeout = timeout
report.shallow = shallow
thread = Installer(report)
work.add_thread(thread)
try:
work.run()
except MultiProjectException as exc:
print ("Exception caught during install: %s" % exc)
success = False
if not robust:
raise
return success
# TODO go back and make sure that everything in options.path is
# described in the yaml, and offer to delete otherwise? not sure,
# but it could go here
def cmd_snapshot(config, localnames=None):
elements = select_elements(config, localnames)
source_aggregate = []
for element in elements:
if element.is_vcs_element():
spec = element.get_versioned_path_spec()
export_spec = PathSpec(
local_name=spec.get_local_name(),
scmtype=spec.get_scmtype(),
uri=spec.get_uri() or spec.get_curr_uri(),
version=(spec.get_current_revision() or
spec.get_revision() or
spec.get_version()),
path=spec.get_path())
if not export_spec.get_version():
sys.stderr.write(
'Warning, discarding non-vcs element %s\n' % element.get_local_name())
source = export_spec.get_legacy_yaml()
source_aggregate.append(source)
else:
sys.stderr.write('Warning, discarding non-vcs element %s\n' %
element.get_local_name())
return source_aggregate
def cmd_info(config, localnames=None, untracked=False, fetch=False):
"""This function compares what should be (config_file) with what is
(directories) and returns a list of dictionary giving each local
path and all the state information about it available.
"""
class InfoRetriever():
"""
Auxilliary class to perform IO-bound operations in individual threads
"""
def __init__(self, element, path, untracked, fetch):
self.element = element
self.path = path
self.fetch = fetch
self.untracked = untracked
def do_work(self):
localname = ""
scm = None
uri = ""
curr_uri = None
exists = False
version = "" # what is given in config file
curr_version_label = "" # e.g. branchname
remote_revision = "" # UID on remote
default_remote_label = None # git default branch
display_version = ''
modified = ""
currevision = "" # revision number of version
specversion = "" # actual revision number
localname = self.element.get_local_name()
path = self.element.get_path() or localname
if localname is None or localname == "":
raise MultiProjectException(
"Missing local-name in element: %s" % self.element)
if (os.path.exists(normabspath(path, self.path))):
exists = True
if self.element.is_vcs_element():
if not exists:
path_spec = self.element.get_path_spec()
version = path_spec.get_version()
else:
path_spec = self.element.get_versioned_path_spec(fetch=fetch)
version = path_spec.get_version()
remote_revision = path_spec.get_remote_revision()
curr_version_label = path_spec.get_curr_version()
if (curr_version_label is not None and
version != curr_version_label):
display_version = curr_version_label
else:
display_version = version
curr_uri = path_spec.get_curr_uri()
status = self.element.get_status(self.path, self.untracked)
if (status is not None and
status.strip() != ''):
modified = True
specversion = path_spec.get_revision()
if (version is not None and
version.strip() != '' and
(specversion is None or specversion.strip() == '')):
specversion = '"%s"' % version
if (self.fetch and specversion == None and
path_spec.get_scmtype() == 'git'):
default_remote_label = self.element.get_default_remote_label()
currevision = path_spec.get_current_revision()
scm = path_spec.get_scmtype()
uri = path_spec.get_uri()
return {'scm': scm,
'exists': exists,
'localname': localname,
'path': path,
'uri': uri,
'curr_uri': curr_uri,
'version': version,
'remote_revision': remote_revision,
'default_remote_label': default_remote_label,
'curr_version_label': curr_version_label,
'specversion': specversion,
'actualversion': currevision,
'modified': modified,
'properties': self.element.get_properties()}
path = config.get_base_path()
# call SCM info in separate threads
elements = config.get_config_elements()
elements = select_elements(config, localnames)
work = DistributedWork(capacity=len(elements), num_threads=-1)
for element in elements:
if element.get_properties() is None or not 'setup-file' in element.get_properties():
work.add_thread(InfoRetriever(element, path, untracked, fetch))
outputs = work.run()
return outputs
def cmd_find_unmanaged_repos(config):
"""
Auxilliary function to find SCM folders within workspace that have not been tracked. This
allows quicker diagnosis of the general state in a workspace, where folders can be part
of the build even when they are not mentioned in the .rosinstall file.
Nested SCMs are not investigated.
"""
class UnmanagedInfoRetriever():
def __init__(self, path, localname, scm_type):
self.path = path
self.localname = localname
self.scm_type = scm_type
self.element = AVCSConfigElement(scm_type, os.path.join(self.path, self.localname), localname, '')
def do_work(self):
vcsc = get_vcs_client(self.scm_type, os.path.join(self.path, self.localname))
return {'scm': '--' + self.scm_type, # prefix '--' to allow copy&paste to set command
'localname': self.localname,
'path': self.path,
'uri': vcsc.get_url(),
'properties': self.element.get_properties()}
path = config.get_base_path()
# call SCM info in separate threads
elements = config.get_config_elements()
managed_paths = [os.path.join(path, e.get_local_name()) for e in elements]
unmanaged_paths = []
scm_clients = {SvnClient: 'svn', GitClient: 'git', BzrClient:'bzr', HgClient:'hg'}
for root, dirs, files in os.walk(path):
if root in managed_paths:
# remove it from the walk if it's managed
del dirs[:]
else:
for client, key in scm_clients.items():
# check if it's a vcs dir
if client.static_detect_presence(root):
# add it to the unmanaged list
unmanaged_paths.append((os.path.relpath(root, path), key))
# don't walk any other directories in this root
del dirs[:]
work = DistributedWork(capacity=len(unmanaged_paths), num_threads=-1)
for localname, scm_type in sorted(unmanaged_paths, key=lambda up: up[0], reverse=True):
work.add_thread(UnmanagedInfoRetriever(path, localname, scm_type))
outputs = work.run()
return outputs
wstool-0.1.18/src/wstool/ui.py 0000664 0000000 0000000 00000007512 13542237257 0016254 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import sys
class Ui(object):
"""
wrap user interaction, such that client libraries may provide own
implementation
"""
# For now, primarily define this for replacement in unittests
GLOBAL_UI = None
@staticmethod
def get_ui():
if Ui.GLOBAL_UI is None:
return Ui()
return Ui.GLOBAL_UI
@staticmethod
def set_ui(uiarg):
Ui.GLOBAL_UI = uiarg
def __init__(self):
pass
def get_backup_path(self):
"""Interactive function asking the user to choose a path for backup"""
backup_path = self.get_input("Please enter backup pathname: ")
print(("backing up to %s" % backup_path))
return backup_path
def get_input(self, prompt):
if sys.hexversion > 0x03000000:
return input(prompt)
else:
return raw_input(prompt)
def prompt_del_abort_retry(self,
prompt,
allow_skip=False,
allow_inplace=False):
"""
Interactive function asking the user to choose a conflict resolution
:param prompt: message to display, str
:param allow_skip: whether to display skip option, bool
:param inplace: whether to show option for inplace replacing (symlinks)
:return: user choice one of backup, delete, abort, inplace, skip
"""
valid_modes = ['(d)elete and replace',
'(a)bort']
if allow_inplace:
valid_modes.append('(i)nplace delete and replace at symlink')
else:
valid_modes.append('(b)ackup and replace')
if allow_skip:
valid_modes.append('(s)kip')
mode = ""
full_prompt = "%s\n %s: " % (prompt, ", ".join(valid_modes))
while mode == "":
mode_input = self.get_input(full_prompt)
if not allow_inplace and mode_input == 'b':
mode = 'backup'
elif mode_input == 'd':
mode = 'delete'
elif mode_input == 'a':
mode = 'abort'
elif allow_inplace and mode_input == 'i':
mode = 'inplace'
elif allow_skip and mode_input == 's':
mode = 'skip'
return mode
wstool-0.1.18/src/wstool/wstool_cli.py 0000664 0000000 0000000 00000015122 13542237257 0020011 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2010, 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 Willow Garage, Inc. 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.
"""%(prog)s is a command to manipulate ROS workspaces. %(prog)s replaces its predecessor rosws.
Official usage:
%(prog)s CMD [ARGS] [OPTIONS]
%(prog)s will try to infer install path from context
Type '%(prog)s help' for usage.
"""
from __future__ import print_function
import os
import sys
from optparse import OptionParser
from wstool.cli_common import get_info_table, get_workspace
import wstool.multiproject_cmd as multiproject_cmd
import wstool.__version__
from wstool.helpers import ROSINSTALL_FILENAME
from wstool.common import MultiProjectException
from wstool.multiproject_cli import MultiprojectCLI, \
__MULTIPRO_CMD_DICT__, __MULTIPRO_CMD_ALIASES__, \
__MULTIPRO_CMD_HELP_LIST__, IndentedHelpFormatterWithNL, \
list_usage, get_header
## This file adds or extends commands from multiproject_cli where ROS
## specific output has to be generated.
_PROGNAME = 'wstool'
class WstoolCLI(MultiprojectCLI):
def __init__(self, config_filename=ROSINSTALL_FILENAME, progname=_PROGNAME):
def config_generator(config, filename, header=None, spec=True,
exact=False, vcs_only=False):
wstool.multiproject_cmd.cmd_persist_config(
config,
filename,
header,
pretty=True,
sort_with_localname=True,
spec=spec,
exact=exact,
vcs_only=vcs_only)
MultiprojectCLI.__init__(
self,
progname=progname,
config_filename=config_filename,
config_generator=config_generator)
def wstool_main(argv=None, usage=None):
"""
Calls the function corresponding to the first argument.
:param argv: sys.argv by default
:param usage: function printing usage string, multiproject_cli.list_usage by default
"""
if argv is None:
argv = sys.argv
if (sys.argv[0] == '-c'):
sys.argv = [_PROGNAME] + sys.argv[1:]
if '--version' in argv:
print("%s: \t%s\n%s" % (_PROGNAME,
wstool.__version__.version,
multiproject_cmd.cmd_version()))
sys.exit(0)
if not usage:
usage = lambda: print(list_usage(progname=_PROGNAME,
description=__doc__,
command_keys=__MULTIPRO_CMD_HELP_LIST__,
command_helps=__MULTIPRO_CMD_DICT__,
command_aliases=__MULTIPRO_CMD_ALIASES__))
workspace = None
if len(argv) < 2:
try:
workspace = get_workspace(argv,
os.getcwd(),
config_filename=ROSINSTALL_FILENAME)
argv.append('info')
except MultiProjectException as e:
print(str(e))
usage()
return 0
if argv[1] in ['--help', '-h']:
usage()
return 0
try:
command = argv[1]
args = argv[2:]
if command == 'help':
if len(argv) < 3:
usage()
return 0
else:
command = argv[2]
args = argv[3:]
args.insert(0, "--help")
# help help
if command == 'help':
usage()
return 0
cli = WstoolCLI(progname=_PROGNAME)
# commands for which we do not infer target workspace
commands = {'init': cli.cmd_init}
# commands which work on a workspace
ws_commands = {
'info': cli.cmd_info,
'remove': cli.cmd_remove,
'set': cli.cmd_set,
'merge': cli.cmd_merge,
'export': cli.cmd_snapshot,
'diff': cli.cmd_diff,
'foreach': cli.cmd_foreach,
'scrape': cli.cmd_scrape,
'status': cli.cmd_status,
'update': cli.cmd_update}
for label in list(ws_commands.keys()):
if label in __MULTIPRO_CMD_ALIASES__:
ws_commands[__MULTIPRO_CMD_ALIASES__[label]] = ws_commands[label]
if command not in commands and command not in ws_commands:
if os.path.exists(command):
args = ['-t', command] + args
command = 'info'
else:
if command.startswith('-'):
print("First argument must be name of a command: %s" % command)
else:
print("Error: unknown command: %s" % command)
usage()
return 1
if command in commands:
return commands[command](args)
else:
if workspace is None and not '--help' in args and not '-h' in args:
workspace = get_workspace(args,
os.getcwd(),
config_filename=ROSINSTALL_FILENAME)
return ws_commands[command](workspace, args)
except KeyboardInterrupt:
return 1
wstool-0.1.18/stdeb.cfg 0000664 0000000 0000000 00000000620 13542237257 0014722 0 ustar 00root root 0000000 0000000 [DEFAULT]
Depends: subversion, mercurial, git-core, bzr, python-yaml, python-vcstools (>= 0.1.38)
Depends3: subversion, mercurial, git-core, bzr, python3-yaml, python3-vcstools (>= 0.1.38)
Conflicts: python3-wstool
Conflicts3: python-wstool
Suite: oneiric precise quantal raring saucy trusty utopic vivid wily xenial yakkety zesty artful bionic wheezy jessie stretch buster
X-Python3-Version: >= 3.2
wstool-0.1.18/test/ 0000775 0000000 0000000 00000000000 13542237257 0014121 5 ustar 00root root 0000000 0000000 wstool-0.1.18/test/__init__.py 0000664 0000000 0000000 00000000000 13542237257 0016220 0 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example.yaml 0000664 0000000 0000000 00000000027 13542237257 0016437 0 ustar 00root root 0000000 0000000 text: foobar
number: 2
wstool-0.1.18/test/example_dirs/ 0000775 0000000 0000000 00000000000 13542237257 0016575 5 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example_dirs/ros/ 0000775 0000000 0000000 00000000000 13542237257 0017400 5 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example_dirs/ros/stack.xml 0000664 0000000 0000000 00000000000 13542237257 0021215 0 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example_dirs/ros_comm/ 0000775 0000000 0000000 00000000000 13542237257 0020413 5 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example_dirs/ros_comm/stack.xml 0000664 0000000 0000000 00000000000 13542237257 0022230 0 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example_dirs/roscpp/ 0000775 0000000 0000000 00000000000 13542237257 0020103 5 ustar 00root root 0000000 0000000 wstool-0.1.18/test/example_dirs/roscpp/manifest.xml 0000664 0000000 0000000 00000000000 13542237257 0022421 0 ustar 00root root 0000000 0000000 wstool-0.1.18/test/io_wrapper.py 0000664 0000000 0000000 00000000606 13542237257 0016644 0 ustar 00root root 0000000 0000000 class StringIO:
"""
StringIO.StringIO does not exist in python3
io.StringIO cannot cope with unicode
"""
def __init__(self):
self.stream = ''
def write(self, data):
self.stream += data
def flush(self):
pass
def __getattr__(self, attr):
return getattr(self.stream, attr)
def getvalue(self):
return self.stream
wstool-0.1.18/test/local/ 0000775 0000000 0000000 00000000000 13542237257 0015213 5 ustar 00root root 0000000 0000000 wstool-0.1.18/test/local/__init__.py 0000664 0000000 0000000 00000000000 13542237257 0017312 0 ustar 00root root 0000000 0000000 wstool-0.1.18/test/local/mock_client.py 0000664 0000000 0000000 00000007346 13542237257 0020066 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
class MockVcsClient():
"""
Mocked vcs client. TODO: we should be using pathon magic mock instead.
"""
class MockVcsClient():
def __init__(self,
scmtype='mocktype',
path_exists=False,
checkout_success=True,
update_success=True,
vcs_presence=False,
url="mockurl",
actualversion=None,
specversion=None,
remoteversion=None):
self.scmtype = scmtype
self.path_exists_flag = path_exists
self.checkout_success = checkout_success
self.update_success = update_success
self.vcs_presence = vcs_presence
self.mockurl = url
self.checkedout = vcs_presence
self.updated = False
self.actualversion = actualversion
self.specversion = specversion
self.remoteversion = remoteversion
def get_vcs_type_name(self):
return self.scmtype
def get_diff(self, basepath=None):
return self.scmtype + "mockdiff%s" % basepath
def get_version(self, revision=None):
if revision == None:
return self.actualversion
else:
return self.specversion
def get_remote_version(self, fetch=False):
return self.remoteversion
def get_current_version_label(self):
return self.scmtype + "mockcurrentversionlabel"
def get_status(self, basepath=None, untracked=False):
return self.scmtype + " mockstatus%s,%s" % (basepath, untracked)
def path_exists(self):
return self.path_exists_flag
def checkout(self, uri=None, version=None, verbose=False, timeout=None, shallow=False):
self.checkedout = True
return self.checkout_success
def update(self, version, verbose=False, timeout=None):
self.updated = True
return self.update_success
def detect_presence(self):
return self.vcs_presence
def get_url(self):
return self.mockurl
def url_matches(self, url, url_or_shortcut):
return (url == url_or_shortcut or
url_or_shortcut is None or
url_or_shortcut.endswith('_shortcut'))
wstool-0.1.18/test/local/test_cli.py 0000664 0000000 0000000 00000131647 13542237257 0017407 0 ustar 00root root 0000000 0000000 #!/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 Willow Garage, Inc. 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.
import os
import sys
import copy
import tempfile
import unittest
import shutil
import subprocess
from mock import Mock
import wstool.cli_common
import wstool.multiproject_cmd
import wstool.multiproject_cli
from wstool.multiproject_cli import MultiprojectCLI, _get_element_diff
import wstool.config
from wstool.common import MultiProjectException
from wstool.config import MultiProjectException, Config
from wstool.config_yaml import PathSpec
from test.scm_test_base import AbstractFakeRosBasedTest, _add_to_file, \
_nth_line_split, _create_yaml_file, _create_config_elt_dict
from test.io_wrapper import StringIO
from . import mock_client
class MockConfigElement():
def __init__(self, local_name='', scmtype=None, path=None, uri=None, spec=None):
self.scmtype = scmtype
self.path = path
self.uri = uri
self.local_name = local_name
self.spec = spec
def get_path(self):
return self.path
def get_local_name(self):
return self.local_name
def get_path_spec(self):
return self.spec
def is_vcs_element(self):
return True if self.scmtype else False
class GetVersionTest(unittest.TestCase):
def test_version(self):
self.assertFalse(None == wstool.multiproject_cmd.cmd_version())
class GetWorkspaceTest(unittest.TestCase):
@classmethod
def setUpClass(self):
self.environback = copy.copy(os.environ)
self.new_environ = os.environ
self.test_root_path = os.path.realpath(tempfile.mkdtemp())
self.install_path = os.path.join(self.test_root_path, "install")
os.makedirs(self.install_path)
self.install_path2 = os.path.join(self.test_root_path, "install2")
os.makedirs(self.install_path2)
_add_to_file(os.path.join(self.install_path, "configfile"), 'content')
path = self.install_path
for i in range(4):
path = os.path.join(path, "path%s" % i)
os.makedirs(path)
@classmethod
def tearDownClass(self):
shutil.rmtree(self.test_root_path)
os.environ.update(self.environback)
def test_option_arg(self):
argv = []
try:
self.assertEqual(None, wstool.cli_common.get_workspace(argv, self.test_root_path))
self.fail("expected Exception")
except MultiProjectException:
pass
argv = ["."]
try:
self.assertEqual(None, wstool.cli_common.get_workspace(argv, self.test_root_path))
self.fail("expected Exception")
except MultiProjectException:
pass
abspath = os.path.abspath('good')
argv = ['bad', '-a', "foo", '-t', 'good', '-b', 'bar', '--bad']
self.assertEqual(abspath, wstool.cli_common.get_workspace(argv, self.test_root_path))
argv = ['bad', '-a', "foo", '--target-workspace=good', '-b', 'bar', '--bad']
self.assertEqual(abspath, wstool.cli_common.get_workspace(argv, self.test_root_path))
argv = ['bad', '-a', "foo", '--target-workspace', 'good', '-b', 'bar', '--bad']
self.assertEqual(abspath, wstool.cli_common.get_workspace(argv, self.test_root_path))
argv = ['bad', '-a', "foo", '-tgood', '-b', 'bar', '--bad']
self.assertEqual(abspath, wstool.cli_common.get_workspace(argv, self.test_root_path))
# not supported by OptionParser
# argv = ['bad', '-a', "foo", '-t=good', '-b', 'bar', '--bad']
# self.assertEqual(abspath, wstool.cli_common.get_workspace(argv, self.test_root_path))
argv = ['bad', '-a', "foo", '-t', 'good', '-b', 'bar', '--bad']
self.assertEqual(abspath, wstool.cli_common.get_workspace(argv, self.test_root_path))
def test_option_env(self):
self.new_environ["VARNAME"] = ""
self.new_environ.pop("VARNAME")
argv = []
try:
self.assertEqual(None, wstool.cli_common.get_workspace(argv, self.test_root_path, varname='VARNAME'))
self.fail("expected Exception")
except MultiProjectException:
pass
self.new_environ["VARNAME"] = ''
argv = []
try:
self.assertEqual(None, wstool.cli_common.get_workspace(argv, self.test_root_path, varname='VARNAME'))
self.fail("expected Exception")
except MultiProjectException:
pass
self.new_environ["VARNAME"] = self.install_path2
argv = []
self.assertEqual(self.install_path2, wstool.cli_common.get_workspace(argv, self.test_root_path, varname='VARNAME'))
def test_option_path(self):
path = self.install_path
self.new_environ["VARNAME"] = self.install_path2
for i in range(4):
path = os.path.join(path, "path%s"%i)
argv = []
self.assertEqual(self.install_path, wstool.cli_common.get_workspace(argv, path, config_filename="configfile"))
try:
self.assertEqual(self.install_path, wstool.cli_common.get_workspace(argv, path, config_filename="configfile", varname='VARNAME'))
self.fail("expected Exception")
except MultiProjectException:
pass
class FunctionsTest(unittest.TestCase):
def test_get_mode(self):
class FakeOpts:
def __init__(self, dele, ab, back):
self.delete_changed = dele
self.backup_changed = back
self.abort_changed = ab
class FakeErrors:
def __init__(self):
self.rerror = None
def error(self, foo):
self.rerror = foo
opts = FakeOpts(dele=False, ab=False, back='')
ferr = FakeErrors()
self.assertEqual("prompt", wstool.multiproject_cli._get_mode_from_options(ferr, opts))
self.assertEqual(None, ferr.rerror)
opts = FakeOpts(dele=True, ab=False, back='')
ferr = FakeErrors()
self.assertEqual("delete", wstool.multiproject_cli._get_mode_from_options(ferr, opts))
self.assertEqual(None, ferr.rerror)
opts = FakeOpts(dele=False, ab=True, back='')
ferr = FakeErrors()
self.assertEqual("abort", wstool.multiproject_cli._get_mode_from_options(ferr, opts))
self.assertEqual(None, ferr.rerror)
opts = FakeOpts(dele=False, ab=False, back='Foo')
ferr = FakeErrors()
self.assertEqual("backup", wstool.multiproject_cli._get_mode_from_options(ferr, opts))
self.assertEqual(None, ferr.rerror)
opts = FakeOpts(dele=True, ab=True, back='')
ferr = FakeErrors()
wstool.multiproject_cli._get_mode_from_options(ferr, opts)
self.assertFalse(None is ferr.rerror)
opts = FakeOpts(dele=False, ab=True, back='Foo')
ferr = FakeErrors()
wstool.multiproject_cli._get_mode_from_options(ferr, opts)
self.assertFalse(None is ferr.rerror)
opts = FakeOpts(dele=True, ab=False, back='Foo')
ferr = FakeErrors()
wstool.multiproject_cli._get_mode_from_options(ferr, opts)
self.assertFalse(None is ferr.rerror)
def test_list_usage(self):
#test function exists and does not fail
usage = wstool.multiproject_cli.list_usage('foo', 'bardesc %(prog)s', ['cmd1', None, 'cmd2'], {'cmd1': 'help1', 'cmd2': 'help2'}, {'cmd1': 'cmd1a'})
tokens = [y.strip() for x in usage.split(' ') for y in x.splitlines()]
self.assertEqual(['bardesc', 'foo', 'cmd1', '(cmd1a)', 'help1', '', 'cmd2', 'help2'], tokens)
class FakeConfig():
def __init__(self, celts=[], elts=[], path=''):
self.elts = elts
self.celts = celts
self.path = path
def get_config_elements(self):
return self.celts
def get_source(self):
return self.elts
def get_base_path(self):
return self.path
class MockVcsConfigElement(wstool.config_elements.VCSConfigElement):
def __init__(self, scmtype, path, local_name, uri, version='',
actualversion='', specversion='', properties=None):
self.scmtype = scmtype
self.path = path
self.local_name = local_name
self.vcsc = mock_client.MockVcsClient(
scmtype, actualversion=actualversion, specversion=specversion)
self.uri = uri
self.version = version
self.install_success = True
self.properties = properties
def install(self, checkout=True, backup=False, backup_path=None,
robust=False, verbose=False, inplace=False, timeout=None, shallow=False):
if not self.install_success:
raise MultiProjectException("Unittest Mock says install failed")
def _get_vcsc(self):
return self.vcsc
class InstallTest(unittest.TestCase):
def test_mock_install(self):
test_root = os.path.realpath(tempfile.mkdtemp())
try:
git1 = PathSpec('foo', 'git', 'git/uri', 'git.version')
svn1 = PathSpec('foos', 'svn', 'svn/uri', '12345')
hg1 = PathSpec('fooh', 'hg', 'hg/uri', 'hg.version')
bzr1 = PathSpec('foob', 'bzr', 'bzr/uri', 'bzr.version')
config = Config([git1, svn1, hg1, bzr1],
test_root,
None,
{"svn": MockVcsConfigElement,
"git": MockVcsConfigElement,
"hg": MockVcsConfigElement,
"bzr": MockVcsConfigElement})
wstool.multiproject_cmd.cmd_install_or_update(config)
wstool.multiproject_cmd.cmd_install_or_update(config)
wstool.multiproject_cmd.cmd_install_or_update(config, num_threads=10)
wstool.multiproject_cmd.cmd_install_or_update(config, num_threads=10)
wstool.multiproject_cmd.cmd_install_or_update(config, num_threads=1)
wstool.multiproject_cmd.cmd_install_or_update(config, num_threads=1)
finally:
shutil.rmtree(test_root)
def test_mock_install_fail(self):
test_root = os.path.realpath(tempfile.mkdtemp())
try:
# robust
git1 = PathSpec('foo', 'git', 'git/uri', 'git.version')
svn1 = PathSpec('foos', 'svn', 'svn/uri', '12345')
hg1 = PathSpec('fooh', 'hg', 'hg/uri', 'hg.version')
bzr1 = PathSpec('foob', 'bzr', 'bzr/uri', 'bzr.version')
config = Config([git1, svn1, hg1, bzr1],
install_path=test_root,
config_filename=None,
extended_types={"svn": MockVcsConfigElement,
"git": MockVcsConfigElement,
"hg": MockVcsConfigElement,
"bzr": MockVcsConfigElement})
config.get_config_elements()[1].install_success = False
wstool.multiproject_cmd.cmd_install_or_update(
config, robust=True)
try:
wstool.multiproject_cmd.cmd_install_or_update(
config, robust=False)
self.fail("expected Exception")
except MultiProjectException:
pass
finally:
shutil.rmtree(test_root)
class GetStatusDiffInfoCmdTest(unittest.TestCase):
def test_status(self):
self.mock_config = FakeConfig()
result = wstool.multiproject_cmd.cmd_status(self.mock_config)
self.assertEqual(len(result), 0)
self.mock_config = FakeConfig(
[MockVcsConfigElement('git', 'gitpath', 'gitname', None)])
result = wstool.multiproject_cmd.cmd_status(self.mock_config)
self.assertEqual(len(result), 1)
self.assertTrue(result[0]['status'] is not None)
self.assertTrue(result[0]['entry'] is not None)
self.mock_config = FakeConfig(
[MockVcsConfigElement('git', 'gitpath', 'gitname', None),
MockVcsConfigElement(
'svn', 'svnpath', 'svnname', None),
MockVcsConfigElement(
'hg', 'hgpath', 'hgname', None),
MockVcsConfigElement('bzr', 'bzrpath', 'bzrname', None)])
result = wstool.multiproject_cmd.cmd_status(self.mock_config)
self.assertEqual(len(result), 4)
self.assertEqual(result[0]['status'].count('git'), 1)
self.assertEqual(result[1]['status'].count('svn'), 1)
self.assertEqual(result[2]['status'].count('hg'), 1)
self.assertEqual(result[3]['status'].count('bzr'), 1)
def test_diff(self):
self.mock_config = FakeConfig()
result = wstool.multiproject_cmd.cmd_diff(self.mock_config)
self.assertEqual(len(result), 0)
self.mock_config = FakeConfig(
[MockVcsConfigElement('git', 'gitpath', 'gitname', None)])
result = wstool.multiproject_cmd.cmd_diff(self.mock_config)
self.assertEqual(1, len(result))
self.assertTrue(result[0]['diff'] is not None)
self.assertTrue(result[0]['entry'] is not None)
self.mock_config = FakeConfig(
[MockVcsConfigElement('git', 'gitpath', 'gitname', None),
MockVcsConfigElement(
'svn', 'svnpath', 'svnname', None),
MockVcsConfigElement(
'hg', 'hgpath', 'hgname', None),
MockVcsConfigElement('bzr', 'bzrpath', 'bzrname', None)])
result = wstool.multiproject_cmd.cmd_diff(self.mock_config)
self.assertEqual(len(result), 4)
self.assertEqual(result[0]['diff'].count('git'), 1)
self.assertEqual(result[1]['diff'].count('svn'), 1)
self.assertEqual(result[2]['diff'].count('hg'), 1)
self.assertEqual(result[3]['diff'].count('bzr'), 1)
def test_info(self):
self.mock_config = FakeConfig([], [], 'foopath')
result = wstool.multiproject_cmd.cmd_info(self.mock_config)
self.assertEqual(len(result), 0)
self.mock_config = FakeConfig(
[MockVcsConfigElement(
'git', 'gitpath', 'gitname', None, version='version')],
[],
'foopath')
result = wstool.multiproject_cmd.cmd_info(self.mock_config)
self.assertEqual(len(result), 1)
self.assertEqual(result[0]['scm'], 'git')
self.assertEqual(result[0]['version'], 'version')
self.mock_config = FakeConfig(
[MockVcsConfigElement('git', 'gitpath', 'gitname', None),
MockVcsConfigElement(
'svn', 'svnpath', 'svnname', None),
MockVcsConfigElement(
'hg', 'hgpath', 'hgname', None),
MockVcsConfigElement(
'bzr', 'bzrpath', 'bzrname', None)],
[],
'foopath')
result = wstool.multiproject_cmd.cmd_info(self.mock_config)
self.assertEqual(len(result), 4)
self.assertEqual(result[0]['scm'], 'git')
self.assertEqual(result[1]['scm'], 'svn')
self.assertEqual(result[2]['scm'], 'hg')
self.assertEqual(result[3]['scm'], 'bzr')
def test_unmanaged(self):
root_path = os.path.realpath(tempfile.mkdtemp())
ws_path = os.path.join(root_path, "ws")
os.makedirs(ws_path)
self.mock_config = FakeConfig([], [], ws_path)
# empty folder
result = wstool.multiproject_cmd.cmd_find_unmanaged_repos(self.mock_config)
self.assertEqual(len(result), 0)
# subfolders no vcs
gitrepo_path = os.path.join(ws_path, "gitrepo")
os.makedirs(gitrepo_path)
svnrepo_path = os.path.join(ws_path, "svnrepo")
os.makedirs(svnrepo_path)
bzrrepo_path = os.path.join(ws_path, "bzrrepo")
os.makedirs(bzrrepo_path)
hgrepo_path = os.path.join(ws_path, "sub/hgrepo")
os.makedirs(hgrepo_path)
result = wstool.multiproject_cmd.cmd_find_unmanaged_repos(self.mock_config)
self.assertEqual(len(result), 0)
# vcs folders
os.makedirs(os.path.join(gitrepo_path, ".git"))
os.makedirs(os.path.join(hgrepo_path, ".hg"))
os.makedirs(os.path.join(svnrepo_path, ".svn"))
os.makedirs(os.path.join(bzrrepo_path, ".bzr"))
result = wstool.multiproject_cmd.cmd_find_unmanaged_repos(self.mock_config)
self.assertEqual(len(result), 4)
# vcs folders covered
mock = MockVcsConfigElement('git',
gitrepo_path,
'gitrepo',
None,
version='version',
actualversion='actual',
specversion='spec')
self.mock_config = FakeConfig([mock], [], ws_path)
# empty folder
result = wstool.multiproject_cmd.cmd_find_unmanaged_repos(self.mock_config)
self.assertEqual(len(result), 3)
def test_info_real_path(self):
root_path = os.path.realpath(tempfile.mkdtemp())
el_path = os.path.join(root_path, "ros")
os.makedirs(el_path)
try:
self.mock_config = FakeConfig([], [], 'foopath')
result = wstool.multiproject_cmd.cmd_info(self.mock_config)
self.assertEqual(len(result), 0)
mock = MockVcsConfigElement('git',
el_path,
'gitname',
None,
version='version',
actualversion='actual',
specversion='spec')
self.mock_config = FakeConfig([mock], [], 'foopath')
result = wstool.multiproject_cmd.cmd_info(self.mock_config)
self.assertEqual(len(result), 1)
self.assertEqual(result[0]['scm'], 'git')
self.assertEqual(result[0]['version'], 'version')
self.assertEqual(result[0]['specversion'], 'spec')
self.assertEqual(result[0]['actualversion'], 'actual')
mock = MockVcsConfigElement('git',
el_path,
'gitname',
None,
version='version',
actualversion='actual',
specversion=None) # means scm does not know version
self.mock_config = FakeConfig([mock], [], 'foopath')
result = wstool.multiproject_cmd.cmd_info(self.mock_config)
self.assertEqual(len(result), 1)
self.assertEqual(result[0]['scm'], 'git')
self.assertEqual(result[0]['version'], 'version')
self.assertEqual(result[0]['specversion'], '"version"')
self.assertEqual(result[0]['actualversion'], 'actual')
finally:
shutil.rmtree(root_path)
def test_get_status(self):
self.test_root_path = os.path.realpath(tempfile.mkdtemp())
try:
basepath = '/foo/path'
entry = {}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'exists': False}
self.assertEqual('x', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'exists': False, 'modified': True}
self.assertEqual('x', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'exists': True, 'modified': True}
self.assertEqual('M', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'modified': True}
self.assertEqual('M', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'actualversion': 'foo', 'specversion': 'bar'}
self.assertEqual('V', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'actualversion': 'foo', 'specversion': 'foo'}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'uri': 'foo', 'curr_uri': 'foo'}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'uri': 'foo', 'curr_uri': 'bar'}
self.assertEqual('V', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'uri': self.test_root_path, 'curr_uri': self.test_root_path}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'uri': self.test_root_path, 'curr_uri': self.test_root_path + '/foo/..'}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'actualversion': 'foo', 'specversion': 'bar', 'modified': True}
self.assertEqual('MV', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'version': 'foo', 'default_remote_label': 'bar'}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'version': None, 'default_remote_label': 'bar', 'curr_version': 'bar'}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'version': None, 'default_remote_label': 'bar', 'curr_version': 'foo'}
self.assertEqual('C', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'remote_revision': 'a1b2c3d4', 'actualversion': 'a1b2c3d4'}
self.assertEqual('', wstool.cli_common._get_status_flags(basepath, entry))
entry = {'remote_revision': 'a1b2c3d4', 'actualversion': '999999'}
self.assertEqual('C', wstool.cli_common._get_status_flags(basepath, entry))
finally:
shutil.rmtree(self.test_root_path)
def test_info_table(self):
basepath = '/foo/path'
entries = []
self.assertEqual('', wstool.cli_common.get_info_table(basepath, entries))
entries = [{'scm': 'scm',
'uri': 'uri',
'curr_uri': 'uri',
'version': 'version',
'localname': 'localname',
'specversion': None,
'actualversion': None}]
self.assertEqual(["localname", "scm", "version", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'scm',
'uri': 'uri',
'curr_uri': 'uri',
'version': 'version',
'localname': 'localname',
'specversion': 'specversion',
'actualversion': 'actualversion'}]
self.assertEqual(["localname", 'V', "scm", "version", "actualversion", "(specversion)", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'scm',
'uri': 'uri',
'curr_uri': 'curr_uri',
'version': 'version',
'localname': 'localname'}]
self.assertEqual(["localname", 'V', "scm", "version", "curr_uri", "(uri)"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'scm',
'uri': 'uri',
'version': 'version',
'localname': 'localname',
'exists': False}]
self.assertEqual(["localname", 'x', "scm", "version", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
# shorten SHAIDs for git
entries = [{'scm': 'git',
'uri': 'uri',
'actualversion': '01234567890123456789012345678',
'localname': 'localname',
'exists': False}]
self.assertEqual(["localname", 'x', "git", "012345678901", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'git',
'uri': 'uri',
'actualversion': '01234567890123456789012345678',
'specversion': '1234567890123456789012345678',
'localname': 'localname'}]
self.assertEqual(["localname", 'V', "git", "012345678901", "(123456789012)", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
# recompute svn startdard layout
entries = [{'scm': 'svn',
'uri': 'https://some.svn.tags.server/some/tags/tagname',
'curr_uri': None,
'version': 'version',
'localname': 'localname',
'specversion': None,
'actualversion': None}]
self.assertEqual(["localname", "svn", "version", "(tags/tagname)", "some.svn.tags.server/some/"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'svn',
'uri': 'https://some.svn.tags.server/some/branches/branchname',
'curr_uri': None,
'version': 'version',
'localname': 'localname',
'specversion': None,
'actualversion': None}]
self.assertEqual(["localname", "svn", "version", "(branches/branchname)", "some.svn.tags.server/some/"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'svn',
'uri': 'https://some.svn.tags.server/some/trunk',
'curr_uri': None,
'version': 'version',
'localname': 'localname',
'specversion': None,
'actualversion': None}]
self.assertEqual(["localname", "svn", "version", "(trunk)", "some.svn.tags.server/some/"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'svn',
'uri': 'https://some.svn.tags.server/some/branches/branchname',
'curr_uri': 'https://some.svn.tags.server/some/tags/tagname',
'version': 'version',
'localname': 'localname',
'specversion': None,
'actualversion': None}]
self.assertEqual(["localname", "svn", "tags/tagname", "(branches/branchname)", "some.svn.tags.server/some/"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'svn',
'uri': 'https://some.svn.tags.server/some/branches/branchname',
'curr_uri': 'https://some.svn.tags.server/some/tags/tagname',
'version': None,
'localname': 'localname',
'specversion': 'broken',
'actualversion': 'version'}]
self.assertEqual(["localname", "V", "svn", "tags/tagname", "(branches/branchname)", "version", "(broken)", "some.svn.tags.server/some/"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'scm',
'uri': 'uri',
'curr_uri': 'uri',
'localname': 'localname',
'actualversion': 'actualversion',
'default_remote_label': 'default_remote_label',
'curr_version': 'curr_version'}]
self.assertEqual(["localname", "C", "scm", "curr_version", "(default_remote_label)", "actualversion", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'scm',
'uri': 'uri',
'curr_uri': 'uri',
'localname': 'localname',
'actualversion': 'actualversion',
'default_remote_label': 'curr_version',
'curr_version': 'curr_version'}]
self.assertEqual(["localname", "scm", "curr_version", "(=)", "actualversion", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries)))
entries = [{'scm': 'scm',
'uri': 'uri',
'curr_uri': 'uri',
'version': 'version',
'localname': 'localname',
'specversion': 'specversion',
'actualversion': 'actualversion'}]
self.assertEqual(["localname", "scm", "uri"], _nth_line_split(-1, wstool.cli_common.get_info_table(basepath, entries, unmanaged=True)))
def test_info_list(self):
basepath = '/foo/path'
entry = {'scm': 'somescm',
'uri': 'someuri',
'curr_uri': 'somecurr_uri',
'version': 'someversion',
'specversion': 'somespecversion',
'actualversion': 'someactualversion',
'localname': 'somelocalname',
'path': 'somepath'}
result = wstool.cli_common.get_info_list(basepath, entry).split()
for x in ['somepath', 'somelocalname', 'someactualversion', 'somespecversion', 'someversion', 'somecurr_uri', 'someuri', 'somescm']:
self.assertTrue(x in result)
class MultiprojectCLITest(AbstractFakeRosBasedTest):
def test_cmd_init(self):
self.local_path = os.path.join(self.test_root_path, "ws30")
os.makedirs(self.local_path)
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
self.assertEqual(0, cli.cmd_init([self.local_path]))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
# self.assertEqual(0, cli.cmd_merge(self.local_path, [self.ros_path, "-y"]))
# self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
# self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
# self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
# self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
def test_init_parallel(self):
self.local_path = os.path.join(self.test_root_path, "ws31")
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall, "--parallel=5"]))
self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'gitrepo')))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
self.assertEqual(0, cli.cmd_merge(self.local_path, [self.simple_changed_vcs_rosinstall, "-y"]))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'gitrepo')))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
self.assertEqual(0, cli.cmd_update(self.local_path, ["--parallel=5"]))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'gitrepo')))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
def test_cmd_info(self):
self.local_path = os.path.join(self.test_root_path, "ws_test_cmd_info")
cli = MultiprojectCLI(progname='multi_cli',
config_filename='.rosinstall')
self.assertEqual(0, cli.cmd_info(self.local_path, []))
self.assertEqual(0, cli.cmd_info(self.local_path, ['--root']))
self.assertEqual(0, cli.cmd_info(self.local_path, ['--yaml']))
self.assertEqual(0, cli.cmd_info(self.local_path, ['--untracked']))
self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall, "--parallel=5"]))
self.assertEqual(0, cli.cmd_merge(self.local_path, [self.simple_changed_vcs_rosinstall, "-y"]))
self.assertEqual(0, cli.cmd_info(self.local_path, []))
self.assertEqual(0, cli.cmd_info(self.local_path, ['gitrepo']))
self.assertEqual(0, cli.cmd_info(self.local_path, ['hgrepo']))
self.assertEqual(0, cli.cmd_info(self.local_path, ['--fetch']))
self.assertEqual(0, cli.cmd_info(self.local_path, ['gitrepo', '--fetch']))
self.assertEqual(0, cli.cmd_info(self.local_path, ['hgrepo', '--fetch']))
def test_cmd_set(self):
self.local_path = os.path.join(self.test_root_path, "ws31b")
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall, "--parallel=5"]))
self.assertEqual(0, cli.cmd_set(self.local_path,
[os.path.join(self.local_path, 'hgrepo'),
"--hg",
'http://some_uri',
'-y']))
cli.cmd_set(self.local_path,
[os.path.join(self.local_path, 'hgrepo'), self.hg_path,
'--hg', '--update', '-y'])
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
self.assertRaises(SystemExit, cli.cmd_set, self.local_path,
[os.path.join(self.local_path, 'hgrepo'),
"--detached",
'-y'])
cli = MultiprojectCLI(progname='multi_cli',
config_filename='.rosinstall',
allow_other_element=True)
self.assertEqual(0, cli.cmd_set(self.local_path,
[os.path.join(self.local_path, 'hgrepo'),
"--detached",
'-y']))
def test_cmd_foreach(self):
self.local_path = os.path.join(self.test_root_path, 'foreach')
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
cli.cmd_init([self.local_path, self.simple_rosinstall])
# specified localname
sys.stdout = f = StringIO()
cli.cmd_foreach(self.local_path, argv=['gitrepo', 'pwd'])
sys.stdout = sys.__stdout__
repo_path = lambda localname: os.path.join(self.local_path, localname)
self.assertEqual('[gitrepo] %s' % repo_path('gitrepo'),
f.getvalue().strip())
# --git option
sys.stdout = f = StringIO()
cli.cmd_foreach(self.local_path, argv=['--git', 'pwd'])
sys.stdout = sys.__stdout__
expected_output = '[ros] %s\n[gitrepo] %s' % (repo_path('ros'),
repo_path('gitrepo'))
self.assertEqual(expected_output, f.getvalue().strip())
def test_cmd_remove(self):
# wstool to create dir
self.local_path = os.path.join(self.test_root_path, "ws32")
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall', allow_other_element=False)
self.assertEqual(0, cli.cmd_init([self.local_path]))
self.assertRaises(MultiProjectException, cli.cmd_merge, self.local_path, [self.git_path, "-y"])
self.assertRaises(MultiProjectException, cli.cmd_merge, self.local_path, [self.hg_path, "-y"])
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall', allow_other_element=True)
self.assertEqual(0, cli.cmd_merge(self.local_path, [self.git_path, "-y"]))
self.assertEqual(0, cli.cmd_merge(self.local_path, [self.hg_path, "-y"]))
config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
config_filename='.rosinstall')
self.assertEqual(len(config.get_config_elements()), 2)
self.assertEqual(0, cli.cmd_remove(self.local_path, [self.git_path]))
config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
config_filename='.rosinstall')
self.assertEqual(len(config.get_config_elements()), 1)
def test_cmd_add_uris(self):
# wstool to create dir
self.local_path = os.path.join(self.test_root_path, "ws33")
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
simple_rel_rosinstall = os.path.join(self.test_root_path, "simple_rel3.rosinstall")
_create_yaml_file([_create_config_elt_dict(scmtype="git",
uri=os.path.join(self.test_root_path, "ros"),
localname='ros')],
simple_rel_rosinstall)
self.assertEqual(0, cli.cmd_init([self.local_path, simple_rel_rosinstall]))
config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
config_filename='.rosinstall')
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('git', config.get_config_elements()[0].get_path_spec().get_scmtype())
wstool.multiproject_cmd.add_uris(config, [self.local_path])
self.assertEqual(len(config.get_config_elements()), 1, config)
self.assertEqual('git', config.get_config_elements()[0].get_path_spec().get_scmtype())
wstool.multiproject_cmd.add_uris(config, [os.path.join(self.local_path, '.rosinstall')])
self.assertEqual(len(config.get_config_elements()), 1, config)
self.assertEqual('git', config.get_config_elements()[0].get_path_spec().get_scmtype())
def test_get_element_diff(self):
self.assertEqual('', _get_element_diff(None, None))
self.assertEqual('', _get_element_diff(None, 42))
self.assertEqual('', _get_element_diff(42, None))
spec = PathSpec('foolocalname',
scmtype='fooscm',
uri='foouri',
version='fooversion',
path='foopath')
spec2 = PathSpec('foolocalname')
element2 = MockConfigElement(local_name='foolocalname', spec=spec2)
elements = [element2]
config = FakeConfig(celts=elements)
output = _get_element_diff(spec, config)
self.assertEqual(' foolocalname', output)
output = _get_element_diff(spec, config, extra_verbose=True)
snippets = [' foolocalname',
'version = fooversion',
'specified uri = foouri',
'scmtype = fooscm']
for s in snippets:
self.assertTrue(s in output, "missing snippet: '%s' in '%s'" % (s, output))
def test_merge_dash(self):
self.local_path = os.path.join(self.test_root_path, "ws35")
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall, "--parallel=5"]))
self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'gitrepo')))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
try:
backup = sys.stdin
with open(self.simple_changed_vcs_rosinstall, 'r') as fhand:
contents = fhand.read()
sys.stdin = Mock()
sys.stdin.readlines.return_value = contents
self.assertEqual(0, cli.cmd_merge(self.local_path, ["-"]))
finally:
sys.stdin = backup
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'gitrepo')))
self.assertFalse(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
self.assertEqual(0, cli.cmd_update(self.local_path, ["--parallel=5"]))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'gitrepo')))
self.assertTrue(os.path.exists(os.path.join(self.local_path, 'hgrepo')))
def test_export(self):
root_path = os.path.realpath(tempfile.mkdtemp())
el_path = os.path.join(root_path, "ros")
os.makedirs(el_path)
try:
# default test
self.mock_config = FakeConfig([], [], 'foopath')
result = wstool.multiproject_cmd.cmd_snapshot(self.mock_config)
self.assertEqual(0, len(result))
mock = MockVcsConfigElement('git',
el_path,
'gitname',
None,
version='version',
actualversion='actual',
specversion='spec')
self.mock_config = FakeConfig([mock], [], 'foopath')
result = wstool.multiproject_cmd.cmd_snapshot(self.mock_config)
self.assertEqual(1, len(result))
self.assertEqual('actual', result[0]['git']['version'])
# test other is discarded
mock2 = wstool.config_elements.OtherConfigElement(el_path,
'othername')
self.mock_config = FakeConfig([mock, mock2], [], 'foopath')
result = wstool.multiproject_cmd.cmd_snapshot(self.mock_config)
self.assertEqual(1, len(result))
# test fallbacks on specs if actual version is not known
mock = MockVcsConfigElement('git',
el_path,
'gitname',
None,
version='version',
actualversion=None,
specversion='spec')
self.mock_config = FakeConfig([mock], [], 'foopath')
result = wstool.multiproject_cmd.cmd_snapshot(self.mock_config)
self.assertEqual(1, len(result))
self.assertEqual('spec', result[0]['git']['version'])
mock = MockVcsConfigElement('git',
el_path,
'gitname',
None,
version='version',
actualversion=None,
specversion=None)
self.mock_config = FakeConfig([mock], [], 'foopath')
result = wstool.multiproject_cmd.cmd_snapshot(self.mock_config)
self.assertEqual(1, len(result))
self.assertEqual('version', result[0]['git']['version'])
finally:
shutil.rmtree(root_path)
def test_scrape(self):
self.local_path = os.path.join(self.test_root_path, "ws37")
cli = MultiprojectCLI(progname='multi_cli', config_filename='.rosinstall')
self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall, "--parallel=5"]))
config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
config_filename='.rosinstall')
try:
cli.cmd_scrape(self.local_path, [], config)
self.fail("expected Exception")
except MultiProjectException:
pass
git_repo_path = os.path.join(self.local_path, 'gitrepo')
hg_repo_path = os.path.join(self.local_path, 'hgrepo')
subprocess.check_call(["git", "init", git_repo_path])
subprocess.check_call(["hg", "init", hg_repo_path])
for cmd in [["touch", "foo.txt"],
["hg", "add", hg_repo_path],
["hg", "commit", "-m", "foo"]]:
subprocess.check_call(cmd, cwd=hg_repo_path)
self.assertEqual(0, cli.cmd_scrape(self.local_path,
['-y'],
config))
config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
config_filename='.rosinstall')
# initial config has 1 element, "ros"
self.assertEqual(len(config.get_config_elements()), 3, config.get_config_elements())
wstool-0.1.18/test/local/test_config.py 0000664 0000000 0000000 00000044063 13542237257 0020100 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import tempfile
import shutil
import unittest
import wstool.config
from wstool.config import MultiProjectException, Config
from wstool.config_yaml import PathSpec
from . import mock_client
_test_root = os.path.dirname(os.path.dirname(__file__))
class MockVcsConfigElement(wstool.config_elements.VCSConfigElement):
def __init__(self, scmtype, path, local_name, uri, version='', installed=False, install_success=True, properties=None):
self.scmtype = scmtype
self.path = path
self.local_name = local_name
self.uri = uri
self.version = version
self.vcsc = mock_client.MockVcsClient()
self.installed = installed
self.install_success = install_success
def install(self, backup_path=None, arg_mode='abort', robust=False):
if not self.install_success:
raise MultiProjectException("Unittest Mock says exception failed")
self.installed = True
class ConfigMock_Test(unittest.TestCase):
def test_mock_vcs_element(self):
yaml = []
install_path = 'install/path'
config_filename = '.filename'
config = Config(yaml, install_path, config_filename)
try:
config._create_vcs_config_element('mock', None, None, None)
fail("expected Exception")
except MultiProjectException: pass
config = Config(yaml, install_path, config_filename, {"mock": MockVcsConfigElement})
self.assertTrue(config._create_vcs_config_element('mock', None, None, None))
class ConfigSimple_Test(unittest.TestCase):
def _get_mock_config(self, yaml, install_path='/install/path', merge_strategy="KillAppend"):
config_filename = '.filename'
return Config(yaml, install_path, config_filename, {"mock": MockVcsConfigElement}, merge_strategy=merge_strategy)
def test_init_fail(self):
try:
Config(None, "path", None)
self.fail("expected Exception")
except MultiProjectException:
pass
try:
Config([PathSpec('foo', 'bar')], "path", None)
self.fail("expected Exception")
except MultiProjectException:
pass
def test_init(self):
yaml = []
install_path = '/install/path'
config_filename = '.filename'
config = Config(yaml, install_path, config_filename)
self.assertEqual(install_path, config.get_base_path())
self.assertEqual([], config.get_config_elements())
config = Config([PathSpec("foo"),
PathSpec(os.path.join(_test_root, "example_dirs", "ros_comm")),
PathSpec(os.path.join(_test_root, "example_dirs", "ros")),
PathSpec(os.path.join(_test_root, "example_dirs", "roscpp")),
PathSpec("bar")],
".",
None)
self.assertEqual(os.path.abspath('.'), config.get_base_path())
def test_config_simple1(self):
mock1 = PathSpec('foo')
config = self._get_mock_config([mock1])
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('foo', config.get_config_elements()[0].get_local_name())
self.assertEqual('/install/path/foo', config.get_config_elements()[0].get_path())
def test_config_simple1_with_setupfile(self):
mock1 = PathSpec('setup.sh', tags='setup-file')
config = self._get_mock_config([mock1])
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('setup.sh', config.get_config_elements()[0].get_local_name())
self.assertEqual('/install/path/setup.sh', config.get_config_elements()[0].get_path())
mock1 = PathSpec('/foo')
mock2 = PathSpec('/opt/setup.sh', tags='setup-file')
mock3 = PathSpec('/bar')
config = self._get_mock_config([mock1, mock2, mock3])
self.assertEqual(3, len(config.get_config_elements()))
self.assertEqual('/opt/setup.sh', config.get_config_elements()[1].get_local_name())
self.assertEqual('/opt/setup.sh', config.get_config_elements()[1].get_path())
def test_config_simple2(self):
git1 = PathSpec('foo', 'git', 'git/uri')
svn1 = PathSpec('foos', 'svn', 'svn/uri')
hg1 = PathSpec('fooh', 'hg', 'hg/uri')
bzr1 = PathSpec('foob', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1])
self.assertEqual(4, len(config.get_config_elements()))
self.assertEqual('foo', config.get_config_elements()[0].get_local_name())
self.assertEqual('/install/path/foo', config.get_config_elements()[0].get_path())
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
self.assertEqual('svn', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/svn/uri', config.get_source()[1].get_uri())
self.assertEqual('hg', config.get_source()[2].get_scmtype())
self.assertEqual('/install/path/hg/uri', config.get_source()[2].get_uri())
self.assertEqual('bzr', config.get_source()[3].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[3].get_uri())
def test_config_simple3(self):
git1 = PathSpec('foo', 'git', 'git/uri', 'git.version')
svn1 = PathSpec('foos', 'svn', 'svn/uri', '12345')
bzr1 = PathSpec('foob', 'bzr', 'bzr/uri', 'bzr.version')
hg1 = PathSpec('fooh', 'hg', 'hg/uri', 'hg.version')
config = self._get_mock_config([git1, svn1, hg1, bzr1])
self.assertEqual(4, len(config.get_config_elements()))
def test_config_realfolders(self):
try:
root_path = tempfile.mkdtemp()
share_path = os.path.join(root_path, "share")
os.makedirs(share_path)
ros_path = os.path.join(share_path, "ros")
os.makedirs(ros_path)
p1 = PathSpec('share')
p2 = PathSpec('share/ros')
config = self._get_mock_config([p1, p2])
self.assertEqual(2, len(config.get_config_elements()))
try:
p1 = PathSpec('share', 'git', 'git/uri', 'git.version')
p2 = PathSpec('share/ros', 'hg', 'hg/uri', 'hg.version')
config = self._get_mock_config([p1, p2])
self.fail("expected overlap Exception")
except MultiProjectException:
pass
try:
p1 = PathSpec('share', 'git', 'git/uri', 'git.version')
p2 = PathSpec('share/ros', 'hg', 'hg/uri', 'hg.version')
config = self._get_mock_config([p2, p1])
self.fail("expected overlap Exception")
except MultiProjectException:
pass
try:
p1 = PathSpec('share', 'git', 'git/uri', 'git.version')
p2 = PathSpec('share/ros')
config = self._get_mock_config([p2, p1])
self.fail("expected overlap Exception")
except MultiProjectException:
pass
try:
p1 = PathSpec('share', 'git', 'git/uri', 'git.version')
p2 = PathSpec('share/ros')
config = self._get_mock_config([p1, p2])
self.fail("expected overlap Exception")
except MultiProjectException:
pass
finally:
shutil.rmtree(root_path)
def test_config_merging_kill_append(self):
git1 = PathSpec('foo', 'git', 'git/uri')
svn1 = PathSpec('foo', 'svn', 'svn/uri')
hg1 = PathSpec('foo', 'hg', 'hg/uri')
bzr1 = PathSpec('foo', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1])
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('bzr', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[0].get_uri())
config = self._get_mock_config([git1, svn1, hg1, bzr1, git1])
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
bzr1 = PathSpec('bar', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1])
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('hg', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/hg/uri', config.get_source()[0].get_uri())
self.assertEqual('bzr', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[1].get_uri())
config = self._get_mock_config([git1, svn1, hg1, bzr1, git1])
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('bzr', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[0].get_uri())
self.assertEqual('git', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[1].get_uri())
def test_config_merging_keep(self):
git1 = PathSpec('foo', 'git', 'git/uri')
svn1 = PathSpec('foo', 'svn', 'svn/uri')
hg1 = PathSpec('foo', 'hg', 'hg/uri')
bzr1 = PathSpec('foo', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1], merge_strategy="MergeKeep")
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
config = self._get_mock_config([git1, svn1, hg1, bzr1, git1], merge_strategy="MergeKeep")
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
bzr1 = PathSpec('bar', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1], merge_strategy="MergeKeep")
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
self.assertEqual('bzr', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[1].get_uri())
config = self._get_mock_config([git1, svn1, hg1, bzr1, git1], merge_strategy="MergeKeep")
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
self.assertEqual('bzr', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[1].get_uri())
def test_config_merging_replace(self):
git1 = PathSpec('foo', 'git', 'git/uri')
svn1 = PathSpec('foo', 'svn', 'svn/uri')
hg1 = PathSpec('foo', 'hg', 'hg/uri')
bzr1 = PathSpec('foo', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1], merge_strategy="MergeReplace")
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('bzr', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[0].get_uri())
config = self._get_mock_config([git1, svn1, hg1, bzr1, git1], merge_strategy="MergeReplace")
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
bzr1 = PathSpec('bar', 'bzr', 'bzr/uri')
config = self._get_mock_config([git1, svn1, hg1, bzr1], merge_strategy="MergeReplace")
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('hg', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/hg/uri', config.get_source()[0].get_uri())
self.assertEqual('bzr', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[1].get_uri())
config = self._get_mock_config([git1, svn1, hg1, bzr1, git1], merge_strategy="MergeReplace")
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('git', config.get_source()[0].get_scmtype())
self.assertEqual('/install/path/git/uri', config.get_source()[0].get_uri())
self.assertEqual('bzr', config.get_source()[1].get_scmtype())
self.assertEqual('/install/path/bzr/uri', config.get_source()[1].get_uri())
def test_remove(self):
git1 = PathSpec('foo', 'git', 'git/uri', 'git.version')
svn1 = PathSpec('foos', 'svn', 'svn/uri', '12345')
hg1 = PathSpec('fooh', 'hg', 'hg/uri', 'hg.version')
bzr1 = PathSpec('foob', 'bzr', 'bzr/uri', 'bzr.version')
config = self._get_mock_config([git1, svn1, hg1, bzr1])
self.assertEqual(4, len(config.get_config_elements()))
self.assertFalse(config.remove_element(None))
self.assertFalse(config.remove_element('bar'))
self.assertEqual(4, len(config.get_config_elements()))
self.assertTrue(config.remove_element('foo'))
self.assertEqual(3, len(config.get_config_elements()))
self.assertEqual('/install/path/foos', config.get_config_elements()[0].get_path())
self.assertEqual('/install/path/fooh', config.get_config_elements()[1].get_path())
self.assertEqual('/install/path/foob', config.get_config_elements()[2].get_path())
self.assertTrue(config.remove_element('fooh'))
self.assertEqual(2, len(config.get_config_elements()))
self.assertEqual('/install/path/foos', config.get_config_elements()[0].get_path())
self.assertEqual('/install/path/foob', config.get_config_elements()[1].get_path())
self.assertTrue(config.remove_element('foos'))
self.assertEqual(1, len(config.get_config_elements()))
self.assertTrue(config.remove_element('foob'))
self.assertEqual(0, len(config.get_config_elements()))
def test_absolute_localname(self):
mock1 = PathSpec('/foo/bim')
config = self._get_mock_config([mock1], install_path='/foo/bar/ba/ra/baz/bam')
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('/foo/bim', config.get_config_elements()[0].get_local_name())
self.assertEqual('/foo/bim', config.get_config_elements()[0].get_path())
def test_unnormalized_localname(self):
"Should source normalize local-name"
mock1 = PathSpec('foo/bar/..')
config = self._get_mock_config([mock1])
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('foo', config.get_config_elements()[0].get_local_name())
self.assertEqual('/install/path/foo', config.get_config_elements()[0].get_path())
def test_long_localname(self):
"Should source choose shorter local-name"
mock1 = PathSpec("/foo/bar/boo/far/bim")
config = self._get_mock_config([mock1], '/foo/bar/boo/far')
self.assertEqual(1, len(config.get_config_elements()))
self.assertEqual('/foo/bar/boo/far/bim', config.get_config_elements()[0].get_local_name())
self.assertEqual('/foo/bar/boo/far/bim', config.get_config_elements()[0].get_path())
def test_double_entry(self):
"Should source be rewritten without duplicates"
mock1 = PathSpec('foo')
mock2 = PathSpec('foo')
config = self._get_mock_config([mock1, mock2])
self.assertEqual(1, len(config.get_config_elements()))
def test_equivalent_entry(self):
"Should source be rewritten without duplicates"
mock1 = PathSpec('foo')
mock2 = PathSpec('./foo')
config = self._get_mock_config([mock1, mock2])
self.assertEqual(1, len(config.get_config_elements()))
def test_double_localname(self):
"Entries have same local name"
mock1 = PathSpec('foo', 'git', 'git/uri')
mock2 = PathSpec('foo', 'hg', 'hg/uri')
config = self._get_mock_config([mock1, mock2])
self.assertEqual(1, len(config.get_config_elements()))
def test_equivalent_localname(self):
"Entries have equivalent local name"
mock1 = PathSpec('foo', 'git', 'git/uri')
mock2 = PathSpec('./foo/bar/..', 'hg', 'hg/uri')
config = self._get_mock_config([mock1, mock2])
self.assertEqual(1, len(config.get_config_elements()))
wstool-0.1.18/test/local/test_config_elt.py 0000664 0000000 0000000 00000023417 13542237257 0020744 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import unittest
import wstool.config
from wstool.common import MultiProjectException
from . import mock_client
class ConfigElements_Test(unittest.TestCase):
def test_simple_config_element_API(self):
path = "some/path"
localname = "some/local/name"
other1 = wstool.config_elements.ConfigElement(path, localname)
self.assertEqual(path, other1.get_path())
self.assertEqual(localname, other1.get_local_name())
self.assertFalse(other1.is_vcs_element())
other1 = wstool.config_elements.OtherConfigElement(path, localname)
self.assertEqual(path, other1.get_path())
self.assertEqual(localname, other1.get_local_name())
self.assertEqual({'other': {'local-name': 'some/local/name'}}, other1.get_path_spec().get_legacy_yaml())
self.assertFalse(other1.is_vcs_element())
other1 = wstool.config_elements.SetupConfigElement(path, localname)
self.assertEqual(path, other1.get_path())
self.assertEqual(localname, other1.get_local_name())
self.assertEqual({'setup-file': {'local-name': 'some/local/name'}}, other1.get_path_spec().get_legacy_yaml())
self.assertFalse(other1.is_vcs_element())
other1 = wstool.config_elements.OtherConfigElement(path, localname, properties=[{}])
self.assertEqual(path, other1.get_path())
self.assertEqual(localname, other1.get_local_name())
self.assertEqual({'other': {'local-name': 'some/local/name'}}, other1.get_path_spec().get_legacy_yaml())
self.assertFalse(other1.is_vcs_element())
other1 = wstool.config_elements.OtherConfigElement(path, localname, properties=['meta'])
self.assertEqual(path, other1.get_path())
self.assertEqual(localname, other1.get_local_name())
self.assertEqual({'other': {'local-name': 'some/local/name', 'meta': None}}, other1.get_path_spec().get_legacy_yaml())
self.assertFalse(other1.is_vcs_element())
other1 = wstool.config_elements.OtherConfigElement(path, localname, properties=[{'meta': {'repo-name': 'skynetish-ros-pkg'}}])
self.assertEqual(path, other1.get_path())
self.assertEqual(localname, other1.get_local_name())
self.assertEqual({'other': {'local-name': 'some/local/name', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, other1.get_path_spec().get_legacy_yaml())
self.assertFalse(other1.is_vcs_element())
def test_mock_vcs_config_element_init(self):
path = "some/path"
localname = "some/local/name"
try:
wstool.config_elements.AVCSConfigElement("mock", None, None, None)
self.fail("Exception expected")
except MultiProjectException:
pass
try:
wstool.config_elements.AVCSConfigElement("mock", "path", None, None)
self.fail("Exception expected")
except MultiProjectException:
pass
try:
wstool.config_elements.AVCSConfigElement("mock", None, None, "some/uri")
self.fail("Exception expected")
except MultiProjectException:
pass
path = "some/path"
localname = "some/local/name"
uri = 'some/uri'
version = 'some.version'
vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, vcsc=mock_client.MockVcsClient())
self.assertEqual(path, vcsc.get_path())
self.assertEqual(localname, vcsc.get_local_name())
self.assertEqual(uri, vcsc.uri)
self.assertTrue(vcsc.is_vcs_element())
self.assertEqual("mocktypemockdiffNone", vcsc.get_diff())
self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri'}}, vcsc.get_path_spec().get_legacy_yaml())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri', }}, vcsc.get_versioned_path_spec().get_legacy_yaml())
vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, None, vcsc=mock_client.MockVcsClient())
self.assertEqual(path, vcsc.get_path())
self.assertEqual(localname, vcsc.get_local_name())
self.assertEqual(uri, vcsc.uri)
self.assertTrue(vcsc.is_vcs_element())
self.assertEqual("mocktypemockdiffNone", vcsc.get_diff())
self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri'}}, vcsc.get_path_spec().get_legacy_yaml())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri', }}, vcsc.get_versioned_path_spec().get_legacy_yaml())
vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, version, vcsc=mock_client.MockVcsClient())
self.assertEqual(path, vcsc.get_path())
self.assertEqual(localname, vcsc.get_local_name())
self.assertEqual(uri, vcsc.uri)
self.assertTrue(vcsc.is_vcs_element())
self.assertEqual("mocktypemockdiffNone", vcsc.get_diff())
self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri'}}, vcsc.get_path_spec().get_legacy_yaml())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri'}}, vcsc.get_versioned_path_spec().get_legacy_yaml())
vcsc = wstool.config_elements.AVCSConfigElement(
"mock", path, localname, uri, version,
vcsc=mock_client.MockVcsClient(),
properties=[{'meta': {'repo-name': 'skynetish-ros-pkg'}}])
self.assertEqual(path, vcsc.get_path())
self.assertEqual(localname, vcsc.get_local_name())
self.assertEqual(uri, vcsc.uri)
self.assertTrue(vcsc.is_vcs_element())
self.assertEqual("mocktypemockdiffNone", vcsc.get_diff())
self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, vcsc.get_path_spec().get_legacy_yaml())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, vcsc.get_versioned_path_spec().get_legacy_yaml())
# this time using 'uri_shortcut' in mock_client.MockVcsClient, get special treatment un url_matches()
uri2 = 'some/uri2'
vcsc = wstool.config_elements.AVCSConfigElement(
"mock", path, localname, uri2, version,
vcsc=mock_client.MockVcsClient(url='url_shortcut'),
properties=[{'meta': {'repo-name': 'skynetish-ros-pkg'}}])
self.assertEqual(path, vcsc.get_path())
self.assertEqual(localname, vcsc.get_local_name())
self.assertEqual(uri2, vcsc.uri)
self.assertTrue(vcsc.is_vcs_element())
self.assertEqual("mocktypemockdiffNone", vcsc.get_diff())
self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri2', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, vcsc.get_path_spec().get_legacy_yaml())
self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri2', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, vcsc.get_versioned_path_spec().get_legacy_yaml())
def test_mock_install(self):
path = "some/path"
localname = "some/local/name"
uri = 'some/uri'
version = 'some.version'
mockclient = mock_client.MockVcsClient(url=uri)
vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, None, vcsc=mockclient)
vcsc.install()
self.assertTrue(mockclient.checkedout)
self.assertFalse(mockclient.updated)
# checkout failure
mockclient = mock_client.MockVcsClient(url=uri, checkout_success=False)
try:
vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, None, vcsc=mockclient)
vcsc.install()
self.fail("should have raised Exception")
except MultiProjectException:
pass
wstool-0.1.18/test/local/test_config_yaml.py 0000664 0000000 0000000 00000046327 13542237257 0021127 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import unittest
import tempfile
import shutil
import subprocess
import wstool.config_yaml
import wstool.config
from wstool.common import MultiProjectException
from wstool.config_yaml import rewrite_included_source, \
get_path_spec_from_yaml, get_yaml_from_uri, get_path_specs_from_uri, \
PathSpec, aggregate_from_uris, __REPOTYPES__
_test_root = os.path.dirname(os.path.dirname(__file__))
class YamlIO_Test(unittest.TestCase):
def test_get_yaml_from_uri_from_file(self):
filename = os.path.join(_test_root, "example.yaml")
yamlstr = get_yaml_from_uri(filename)
self.assertTrue("text" in yamlstr)
self.assertTrue(yamlstr["text"] == "foobar")
self.assertTrue("number" in yamlstr)
self.assertTrue(yamlstr["number"] == 2)
# invalid
try:
yaml = get_yaml_from_uri(
os.path.join(_test_root, "example-broken.yaml"))
except MultiProjectException:
pass
try:
get_path_specs_from_uri(filename)
self.fail("Expected exception")
except MultiProjectException:
pass
def test_get_yaml_from_uri_from_missing_file(self):
filename = "/asdfasdfasdfasfasdf_does_not_exist"
try:
get_yaml_from_uri(filename)
self.fail("Expected exception")
except MultiProjectException:
pass
try:
get_path_specs_from_uri(filename)
self.fail("Expected exception")
except MultiProjectException:
pass
def test_get_yaml_from_uri_from_invalid_url(self):
url = "http://invalidurl"
try:
get_yaml_from_uri(url)
self.fail("Expected exception")
except MultiProjectException:
pass
# valid but non-yaml
url = "http://www.google.com"
try:
get_yaml_from_uri(url)
self.fail("Expected exception")
except MultiProjectException:
pass
class ConfigElementYamlFunctions_Test(unittest.TestCase):
def test_rewrite_included_source(self):
base_path = '/foo/bar'
version = 'common_rosdeps-1.0.2'
uri = 'https://kforge.ros.org/common/rosdepcore'
# same simple
struct = [PathSpec('local', 'hg', uri, version)]
rewrite_included_source(struct, "/foo/bar")
self.assertEqual(PathSpec(os.path.join(base_path, "local")), struct[0])
# absolute path
struct = [PathSpec("/opt/poo", 'hg', uri, version)]
rewrite_included_source(struct, "/foo/bar")
self.assertEqual([PathSpec("/opt/poo")], struct)
# absolute path, relative basepath
struct = [PathSpec("/opt/poo", 'hg', uri, version)]
rewrite_included_source(struct, "foo/bar")
self.assertEqual([PathSpec("/opt/poo")], struct)
# relative base path
struct = [PathSpec("../opt/poo", 'hg', uri, version)]
rewrite_included_source(struct, "foo/bar")
self.assertEqual([PathSpec("foo/opt/poo")], struct)
def test_rewrite_included_source_setupfile(self):
base_path = '/foo/bar'
version = 'common_rosdeps-1.0.2'
uri = 'https://kforge.ros.org/common/rosdepcore'
# same simple
struct = [PathSpec('local', tags='setup-file')]
rewrite_included_source(struct, "/foo/bar")
self.assertEqual(PathSpec(os.path.join(base_path, "local"), tags='setup-file'), struct[0])
# absolute path
struct = [PathSpec("/opt/poo", tags='setup-file')]
rewrite_included_source(struct, "/foo/bar")
self.assertEqual([PathSpec("/opt/poo", tags='setup-file')], struct)
# absolute path, relative basepath
struct = [PathSpec("/opt/poo", tags='setup-file')]
rewrite_included_source(struct, "foo/bar")
self.assertEqual([PathSpec("/opt/poo", tags='setup-file')], struct)
# relative base path
struct = [PathSpec("../opt/poo", tags='setup-file')]
rewrite_included_source(struct, "foo/bar")
self.assertEqual([PathSpec("foo/opt/poo", tags='setup-file')], struct)
class UriAggregationTest(unittest.TestCase):
def test_aggregate_from_uris(self):
self.directory = tempfile.mkdtemp()
config = wstool.config.Config(
[PathSpec('ros', 'svn', 'some/uri')], self.directory)
wstool.config_yaml.generate_config_yaml(config, 'foo', "# Hello\n")
ryaml = aggregate_from_uris(
[self.directory], config.get_config_filename())
self.assertEqual(ryaml[0].get_legacy_yaml(),
{'other': {'local-name': self.directory}})
self.assertRaises(MultiProjectException,
aggregate_from_uris,
[self.directory],
config.get_config_filename(),
allow_other_element=False)
def tearDown(self):
if os.path.exists(self.directory):
shutil.rmtree(self.directory)
class ConfigFile_Test(unittest.TestCase):
def make_repo_get_uuid(self):
repo_path = os.path.join(self.directory, self.git)
subprocess.check_call(['git', 'init', self.git], cwd=self.directory)
subprocess.check_call(['touch', 'test.txt'], cwd=repo_path)
subprocess.check_call(['git', 'add', '*'], cwd=repo_path)
subprocess.check_call(['git', 'commit', '-m', 'msg'], cwd=repo_path)
subprocess.check_call(['git', 'remote', 'add', 'origin', self.uri],
cwd=repo_path)
po = subprocess.Popen(['git', 'rev-parse', 'HEAD'], cwd=repo_path,
stdout=subprocess.PIPE)
uuid = po.stdout.read().decode('UTF-8').rstrip('"\n').lstrip('"\n')
return uuid
def helper(self, check_config_entries, args, entries):
assert check_config_entries.__class__.__name__ == 'function'
header = '# Hello'
filename = 'foo'
uuid = self.make_repo_get_uuid()
for entry in entries:
self.assertTrue(isinstance(entry, PathSpec))
if entry._scmtype:
self.assertTrue(entry._scmtype in __REPOTYPES__)
config = wstool.config.Config(entries, self.directory)
wstool.config_yaml.generate_config_yaml(config, filename,
header + '\n', **args)
filepath = os.path.join(self.directory, filename)
self.assertTrue(os.path.exists(filepath))
with open(filepath, 'r') as f:
read_data = f.read()
lines = read_data.splitlines()
self.assertTrue(len(lines) > 0)
self.assertEqual(header, lines[0])
check_config_entries(self, lines[1:], uuid)
def test_generate_empty(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(0, len(lines))
self.helper(check_config_entries, {}, [])
def test_generate_with_version(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(2, len(lines))
self.assertEqual(self.git_el % self.version, lines[0])
self.assertEqual(self.other_el, lines[1])
self.helper(check_config_entries, {},
[PathSpec(self.git, 'git', self.uri, self.version),
PathSpec(self.other)])
def test_generate_with_exact(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(2, len(lines))
self.assertEqual(self.git_el % uuid, lines[0])
self.assertEqual(self.other_el, lines[1])
self.helper(check_config_entries, {'spec': False, 'exact': True},
[PathSpec(self.git, 'git', self.uri, self.version),
PathSpec(self.other)])
def test_generate_with_vcs_only(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(1, len(lines))
self.assertEqual(self.git_el % self.version, lines[0])
self.helper(check_config_entries, {'vcs_only': True},
[PathSpec(self.git, 'git', self.uri, self.version),
PathSpec(self.other)])
def test_generate_with_spec_exact(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(1, len(lines))
self.assertEqual(self.git_el % uuid, lines[0])
self.helper(check_config_entries,
{'spec': True, 'exact': 'True', 'vcs_only': True},
[PathSpec(self.git, 'git', self.uri, self.version),
PathSpec(self.other)])
def test_generate_with_other(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(1, len(lines))
self.assertEqual(self.other_el, lines[0])
self.helper(check_config_entries, {}, [PathSpec(self.other)])
def test_generate_with_stack(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(1, len(lines))
self.assertEqual("- svn: {local-name: ros, uri: %s/some/uri}"
% self.directory, lines[0])
self.helper(check_config_entries, {},
[PathSpec('ros', 'svn', 'some/uri')])
def test_generate_with_pretty_format(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(3, len(lines))
self.assertEqual('''\
- git:
local-name: %s
uri: %s\
''' % (self.git, self.uri), '\n'.join(lines))
self.helper(check_config_entries, {'pretty': True},
[PathSpec(self.git, 'git', self.uri)])
def test_generate_sorted_with_localname(self):
def check_config_entries(self, lines, uuid):
self.assertEqual(2, len(lines))
self.assertEqual('''\
- git: {local-name: git, uri: %s/some/uri2}
- svn: {local-name: ros, uri: %s/some/uri1}\
''' % (self.directory, self.directory), "\n".join(lines[:2]))
self.helper(check_config_entries, {'sort_with_localname': True},
[PathSpec('ros', 'svn', 'some/uri1'),
PathSpec('git', 'git', 'some/uri2')])
def setUp(self):
self.uri = 'http://some/uri'
self.version = 'master'
self.git = 'ros'
self.git_el = '- git: {{local-name: {0}, uri: \'{1}\', version: %s}}' \
.format(self.git, self.uri)
self.other = 'foobar'
self.other_el = '- other: {local-name: %s}' % self.other
self.directory = tempfile.mkdtemp()
def tearDown(self):
if os.path.exists(self.directory):
shutil.rmtree(self.directory)
class ConfigElementYamlWrapper_Test(unittest.TestCase):
def test_original_syntax_scm(self):
# - hg: {local-name: common_rosdeps, version: common_rosdeps-1.0.2, uri: https://kforge.ros.org/common/rosdepcore}
local_name = 'common_rosdeps'
version = 'common_rosdeps-1.0.2'
uri = 'https://kforge.ros.org/common/rosdepcore'
scmtype = 'hg'
struct = {scmtype: {'local-name': local_name, 'version': version, 'uri': uri}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual(scmtype, wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
self.assertEqual(struct, wrap.get_legacy_yaml())
# empty version
local_name = 'common_rosdeps'
version = None
uri = 'https://kforge.ros.org/common/rosdepcore'
scmtype = 'hg'
struct = {scmtype: {'local-name': local_name, 'version': version, 'uri': uri}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual(scmtype, wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
self.assertEqual({scmtype: {'local-name': local_name, 'uri': uri}}, wrap.get_legacy_yaml())
# no version
local_name = 'common_rosdeps'
version = None
uri = 'https://kforge.ros.org/common/rosdepcore'
scmtype = 'hg'
struct = {scmtype: {'local-name': local_name, 'uri': uri}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual(scmtype, wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
self.assertEqual({'hg': {'local-name': 'common_rosdeps', 'uri': 'https://kforge.ros.org/common/rosdepcore'}}, wrap.get_legacy_yaml())
# other
local_name = 'common_rosdeps'
version = None
uri = None
scmtype = 'other'
struct = {scmtype: {'local-name': local_name, 'version': version, 'uri': uri}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual(None, wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
self.assertEqual({scmtype: {'local-name': local_name}}, wrap.get_legacy_yaml())
# properties (undocumented feature required for builds)
local_name = 'common_rosdeps'
version = None
uri = None
scmtype = 'other'
struct = {scmtype: {'local-name': local_name, 'version': version, 'uri': uri,
'meta': {'repo-name': 'skynetish-ros-pkg'}}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual(None, wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
self.assertEqual([{'meta': {'repo-name': 'skynetish-ros-pkg'}}], wrap.get_tags())
self.assertEqual({scmtype: {'local-name': local_name, 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, wrap.get_legacy_yaml())
# properties (undocumented feature required for builds)
local_name = 'common_rosdeps'
version = None
uri = 'some/uri'
scmtype = 'git'
struct = {scmtype: {'local-name': local_name, 'version': version, 'uri': uri,
'meta': {'repo-name': 'skynetish-ros-pkg'}}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual('git', wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
self.assertEqual([{'meta': {'repo-name': 'skynetish-ros-pkg'}}], wrap.get_tags())
self.assertEqual({scmtype: {'local-name': local_name, 'uri': 'some/uri', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, wrap.get_legacy_yaml())
def test_original_syntax_invalids(self):
local_name = 'common_rosdeps'
version = '1234'
uri = 'https://kforge.ros.org/common/rosdepcore'
scmtype = 'hg'
try:
struct = {}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"hello world": None}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"git": None}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"git": {}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"git": {"uri": uri}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"git": {"local-name": local_name}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"foo": {"foo": None}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"other": {"foo": None}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"other": {"uri": uri}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"other": {"version": version}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
def test_original_syntax_setupfile(self):
local_name = '/opt/ros/fuerte/setup.sh'
version = None
uri = None
scmtype = 'setup-file'
struct = {scmtype: {'local-name': local_name, 'version': version, 'uri': uri}}
wrap = get_path_spec_from_yaml(struct)
self.assertEqual(None, wrap.get_scmtype())
self.assertEqual(scmtype, wrap.get_legacy_type())
self.assertEqual(version, wrap.get_version())
self.assertEqual(uri, wrap.get_uri())
version = "1234"
uri = 'https://kforge.ros.org/common/rosdepcore'
try:
struct = {"setup-file": {"uri": uri}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
try:
struct = {"setup-file": {"version": version}}
get_path_spec_from_yaml(struct)
self.fail("expected exception")
except MultiProjectException:
pass
wstool-0.1.18/test/local/test_diff_functions_bzr.py 0000664 0000000 0000000 00000026146 13542237257 0022512 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
from __future__ import unicode_literals
import os
import sys
from test.io_wrapper import StringIO
import subprocess
import wstool
import wstool.helpers
import wstool.wstool_cli
from wstool.wstool_cli import WstoolCLI
from wstool.wstool_cli import wstool_main
import test.scm_test_base
from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split
def create_bzr_repo(remote_path):
# create a "remote" repo
subprocess.check_call(["bzr", "init"], cwd=remote_path)
subprocess.check_call(["touch", "fixed.txt"], cwd=remote_path)
subprocess.check_call(["touch", "modified.txt"], cwd=remote_path)
subprocess.check_call(["touch", "modified-fs.txt"], cwd=remote_path)
subprocess.check_call(["touch", "deleted.txt"], cwd=remote_path)
subprocess.check_call(["touch", "deleted-fs.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "fixed.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "modified.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "modified-fs.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "deleted.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "deleted-fs.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "commit", "-m", "modified"], cwd=remote_path)
def modify_bzr_repo(clone_path):
# make local modifications
subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path)
subprocess.check_call(["bzr", "rm", "deleted.txt"], cwd=clone_path)
_add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n")
_add_to_file(os.path.join(clone_path, "added.txt"), "flam\n")
subprocess.check_call(["bzr", "add", "added.txt"], cwd=clone_path)
class WstoolDiffBzrTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(remote_path)
create_bzr_repo(remote_path)
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"),
"- other: {local-name: ../ros}\n- bzr: {local-name: clone, uri: %s}" % remote_path)
cmd = ["wstool", "update", "-t", "ws"]
os.chdir(self.test_root_path)
wstool_main(cmd)
clone_path = os.path.join(self.local_path, "clone")
modify_bzr_repo(clone_path)
def check_diff_output(self, output):
# uncomment following line for easiest way to get actual output with escapes
# self.assertEqual(None, output);
# bzr writes date-time of file into diff
self.assertTrue(output.startswith("=== added file 'added.txt'\n--- clone/added.txt"), msg=0)
self.assertTrue(0 < output.find("+++ clone/added.txt"), msg=1)
self.assertTrue(0 < output.find("@@ -0,0 +1,1 @@\n+flam\n\n"), msg=2)
self.assertTrue(0 < output.find("=== removed file 'deleted-fs.txt'\n=== removed file 'deleted.txt'\n=== modified file 'modified-fs.txt'\n--- clone/modified-fs.txt"), msg=3)
self.assertTrue(0 < output.find("@@ -0,0 +1,1 @@\n+foo\n\n=== modified file 'modified.txt'\n--- clone/modified.txt"), msg=4)
def test_wstool_diff_bzr_outside(self):
"""Test diff output for bzr when run outside workspace"""
cmd = ["wstool", "diff", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_diff_bzr_inside(self):
"""Test diff output for bzr when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "diff"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_wstool_status_bzr_inside(self):
"""Test status output for bzr when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "status"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.assertEqual('+N clone/added.txt\n D clone/deleted-fs.txt\n-D clone/deleted.txt\n M clone/modified-fs.txt\n M clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(directory, []))
def test_wstool_status_bzr_outside(self):
"""Test status output for bzr when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertEqual('+N clone/added.txt\n D clone/deleted-fs.txt\n-D clone/deleted.txt\n M clone/modified-fs.txt\n M clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_status_bzr_untracked(self):
"""Test status output for bzr when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws", "--untracked"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertEqual('? clone/added-fs.txt\n+N clone/added.txt\n D clone/deleted-fs.txt\n-D clone/deleted.txt\n M clone/modified-fs.txt\n M clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"]))
def test_wstool_info_bzr(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'bzr'], tokens[0:3], output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), []))
class WstoolInfoBzrTest(AbstractSCMTest):
def setUp(self):
AbstractSCMTest.setUp(self)
remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(remote_path)
# create a "remote" repo
subprocess.check_call(["bzr", "init"], cwd=remote_path)
subprocess.check_call(["touch", "test.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "test.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "commit", "-m", "modified"], cwd=remote_path)
self.version_init = "1"
subprocess.check_call(["bzr", "tag", "footag"], cwd=remote_path)
subprocess.check_call(["touch", "test2.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "add", "test2.txt"], cwd=remote_path)
subprocess.check_call(["bzr", "commit", "-m", "modified"], cwd=remote_path)
self.version_end = "2"
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- bzr: {local-name: clone, uri: ../remote}")
cmd = ["wstool", "update"]
os.chdir(self.local_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
def test_rosinstall_detailed_locapath_info(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'bzr', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens)
clone_path = os.path.join(self.local_path, "clone")
# make local modifications check
subprocess.check_call(["rm", "test2.txt"], cwd=clone_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'bzr', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens)
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- bzr: {local-name: clone, uri: ../remote, version: \"footag\"}")
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'bzr', 'footag', self.version_end, "(%s)" % self.version_init, os.path.join(self.test_root_path, 'remote')], tokens)
subprocess.check_call(["rm", "-rf", "clone"], cwd=self.local_path)
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'x', 'bzr', 'footag', os.path.join(self.test_root_path, 'remote')], tokens)
wstool-0.1.18/test/local/test_diff_functions_git.py 0000664 0000000 0000000 00000037621 13542237257 0022500 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import sys
import subprocess
from test.io_wrapper import StringIO
import wstool
import wstool.helpers
import wstool.wstool_cli
from wstool.wstool_cli import WstoolCLI
from wstool.wstool_cli import wstool_main
import test.scm_test_base
from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split
def create_git_repo(remote_path):
# create a "remote" repo
subprocess.check_call(["git", "init"], cwd=remote_path)
subprocess.check_call(["touch", "fixed.txt"], cwd=remote_path)
subprocess.check_call(["touch", "modified.txt"], cwd=remote_path)
subprocess.check_call(["touch", "modified-fs.txt"], cwd=remote_path)
subprocess.check_call(["touch", "deleted.txt"], cwd=remote_path)
subprocess.check_call(["touch", "deleted-fs.txt"], cwd=remote_path)
subprocess.check_call(["git", "add", "*"], cwd=remote_path)
subprocess.check_call(["git", "commit", "-m", "modified"], cwd=remote_path)
def modify_git_repo(clone_path):
# make local modifications
subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path)
subprocess.check_call(["git", "rm", "deleted.txt"], cwd=clone_path)
_add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n")
subprocess.check_call(["git", "add", "modified.txt"], cwd=clone_path)
_add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n")
_add_to_file(os.path.join(clone_path, "added.txt"), "flam\n")
subprocess.check_call(["git", "add", "added.txt"], cwd=clone_path)
class WstoolDiffGitTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(remote_path)
create_git_repo(remote_path)
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- git: {local-name: clone, uri: ../remote}")
cmd = ["wstool", "update", "-t", "ws"]
os.chdir(self.test_root_path)
wstool_main(cmd)
clone_path = os.path.join(self.local_path, "clone")
modify_git_repo(clone_path)
def check_diff_output(self, output):
# sha ids are always same with git
self.assertEqual('diff --git clone/added.txt clone/added.txt\nnew file mode 100644\nindex 0000000..8d63207\n--- /dev/null\n+++ clone/added.txt\n@@ -0,0 +1 @@\n+flam\ndiff --git clone/deleted-fs.txt clone/deleted-fs.txt\ndeleted file mode 100644\nindex e69de29..0000000\ndiff --git clone/deleted.txt clone/deleted.txt\ndeleted file mode 100644\nindex e69de29..0000000\ndiff --git clone/modified-fs.txt clone/modified-fs.txt\nindex e69de29..257cc56 100644\n--- clone/modified-fs.txt\n+++ clone/modified-fs.txt\n@@ -0,0 +1 @@\n+foo\ndiff --git clone/modified.txt clone/modified.txt\nindex e69de29..257cc56 100644\n--- clone/modified.txt\n+++ clone/modified.txt\n@@ -0,0 +1 @@\n+foo', output.rstrip())
def test_wstool_diff_git_outside(self):
"""Test diff output for git when run outside workspace"""
cmd = ["wstool", "diff", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_diff_git_inside(self):
"""Test diff output for git when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "diff"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_wstool_status_git_inside(self):
"""Test status output for git when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "status"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.assertEqual('A clone/added.txt\n D clone/deleted-fs.txt\nD clone/deleted.txt\n M clone/modified-fs.txt\nM clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_Wstool_status_git_outside(self):
"""Test status output for git when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertEqual('A clone/added.txt\n D clone/deleted-fs.txt\nD clone/deleted.txt\n M clone/modified-fs.txt\nM clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), []))
def test_Wstool_status_git_untracked(self):
"""Test untracked status output for git when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws", "--untracked"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertEqual('A clone/added.txt\n D clone/deleted-fs.txt\nD clone/deleted.txt\n M clone/modified-fs.txt\nM clone/modified.txt\n?? clone/added-fs.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"]))
def test_wstool_info_git(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'git'], tokens[0:3])
tokens2 = _nth_line_split(-1, output)
self.assertEqual(1, len(tokens2))
self.assertEqual('../ros', tokens2[0])
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), []))
class WstoolInfoGitTest(AbstractSCMTest):
def setUp(self):
AbstractSCMTest.setUp(self)
self.remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(self.remote_path)
# create a "remote" repo
subprocess.check_call(["git", "init"], cwd=self.remote_path)
subprocess.check_call(["touch", "test.txt"], cwd=self.remote_path)
subprocess.check_call(["git", "add", "*"], cwd=self.remote_path)
subprocess.check_call(["git", "commit", "-m", "modified"], cwd=self.remote_path)
po = subprocess.Popen(["git", "log", "-n", "1", "--pretty=format:\"%H\""], cwd=self.remote_path, stdout=subprocess.PIPE)
self.version_init = po.stdout.read().decode('UTF-8').rstrip('"').lstrip('"')[0:12]
subprocess.check_call(["git", "tag", "footag"], cwd=self.remote_path)
subprocess.check_call(["touch", "test2.txt"], cwd=self.remote_path)
subprocess.check_call(["git", "add", "*"], cwd=self.remote_path)
subprocess.check_call(["git", "commit", "-m", "modified"], cwd=self.remote_path)
po = subprocess.Popen(["git", "log", "-n", "1", "--pretty=format:\"%H\""], cwd=self.remote_path, stdout=subprocess.PIPE)
self.version_end = po.stdout.read().decode('UTF-8').rstrip('"').lstrip('"')[0:12]
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- git: {local-name: clone, uri: ../remote}")
self.clone_path = os.path.join(self.local_path, "clone")
cmd = ["wstool", "update"]
os.chdir(self.local_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
def test_wstool_detailed_localpath_info(self):
cmd = ["wstool", "info", "-t", "ws", "--managed-only"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'git', 'master', '(-)', self.version_end, self.remote_path], tokens)
# test when remote version is different
subprocess.check_call(["git", "reset", "--hard", "HEAD~1"], cwd=self.clone_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'C', 'git', 'master', '(-)', self.version_init, self.remote_path], tokens)
# return branch back to original revision
subprocess.check_call(["git", "reset", "--hard", self.version_end], cwd=self.clone_path)
# make local modifications check
subprocess.check_call(["rm", "test2.txt"], cwd=self.clone_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'git', 'master', '(-)', self.version_end, self.remote_path], tokens)
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- git: {local-name: clone, uri: ../remote, version: \"footag\"}")
# test when version is different
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'git', 'master', '(footag)', self.version_end, "(%s)" % self.version_init, self.remote_path], tokens)
# test when tracking branch is different from current branch
subprocess.check_call(["git", "checkout", "-b", "test_branch"], cwd=self.clone_path)
subprocess.check_call(["git", "config", "--replace-all", "branch.test_branch.remote", "origin"], cwd=self.clone_path)
subprocess.check_call(["git", "config", "--replace-all", "branch.test_branch.merge", "master"], cwd=self.clone_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'git', 'test_branch', '<', 'master', '(footag)', self.version_end, "(%s)" % self.version_init, self.remote_path], tokens)
# test when remote is different from origin by rename
subprocess.check_call(["git", "remote", "rename", "origin", "remote2"], cwd=self.clone_path)
subprocess.check_call(["git", "config", "--replace-all", "branch.test_branch.remote", "remote2"], cwd=self.clone_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'git', 'test_branch', '<', 'remote2/master', '(footag)', self.version_end, "(%s)" % self.version_init, "(%s)" % self.remote_path], tokens)
# return remote name to origin
subprocess.check_call(["git", "remote", "rename", "remote2", "origin"], cwd=self.clone_path)
# test when remote is different from origin, no fetch
subprocess.check_call(["git", "remote", "add", "remote2", "../../remote"], cwd=self.clone_path)
subprocess.check_call(["git", "config", "--replace-all", "branch.test_branch.remote", "remote2"], cwd=self.clone_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'git', 'test_branch', '(footag)', self.version_end, "(%s)" % self.version_init, self.remote_path], tokens)
# test when remote is different from origin, with fetch
sys.stdout = output = StringIO()
wstool_main(cmd + ['--fetch'])
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'git', 'test_branch', '<', 'remote2/master', '(footag)', self.version_end, "(%s)" % self.version_init, self.remote_path], tokens)
# return branch back to master
subprocess.check_call(["git", "checkout", "master"], cwd=self.clone_path)
# using a denormalized local-name here
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- git: {local-name: clone/../clone, uri: ../remote, version: \"footag\"}")
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'git', 'master', '(footag)', self.version_end, "(%s)" %
self.version_init, self.remote_path], tokens)
# using an absolute path to clone dir here
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- git: {local-name: '"+self.clone_path+"', uri: ../remote, version: \"footag\"}")
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual([self.clone_path, 'MV', 'git', 'master', '(footag)', self.version_end, "(%s)" % self.version_init, self.remote_path], tokens)
# using an absolute path here where relative path is shorter to display (also checks x for missing)
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- git: {local-name: '"+os.path.join(self.local_path, "../foo")+"', uri: ../remote, version: \"footag\"}")
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
localname = os.path.join(os.path.dirname(self.local_path), 'foo')
self.assertEqual([localname, 'x', 'git', '(footag)', self.remote_path], tokens)
wstool-0.1.18/test/local/test_diff_functions_hg.py 0000664 0000000 0000000 00000026436 13542237257 0022315 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
from __future__ import unicode_literals
import os
import sys
from test.io_wrapper import StringIO
import subprocess
import wstool
import wstool.helpers
import wstool.wstool_cli
from wstool.wstool_cli import WstoolCLI
from wstool.wstool_cli import wstool_main
import test.scm_test_base
from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split
def create_hg_repo(remote_path):
# create a "remote" repo
subprocess.check_call(["hg", "init"], cwd=remote_path)
subprocess.check_call(["touch", "fixed.txt"], cwd=remote_path)
subprocess.check_call(["touch", "modified.txt"], cwd=remote_path)
subprocess.check_call(["touch", "modified-fs.txt"], cwd=remote_path)
subprocess.check_call(["touch", "deleted.txt"], cwd=remote_path)
subprocess.check_call(["touch", "deleted-fs.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "fixed.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "modified.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "modified-fs.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "deleted.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "deleted-fs.txt"], cwd=remote_path)
subprocess.check_call(["hg", "commit", "-m", "modified"], cwd=remote_path)
def modify_hg_repo(clone_path):
# make local modifications
subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path)
subprocess.check_call(["hg", "rm", "deleted.txt"], cwd=clone_path)
_add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n")
_add_to_file(os.path.join(clone_path, "added.txt"), "flam\n")
subprocess.check_call(["hg", "add", "added.txt"], cwd=clone_path)
class WstoolDiffHgTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(remote_path)
create_hg_repo(remote_path)
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- hg: {local-name: clone, uri: ../remote}")
cmd = ["wstool", "update", "-t", "ws"]
os.chdir(self.test_root_path)
wstool_main(cmd)
clone_path = os.path.join(self.local_path, "clone")
modify_hg_repo(clone_path)
def check_diff_output(self, output):
# sha ids are always same with hg
self.assertEqual('diff --git clone/added.txt clone/added.txt\nnew file mode 100644\n--- /dev/null\n+++ clone/added.txt\n@@ -0,0 +1,1 @@\n+flam\ndiff --git clone/deleted.txt clone/deleted.txt\ndeleted file mode 100644\ndiff --git clone/modified-fs.txt clone/modified-fs.txt\n--- clone/modified-fs.txt\n+++ clone/modified-fs.txt\n@@ -0,0 +1,1 @@\n+foo\ndiff --git clone/modified.txt clone/modified.txt\n--- clone/modified.txt\n+++ clone/modified.txt\n@@ -0,0 +1,1 @@\n+foo\n', output)
def test_wstool_diff_hg_outside(self):
"""Test diff output for hg when run outside workspace"""
cmd = ["wstool", "diff", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_diff_hg_inside(self):
"""Test diff output for hg when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "diff"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(directory, []))
def test_wstool_status_hg_inside(self):
"""Test status output for hg when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "status"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.assertEqual('M clone/modified-fs.txt\nM clone/modified.txt\nA clone/added.txt\nR clone/deleted.txt\n! clone/deleted-fs.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_wstool_status_hg_outside(self):
"""Test status output for hg when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertEqual('M clone/modified-fs.txt\nM clone/modified.txt\nA clone/added.txt\nR clone/deleted.txt\n! clone/deleted-fs.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_status_hg_untracked(self):
"""Test untracked status output for hg when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws", "--untracked"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertEqual('M clone/modified-fs.txt\nM clone/modified.txt\nA clone/added.txt\nR clone/deleted.txt\n! clone/deleted-fs.txt\n? clone/added-fs.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"]))
def test_wstool_info_hg(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'hg'], tokens[0:3], output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), []))
class WstoolInfoHgTest(AbstractSCMTest):
def setUp(self):
AbstractSCMTest.setUp(self)
remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(remote_path)
# create a "remote" repo
subprocess.check_call(["hg", "init"], cwd=remote_path)
subprocess.check_call(["touch", "test.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "test.txt"], cwd=remote_path)
subprocess.check_call(["hg", "commit", "-m", "modified"], cwd=remote_path)
po = subprocess.Popen(["hg", "log", "--template", "'{node|short}'", "-l1"], cwd=remote_path, stdout=subprocess.PIPE)
self.version_init = po.stdout.read().decode('UTF-8').rstrip("'").lstrip("'")
subprocess.check_call(["hg", "tag", "footag"], cwd=remote_path)
subprocess.check_call(["touch", "test2.txt"], cwd=remote_path)
subprocess.check_call(["hg", "add", "test2.txt"], cwd=remote_path)
subprocess.check_call(["hg", "commit", "-m", "modified"], cwd=remote_path)
po = subprocess.Popen(["hg", "log", "--template", "'{node|short}'", "-l1"], cwd=remote_path, stdout=subprocess.PIPE)
self.version_end = po.stdout.read().decode('UTF-8').rstrip("'").lstrip("'")
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- hg: {local-name: clone, uri: ../remote}")
cmd = ["wstool", "update"]
os.chdir(self.local_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
def test_rosinstall_detailed_locapath_info(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'hg', 'default', '(-)', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens)
clone_path = os.path.join(self.local_path, "clone")
# make local modifications check
subprocess.check_call(["hg", "rm", "test2.txt"], cwd=clone_path)
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'hg', 'default', '(-)', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens)
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- hg: {local-name: clone, uri: ../remote, version: \"footag\"}")
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'hg', 'default', '(footag)', self.version_end, "(%s)" % self.version_init, os.path.join(self.test_root_path, 'remote')], tokens)
subprocess.check_call(["rm", "-rf", "clone"], cwd=self.local_path)
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'x', 'hg', '(footag)', os.path.join(self.test_root_path, 'remote')], tokens)
wstool-0.1.18/test/local/test_diff_functions_multi.py 0000664 0000000 0000000 00000035162 13542237257 0023045 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
from __future__ import unicode_literals
import os
import sys
from test.io_wrapper import StringIO
import wstool
import wstool.helpers
import wstool.wstool_cli
from wstool.wstool_cli import WstoolCLI
from wstool.wstool_cli import wstool_main
from test.scm_test_base import AbstractSCMTest, _add_to_file
from test.local.test_diff_functions_svn import create_svn_repo, modify_svn_repo
from test.local.test_diff_functions_git import create_git_repo, modify_git_repo
from test.local.test_diff_functions_hg import create_hg_repo, modify_hg_repo
from test.local.test_diff_functions_bzr import create_bzr_repo, modify_bzr_repo
class WstoolDiffMultiTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
remote_path_svn = os.path.join(self.test_root_path, "remote_svn")
remote_path_git = os.path.join(self.test_root_path, "remote_git")
remote_path_bzr = os.path.join(self.test_root_path, "remote_bzr")
remote_path_hg = os.path.join(self.test_root_path, "remote_hg")
os.makedirs(remote_path_git)
os.makedirs(remote_path_svn)
os.makedirs(remote_path_hg)
os.makedirs(remote_path_bzr)
filler_path = os.path.join(self.test_root_path, "filler")
svn_uri = "file://localhost"+remote_path_svn
create_svn_repo(self.test_root_path, remote_path_svn, filler_path, svn_uri)
create_git_repo(remote_path_git)
create_hg_repo(remote_path_hg)
create_bzr_repo(remote_path_bzr)
# wstool the remote repo and fake ros (using git twice to check all overlaps)
rosinstall_spec = """- other: {local-name: ../ros}
- git: {local-name: clone_git, uri: ../remote_git}
- svn: {local-name: clone_svn, uri: '%s'}
- hg: {local-name: clone_hg, uri: ../remote_hg}
- bzr: {local-name: clone_bzr, uri: ../remote_bzr}
- git: {local-name: clone_git2, uri: ../remote_git}""" % svn_uri
_add_to_file(os.path.join(self.local_path, ".rosinstall"), rosinstall_spec)
cmd = ["rosws", "update", "-t", "ws"]
os.chdir(self.test_root_path)
wstool_main(cmd)
clone_path_git = os.path.join(self.local_path, "clone_git")
clone_path_git2 = os.path.join(self.local_path, "clone_git2")
clone_path_svn = os.path.join(self.local_path, "clone_svn")
clone_path_hg = os.path.join(self.local_path, "clone_hg")
clone_path_bzr = os.path.join(self.local_path, "clone_bzr")
modify_git_repo(clone_path_git2)
modify_git_repo(clone_path_git)
modify_svn_repo(clone_path_svn)
modify_hg_repo(clone_path_hg)
modify_bzr_repo(clone_path_bzr)
def check_diff_output(self, output):
# this tests that there are proper newlines between diff outputs
# for svn, the order varies, so we check two known variants
self.assertTrue("\nIndex: clone_svn/added.txt" in output, output)
self.assertTrue("\nIndex: clone_svn/added.txt" in output, output)
self.assertTrue("\nIndex: clone_svn/modified.txt" in output, output)
self.assertTrue("\ndiff --git clone_hg/added.txt" in output, output)
self.assertTrue("\n=== added file 'added.txt'\n--- clone_bzr/added.txt" in output, output)
self.assertTrue("\ndiff --git clone_git2/added.txt" in output, output)
def test_multi_diff_rosinstall_outside(self):
'''Test wstool diff output from outside workspace.
In particular asserts that there are newlines between diffs, and no overlaps'''
cmd = ["wstool", "diff", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.check_diff_output(output)
def test_multi_diff_wstool_outside(self):
'''Test wstool diff output from outside workspace.
In particular asserts that there are newlines between diffs, and no overlaps'''
cmd = ["wstool", "diff", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), []))
def test_multi_diff_rosinstall_inside(self):
'''Test wstool diff output from inside workspace.
In particular asserts that there are newlines between diffs, and no overlaps'''
directory = self.test_root_path + "/ws"
cmd = ["wstool", "diff"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
self.check_diff_output(output)
def test_multi_diff_wstool_inside(self):
'''Test wstool diff output from inside workspace.
In particular asserts that there are newlines between diffs, and no overlaps'''
directory = self.test_root_path + "/ws"
cmd = ["wstool", "diff"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_multi_status_rosinstall_inside(self):
"""Test wstool status output when run inside workspace.
In particular asserts that there are newlines between statuses, and no overlaps"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "status"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
self.assertStatusListEqual('A clone_git/added.txt\n D clone_git/deleted-fs.txt\nD clone_git/deleted.txt\n M clone_git/modified-fs.txt\nM clone_git/modified.txt\nA clone_svn/added.txt\nD clone_svn/deleted.txt\n! clone_svn/deleted-fs.txt\nM clone_svn/modified.txt\nM clone_hg/modified-fs.txt\nM clone_hg/modified.txt\nA clone_hg/added.txt\nR clone_hg/deleted.txt\n! clone_hg/deleted-fs.txt\n+N clone_bzr/added.txt\n D clone_bzr/deleted-fs.txt\n-D clone_bzr/deleted.txt\n M clone_bzr/modified-fs.txt\n M clone_bzr/modified.txt\nA clone_git2/added.txt\n D clone_git2/deleted-fs.txt\nD clone_git2/deleted.txt\n M clone_git2/modified-fs.txt\nM clone_git2/modified.txt\n', output)
def test_multi_status_wstool_inside(self):
"""Test wstool status output when run inside workspace.
In particular asserts that there are newlines between statuses, and no overlaps"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "status"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.assertStatusListEqual('A clone_git/added.txt\n D clone_git/deleted-fs.txt\nD clone_git/deleted.txt\n M clone_git/modified-fs.txt\nM clone_git/modified.txt\nA clone_svn/added.txt\nD clone_svn/deleted.txt\n! clone_svn/deleted-fs.txt\nM clone_svn/modified.txt\nM clone_hg/modified-fs.txt\nM clone_hg/modified.txt\nA clone_hg/added.txt\nR clone_hg/deleted.txt\n! clone_hg/deleted-fs.txt\n+N clone_bzr/added.txt\n D clone_bzr/deleted-fs.txt\n-D clone_bzr/deleted.txt\n M clone_bzr/modified-fs.txt\n M clone_bzr/modified.txt\nA clone_git2/added.txt\n D clone_git2/deleted-fs.txt\nD clone_git2/deleted.txt\n M clone_git2/modified-fs.txt\nM clone_git2/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_multi_status_rosinstall_outside(self):
"""Test wstool status output when run outside workspace.
In particular asserts that there are newlines between statuses, and no overlaps"""
cmd = ["rosinstall", "status", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertStatusListEqual('A clone_git/added.txt\n D clone_git/deleted-fs.txt\nD clone_git/deleted.txt\n M clone_git/modified-fs.txt\nM clone_git/modified.txt\nA clone_svn/added.txt\nD clone_svn/deleted.txt\n! clone_svn/deleted-fs.txt\nM clone_svn/modified.txt\nM clone_hg/modified-fs.txt\nM clone_hg/modified.txt\nA clone_hg/added.txt\nR clone_hg/deleted.txt\n! clone_hg/deleted-fs.txt\n+N clone_bzr/added.txt\n D clone_bzr/deleted-fs.txt\n-D clone_bzr/deleted.txt\n M clone_bzr/modified-fs.txt\n M clone_bzr/modified.txt\nA clone_git2/added.txt\n D clone_git2/deleted-fs.txt\nD clone_git2/deleted.txt\n M clone_git2/modified-fs.txt\nM clone_git2/modified.txt\n', output)
def test_multi_status_wstool_outside(self):
"""Test wstool status output when run outside workspace.
In particular asserts that there are newlines between statuses, and no overlaps"""
cmd = ["wstool", "status", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertStatusListEqual('A clone_git/added.txt\n D clone_git/deleted-fs.txt\nD clone_git/deleted.txt\n M clone_git/modified-fs.txt\nM clone_git/modified.txt\nA clone_svn/added.txt\nD clone_svn/deleted.txt\n! clone_svn/deleted-fs.txt\nM clone_svn/modified.txt\nM clone_hg/modified-fs.txt\nM clone_hg/modified.txt\nA clone_hg/added.txt\nR clone_hg/deleted.txt\n! clone_hg/deleted-fs.txt\n+N clone_bzr/added.txt\n D clone_bzr/deleted-fs.txt\n-D clone_bzr/deleted.txt\n M clone_bzr/modified-fs.txt\n M clone_bzr/modified.txt\nA clone_git2/added.txt\n D clone_git2/deleted-fs.txt\nD clone_git2/deleted.txt\n M clone_git2/modified-fs.txt\nM clone_git2/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), []))
def test_multi_status_untracked(self):
'''tests status output for --untracked.
In particular asserts that there are newlines between statuses, and no overlaps'''
cmd = ["wstool", "status", "-t", "ws", "--untracked"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertStatusListEqual('A clone_git/added.txt\n D clone_git/deleted-fs.txt\nD clone_git/deleted.txt\n M clone_git/modified-fs.txt\nM clone_git/modified.txt\n?? clone_git/added-fs.txt\n? clone_svn/added-fs.txt\nA clone_svn/added.txt\nD clone_svn/deleted.txt\n! clone_svn/deleted-fs.txt\nM clone_svn/modified.txt\nM clone_hg/modified-fs.txt\nM clone_hg/modified.txt\nA clone_hg/added.txt\nR clone_hg/deleted.txt\n! clone_hg/deleted-fs.txt\n? clone_hg/added-fs.txt\n? clone_bzr/added-fs.txt\n+N clone_bzr/added.txt\n D clone_bzr/deleted-fs.txt\n-D clone_bzr/deleted.txt\n M clone_bzr/modified-fs.txt\n M clone_bzr/modified.txt\nA clone_git2/added.txt\n D clone_git2/deleted-fs.txt\nD clone_git2/deleted.txt\n M clone_git2/modified-fs.txt\nM clone_git2/modified.txt\n?? clone_git2/added-fs.txt\n', output)
cmd = ["wstool", "status", "-t", "ws", "--untracked"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertStatusListEqual('A clone_git/added.txt\n D clone_git/deleted-fs.txt\nD clone_git/deleted.txt\n M clone_git/modified-fs.txt\nM clone_git/modified.txt\n?? clone_git/added-fs.txt\n? clone_svn/added-fs.txt\nA clone_svn/added.txt\nD clone_svn/deleted.txt\n! clone_svn/deleted-fs.txt\nM clone_svn/modified.txt\nM clone_hg/modified-fs.txt\nM clone_hg/modified.txt\nA clone_hg/added.txt\nR clone_hg/deleted.txt\n! clone_hg/deleted-fs.txt\n? clone_hg/added-fs.txt\n? clone_bzr/added-fs.txt\n+N clone_bzr/added.txt\n D clone_bzr/deleted-fs.txt\n-D clone_bzr/deleted.txt\n M clone_bzr/modified-fs.txt\n M clone_bzr/modified.txt\nA clone_git2/added.txt\n D clone_git2/deleted-fs.txt\nD clone_git2/deleted.txt\n M clone_git2/modified-fs.txt\nM clone_git2/modified.txt\n?? clone_git2/added-fs.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"]))
wstool-0.1.18/test/local/test_diff_functions_svn.py 0000664 0000000 0000000 00000027147 13542237257 0022525 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
from __future__ import unicode_literals
import os
import sys
from test.io_wrapper import StringIO
import subprocess
import re
import wstool
import wstool.helpers
import wstool.wstool_cli
from wstool.wstool_cli import WstoolCLI
from wstool.wstool_cli import wstool_main
import test.scm_test_base
from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split
def create_svn_repo(test_root_path, remote_path, filler_path, svn_uri):
# create a "remote" repo
subprocess.check_call(["svnadmin", "create", remote_path], cwd=test_root_path)
subprocess.check_call(["svn", "checkout", svn_uri, filler_path], cwd=test_root_path)
subprocess.check_call(["touch", "fixed.txt"], cwd=filler_path)
subprocess.check_call(["touch", "modified.txt"], cwd=filler_path)
subprocess.check_call(["touch", "modified-fs.txt"], cwd=filler_path)
subprocess.check_call(["touch", "deleted.txt"], cwd=filler_path)
subprocess.check_call(["touch", "deleted-fs.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "fixed.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "modified.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "modified-fs.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "deleted.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "deleted-fs.txt"], cwd=filler_path)
subprocess.check_call(["svn", "commit", "-m", "modified"], cwd=filler_path)
def modify_svn_repo(clone_path):
# make local modifications
subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path)
subprocess.check_call(["svn", "rm", "deleted.txt"], cwd=clone_path)
#_add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n")
_add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n")
_add_to_file(os.path.join(clone_path, "added.txt"), "flam\n")
subprocess.check_call(["svn", "add", "--no-auto-props", "added.txt"], cwd=clone_path)
class WstoolDiffSvnTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
remote_path = os.path.join(self.test_root_path, "remote")
filler_path = os.path.join(self.test_root_path, "filler")
svn_uri = "file://localhost" + remote_path
create_svn_repo(self.test_root_path, remote_path, filler_path, svn_uri)
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- svn: {local-name: clone, uri: '" + svn_uri + "'}")
cmd = ["wstool", "update", "-t", "ws"]
os.chdir(self.test_root_path)
wstool_main(cmd)
clone_path = os.path.join(self.local_path, "clone")
modify_svn_repo(clone_path)
def check_diff_output(self, output):
# svn 1.9 added the "nonexistent" output, replace it with the
# revision 0 that the test results expect.
output_fixed = re.sub("\(nonexistent\)", "(revision 0)", output)
# svn output order varies between versions
expected = ["""\
Index: clone/added.txt
===================================================================
--- clone/added.txt\t(revision 0)
+++ clone/added.txt\t""",
"""@@ -0,0 +1 @@
+flam""",
"""\
Index: clone/modified.txt
===================================================================
--- clone/modified.txt\t(revision 1)
+++ clone/modified.txt\t(working copy)
@@ -0,0 +1 @@
+foo"""]
for snippet in expected:
for line in snippet.splitlines():
# assertIn is not supported in Python2.6
self.assertTrue(line in output_fixed, output)
def test_wstool_diff_svn_outside(self):
"""Test diff output for svn when run outside workspace"""
cmd = ["wstool", "diff", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_diff_svn_inside(self):
"""Test diff output for svn when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "diff"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.check_diff_output(output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(directory, []))
def test_wstool_status_svn_inside(self):
"""Test status output for svn when run inside workspace"""
directory = self.test_root_path + "/ws"
cmd = ["wstool", "status"]
os.chdir(directory)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
self.assertStatusListEqual('A clone/added.txt\nD clone/deleted.txt\n! clone/deleted-fs.txt\nM clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_diff(directory, []))
def test_wstool_status_svn_outside(self):
"""Test status output for svn when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertStatusListEqual('A clone/added.txt\nD clone/deleted.txt\n! clone/deleted-fs.txt\nM clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), []))
def test_wstool_status_svn_untracked(self):
"""Test status output for svn when run outside workspace"""
cmd = ["wstool", "status", "-t", "ws", "--untracked"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue()
self.assertStatusListEqual('? clone/added-fs.txt\nA clone/added.txt\nD clone/deleted.txt\n! clone/deleted-fs.txt\nM clone/modified.txt\n', output)
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"]))
def test_wstool_info_svn(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'svn'], tokens[0:3])
cli = WstoolCLI()
self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), []))
class WstoolInfoSvnTest(AbstractSCMTest):
def setUp(self):
AbstractSCMTest.setUp(self)
remote_path = os.path.join(self.test_root_path, "remote")
filler_path = os.path.join(self.test_root_path, "filler")
self.svn_uri = "file://localhost" + remote_path
# create a "remote" repo
subprocess.check_call(["svnadmin", "create", remote_path], cwd=self.test_root_path)
subprocess.check_call(["svn", "checkout", self.svn_uri, filler_path], cwd=self.test_root_path)
subprocess.check_call(["touch", "test.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "test.txt"], cwd=filler_path)
subprocess.check_call(["svn", "commit", "-m", "modified"], cwd=filler_path)
subprocess.check_call(["touch", "test2.txt"], cwd=filler_path)
subprocess.check_call(["svn", "add", "test2.txt"], cwd=filler_path)
subprocess.check_call(["svn", "commit", "-m", "modified"], cwd=filler_path)
self.version_init = "-r1"
self.version_end = "-r2"
# wstool the remote repo and fake ros
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- svn: {local-name: clone, uri: '" + self.svn_uri + "'}")
cmd = ["wstool", "update"]
os.chdir(self.local_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
sys.stdout = sys.__stdout__
def test_rosinstall_detailed_locapath_info(self):
cmd = ["wstool", "info", "-t", "ws"]
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'svn', self.version_end, self.svn_uri], tokens)
clone_path = os.path.join(self.local_path, "clone")
# make local modifications check
subprocess.check_call(["touch", "test3.txt"], cwd=clone_path)
subprocess.check_call(["svn", "add", "test3.txt"], cwd=clone_path)
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'M', 'svn', self.version_end, self.svn_uri], tokens)
subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path)
_add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- svn: {local-name: clone, uri: '" + self.svn_uri + "', version: \"1\"}")
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'MV', 'svn', '1', '(-)', self.version_end, "(%s)" % self.version_init, self.svn_uri], tokens)
subprocess.check_call(["rm", "-rf", "clone"], cwd=self.local_path)
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
output = output.getvalue()
tokens = _nth_line_split(-2, output)
self.assertEqual(['clone', 'x', 'svn', '(-)', self.svn_uri], tokens)
wstool-0.1.18/test/local/test_export.py 0000664 0000000 0000000 00000010522 13542237257 0020145 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import datetime
import os
import sys
import subprocess
from test.io_wrapper import StringIO
from wstool.wstool_cli import wstool_main
from test.scm_test_base import AbstractSCMTest, _add_to_file, get_git_hash
from test.local.test_diff_functions_git import create_git_repo, modify_git_repo
class WstoolExportTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
self.remote_path = os.path.join(self.test_root_path, 'remote')
self.new_remote_path = os.path.join(self.test_root_path, 'fooo')
self.version = 'master'
self.branch = 'test_branch'
self.date = datetime.date.today().isoformat()
os.makedirs(self.remote_path)
create_git_repo(self.remote_path)
# wstool the remote repo and fake ros
entry = '''\
- other: {local-name: ../ros}
- git: {local-name: clone, uri: ../remote, version: %s}
''' % self.version
_add_to_file(os.path.join(self.local_path, '.rosinstall'), entry)
cmd = ['wstool', 'update', '-t', 'ws']
os.chdir(self.test_root_path)
wstool_main(cmd)
self.clone_path = os.path.join(self.local_path, 'clone')
modify_git_repo(self.clone_path)
subprocess.check_call(['git', 'checkout', '-b', self.branch],
cwd=self.clone_path)
subprocess.check_call(['git', 'remote', 'set-url', 'origin',
self.new_remote_path], cwd=self.clone_path)
@staticmethod
def exp(*args):
return '''\
# THIS IS AN AUTOGENERATED FILE, LAST GENERATED USING wstool ON %s
- git:
local-name: clone
uri: %s
version: %s
''' % args
def helper(self, spec, exact, expected_output):
cmd = ['wstool', 'export', '-t', 'ws']
if spec:
cmd += ['--spec']
if exact:
cmd += ['--exact']
os.chdir(self.test_root_path)
sys.stdout = output = StringIO()
wstool_main(cmd)
sys.stdout = sys.__stdout__
output = output.getvalue().encode('utf-8')
expected_output = expected_output.encode('utf-8')
self.assertEqual(expected_output, output)
def test_wstool_export(self):
o = self.exp(self.date, self.new_remote_path, self.branch)
self.helper(False, False, o)
def test_wstool_export_spec(self):
o = self.exp(self.date, self.remote_path, self.version)
self.helper(True, False, o)
def test_wstool_export_exact(self):
uuid = get_git_hash(self.clone_path)
o = self.exp(self.date, self.new_remote_path, uuid)
self.helper(False, True, o)
def test_wstool_export_spec_exact(self):
uuid = get_git_hash(self.clone_path)
o = self.exp(self.date, self.remote_path, uuid)
self.helper(True, True, o)
wstool-0.1.18/test/local/test_interation.py 0000664 0000000 0000000 00000007664 13542237257 0021015 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import wstool
import wstool.multiproject_cmd
import wstool.ui
from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict
class FakeUi(wstool.ui.Ui):
def __init__(self, path='', mode='skip', prompt_result='y'):
self.path = path
self.mode = mode
def get_backup_path(self):
return path
def prompt_del_abort_retry(self, prompt, allow_skip=False):
return mode
def get_input(self, prompt):
return prompt_result
class RosinstallInteractive(AbstractFakeRosBasedTest):
"""tests with possible User Interaction, using mock to simulate user input"""
def setUp(self):
self.old_ui = wstool.ui.Ui.get_ui()
wstool.ui.Ui.set_ui(FakeUi())
def tearDown(self):
wstool.ui.Ui.set_ui(self.old_ui)
def test_twice_with_relpath(self):
"""runs wstool with generated self.simple_rosinstall to create local wstool env
and creates a directory for a second local wstool env"""
AbstractFakeRosBasedTest.setUp(self)
self.rel_uri_rosinstall = os.path.join(self.test_root_path, "rel_uri.rosinstall")
_create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path),
_create_config_elt_dict("git", "gitrepo", os.path.relpath(self.git_path, self.directory))],
self.rel_uri_rosinstall)
config = wstool.multiproject_cmd.get_config(self.directory, [self.rel_uri_rosinstall, self.ros_path])
wstool.multiproject_cmd.cmd_info(config)
wstool.multiproject_cmd.cmd_find_unmanaged_repos
wstool.multiproject_cmd.cmd_install_or_update(config)
config = wstool.multiproject_cmd.get_config(self.directory, [self.rel_uri_rosinstall, self.ros_path])
wstool.multiproject_cmd.cmd_install_or_update(config)
self.rel_uri_rosinstall2 = os.path.join(self.test_root_path, "rel_uri.wstool2")
# switch URIs to confuse config
_create_yaml_file([_create_config_elt_dict("git", "ros", os.path.relpath(self.git_path, self.directory)),
_create_config_elt_dict("git", "gitrepo", self.ros_path)],
self.rel_uri_rosinstall2)
config = wstool.multiproject_cmd.get_config(self.directory, [self.rel_uri_rosinstall, self.ros_path])
wstool.multiproject_cmd.cmd_install_or_update(config)
wstool-0.1.18/test/local/test_multiproject_functions.py 0000664 0000000 0000000 00000023772 13542237257 0023450 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import unittest
from wstool.common import DistributedWork, WorkerThread, normabspath,\
is_web_uri, select_elements, select_element, normalize_uri, realpath_relation,\
conditional_abspath, string_diff, MultiProjectException
class FooThing:
def __init__(self, el, result=None):
self.element = el
self.done = False
self.result = result
def do_work(self):
self.done = True
return self.result
def get_path_spec(self):
return self.element
def get_local_name(self):
return 'bar'
class MockElement:
def __init__(self, localname, path):
self.localname = localname
self.path = path
def get_local_name(self):
return self.localname
def get_path(self):
return self.path
class FunctionsTest(unittest.TestCase):
def test_normabspath(self):
base = "/foo/bar"
self.assertEqual("/foo/bar", normabspath('.', base))
self.assertEqual("/foo/bar", normabspath('foo/..', base))
self.assertEqual("/foo/bar", normabspath(base, base))
self.assertEqual("/foo", normabspath("/foo", base))
self.assertEqual("/foo/bar/bim", normabspath('bim', base))
self.assertEqual("/foo", normabspath('..', base))
def test_is_web_uri(self):
self.assertTrue(is_web_uri('http://foo.com'))
self.assertTrue(is_web_uri('http://foo.com/bar'))
self.assertTrue(is_web_uri('http://foo.com:42'))
self.assertTrue(is_web_uri('http://foo.com:42/bar'))
self.assertTrue(is_web_uri('ssh://foo.com'))
self.assertTrue(is_web_uri('lp:foo.com'))
self.assertTrue(is_web_uri('git://foo.com'))
self.assertTrue(is_web_uri('git+ssh://foo.com:foo'))
self.assertTrue(is_web_uri('user@foo:foo/bar'))
self.assertFalse(is_web_uri('foo/bar'))
self.assertFalse(is_web_uri('bar'))
self.assertFalse(is_web_uri(''))
self.assertFalse(is_web_uri(None))
def test_normalize_uri(self):
self.assertEqual('/foo', normalize_uri('/foo', None))
self.assertEqual(None, normalize_uri(None, None))
self.assertEqual('/bar/foo', normalize_uri('foo', '/bar'))
self.assertEqual('http://foo.com', normalize_uri('http://foo.com', None))
def test_string_diff(self):
self.assertEqual('', string_diff(None, None))
self.assertEqual('foo', string_diff('foo', 'foo'))
self.assertEqual('foo3', string_diff('foo', 'foo3'))
self.assertEqual(
'...7890foo3',
string_diff('12345678901234567890foo',
'12345678901234567890foo3'))
self.assertEqual(
'...7890foo3',
string_diff('12345678901234567890foo4',
'12345678901234567890foo3'))
self.assertEqual(
'...7890foo3',
string_diff('12345678901234567890foo45',
'12345678901234567890foo3'))
self.assertEqual(
'...4567890foo123456789123456789',
string_diff('12345678901234567890',
'12345678901234567890foo123456789123456789'))
self.assertEqual("['foo']", string_diff(['foo'], ['foo']))
self.assertEqual("['bar']", string_diff(['foo'], ['bar']))
def test_conditional_abspath(self):
path = "foo"
self.assertEqual(os.path.normpath(os.path.join(os.getcwd(), path)), conditional_abspath(path))
path = "http://someuri.com"
self.assertEqual("http://someuri.com", conditional_abspath(path))
def test_abspath_overlap(self):
base = "/foo/bar"
# simple
self.assertEqual('SAME_AS', realpath_relation("/foo", "/foo"))
self.assertEqual('SAME_AS', realpath_relation("/", "/"))
# denormalized
self.assertEqual('SAME_AS', realpath_relation("/foo/.", "/foo/bar/../"))
# subdir
self.assertEqual('PARENT_OF', realpath_relation("/foo", "/foo/bar/baz/bam"))
self.assertEqual('CHILD_OF', realpath_relation("/foo/bar/baz/bam", "/foo"))
## Negatives
self.assertEqual(None, realpath_relation("/foo", "/bar"))
self.assertEqual(None, realpath_relation("/foo", "/foo2"))
self.assertEqual(None, realpath_relation("/foo/bar", "/foo/ba"))
self.assertEqual(None, realpath_relation("/foo/ba", "/foo/bar/baz"))
self.assertEqual(None, realpath_relation("/foo/bar/baz", "/foo/ba"))
def test_select_element(self):
self.assertEqual(None, select_element(None, None))
self.assertEqual(None, select_element([], None))
mock1 = MockElement('foo', '/test/path1')
mock2 = MockElement('bar', '/test/path2')
mock3 = MockElement('baz', '/test/path3')
self.assertEqual(None, select_element([], 'pin'))
self.assertEqual(None, select_element([mock1], 'pin'))
self.assertEqual(None, select_element([mock1, mock3], 'pin'))
self.assertEqual('bar', select_element([mock1, mock2, mock3], 'bar').get_local_name())
self.assertEqual('bar', select_element([mock1, mock2, mock3], '/test/path2').get_local_name())
self.assertEqual('bar', select_element([mock1, mock2, mock3], '/test/../foo/../test/path2/').get_local_name())
def test_worker_thread(self):
try:
w = WorkerThread(None, None, None)
self.fail("expected Exception")
except MultiProjectException:
pass
try:
w = WorkerThread(FooThing(el=None), 2, 3)
self.fail("expected Exception")
except MultiProjectException:
pass
thing = FooThing(FooThing(None))
result = [None]
w = WorkerThread(thing, result, 0)
self.assertEqual(thing.done, False)
w.run()
self.assertEqual(thing.done, True, result)
self.assertEqual(True, 'error' in result[0])
thing = FooThing(FooThing(None), result={'done': True})
result = [None]
w = WorkerThread(thing, result, 0)
self.assertEqual(thing.done, False)
w.run()
self.assertEqual(thing.done, True, result)
self.assertEqual(False, 'error' in result[0], result)
def test_distributed_work_init(self):
work = DistributedWork(capacity=200)
self.assertEqual(10, work.num_threads)
work = DistributedWork(capacity=3, num_threads=5)
self.assertEqual(3, work.num_threads)
work = DistributedWork(capacity=5, num_threads=3)
self.assertEqual(3, work.num_threads)
work = DistributedWork(capacity=3, num_threads=-1)
self.assertEqual(3, work.num_threads)
def test_distributed_work(self):
work = DistributedWork(3)
thing1 = FooThing(FooThing(FooThing(None)), result={'done': True})
thing2 = FooThing(FooThing(FooThing(None)), result={'done': True})
thing3 = FooThing(FooThing(FooThing(None)), result={'done': True})
self.assertEqual(3, len(work.outputs))
work.add_thread(thing1)
self.assertEqual(1, len(work.threads))
work.add_thread(thing2)
self.assertEqual(2, len(work.threads))
work.add_thread(thing3)
self.assertEqual(3, len(work.threads))
self.assertEqual(thing1.done, False)
self.assertEqual(thing2.done, False)
self.assertEqual(thing3.done, False)
output = work.run()
self.assertEqual(False, 'error' in output[0], output)
self.assertEqual(False, 'error' in output[1], output)
self.assertEqual(False, 'error' in output[2], output)
def test_select_elements(self):
self.assertEqual([], select_elements(None, None))
mock1 = MockElement('foo', '/test/path1')
mock2 = MockElement('bar', '/test/path2')
mock3 = MockElement('baz', '/test/path3')
class FakeConfig():
def get_config_elements(self):
return [mock1, mock2, mock3]
def get_base_path(self):
return '/foo/bar'
self.assertEqual([mock1, mock2, mock3],
select_elements(FakeConfig(), None))
self.assertEqual([mock2],
select_elements(FakeConfig(), ['bar']))
self.assertEqual([mock1, mock2, mock3],
select_elements(FakeConfig(), ['/foo/bar']))
self.assertRaises(MultiProjectException, select_elements, FakeConfig(), ['bum'])
self.assertRaises(MultiProjectException, select_elements, FakeConfig(), ['foo', 'bum', 'bar'])
self.assertRaises(MultiProjectException, select_elements, FakeConfig(), ['bu*'])
wstool-0.1.18/test/local/test_rosinstall_options.py 0000664 0000000 0000000 00000014000 13542237257 0022564 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import copy
import tempfile
import wstool
from wstool.wstool_cli import wstool_main
from wstool.common import MultiProjectException
from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict, _create_git_repo
class RosinstallOptionsTest(AbstractFakeRosBasedTest):
"""Test command line option for failure behavior"""
@classmethod
def setUpClass(self):
AbstractFakeRosBasedTest.setUpClass()
# create another repo in git
self.git_path2 = os.path.join(self.test_root_path, "gitrepo2")
_create_git_repo(self.git_path2)
self.simple_changed_uri_rosinstall = os.path.join(self.test_root_path, "simple_changed_uri.rosinstall")
# same local name for gitrepo, different uri
_create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path),
_create_config_elt_dict("git", "gitrepo", self.git_path2)],
self.simple_changed_uri_rosinstall)
# create a broken config
self.broken_rosinstall = os.path.join(self.test_root_path, "broken.rosinstall")
_create_yaml_file([_create_config_elt_dict("other", self.ros_path),
_create_config_elt_dict("hg", "hgrepo", self.hg_path + "invalid")],
self.broken_rosinstall)
def test_Rosinstall_help(self):
cmd = copy.copy(self.wstool_fn)
cmd.append("-h")
self.assertEqual(0, wstool_main(cmd))
def test_rosinstall_delete_changes(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend(["init", self.directory, self.simple_rosinstall])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["merge", "-t", self.directory, self.simple_changed_uri_rosinstall, "--merge-replace", "-y"])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["update", "-t", self.directory, "--delete-changed-uri"])
self.assertEqual(0, wstool_main(cmd))
def test_rosinstall_abort_changes(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend(["init", self.directory, self.simple_rosinstall])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["merge", "-t", self.directory, self.simple_changed_uri_rosinstall, "--merge-replace", "-y"])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["update", "-t", self.directory, "--abort-changed-uri"])
try:
wstool_main(cmd)
self.fail("expected exception")
except MultiProjectException:
pass
def test_rosinstall_backup_changes(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend(["init", self.directory, self.simple_rosinstall])
self.assertEqual(0, wstool_main(cmd))
directory1 = tempfile.mkdtemp()
self.directories["backup1"] = directory1
cmd = copy.copy(self.wstool_fn)
cmd.extend(["merge", "-t", self.directory, self.simple_changed_uri_rosinstall, "--merge-replace", "-y"])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["update", "-t", self.directory, "--backup-changed-uri=%s" % directory1])
self.assertEqual(0, wstool_main(cmd))
self.assertEqual(len(os.listdir(directory1)), 1)
def test_rosinstall_change_vcs_type(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend(["init", self.directory, self.simple_rosinstall])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["merge", "-t", self.directory, self.simple_changed_vcs_rosinstall, "--merge-replace", "-y"])
self.assertEqual(0, wstool_main(cmd))
cmd = copy.copy(self.wstool_fn)
cmd.extend(["update", "-t", self.directory, "--delete-changed-uri"])
self.assertEqual(0, wstool_main(cmd))
def test_rosinstall_invalid_fail(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend([self.directory, "init", self.broken_rosinstall])
try:
wstool_main(cmd)
self.fail("expected exception")
except MultiProjectException:
pass
def test_rosinstall_invalid_continue(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend(["-t", self.directory, "merge", self.broken_rosinstall, "--continue-on-error"])
self.assertTrue(wstool_main(cmd))
wstool-0.1.18/test/local/test_rosinstall_standalone_functions.py 0000664 0000000 0000000 00000004161 13542237257 0025320 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
import os
import unittest
import subprocess
import wstool.helpers
from wstool.config import Config
from wstool.config_yaml import PathSpec
from mock import Mock
class FunctionsTest(unittest.TestCase):
def test_get_ros_package_path(self):
config = Config([PathSpec("foo"),
PathSpec("bar")],
".",
None)
self.assertEqual(list(map(os.path.abspath, ['bar',
'foo'])),
wstool.helpers.get_ros_package_path(config))
wstool-0.1.18/test/local/test_shallow_checkout_git.py 0000664 0000000 0000000 00000010705 13542237257 0023030 0 ustar 00root root 0000000 0000000 # Software License Agreement (BSD License)
#
# Copyright (c) 2009, Willow Garage, Inc.
# Copyright (c) 2017, wstool authors
# 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 Willow Garage, Inc. 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.
import os
import subprocess
from wstool.wstool_cli import wstool_main
from test.scm_test_base import AbstractSCMTest, _add_to_file
def create_git_repo(remote_path):
# create a "remote" repo
subprocess.check_call(["git", "init"], cwd=remote_path)
subprocess.check_call(["touch", "1.txt"], cwd=remote_path)
subprocess.check_call(["git", "add", "."], cwd=remote_path)
subprocess.check_call(["git", "commit", "-m", "first commit"], cwd=remote_path)
subprocess.check_call(["touch", "2.txt"], cwd=remote_path)
subprocess.check_call(["git", "add", "."], cwd=remote_path)
subprocess.check_call(["git", "commit", "-m", "second commit"], cwd=remote_path)
subprocess.check_call(["touch", "3.txt"], cwd=remote_path)
subprocess.check_call(["git", "add", "."], cwd=remote_path)
subprocess.check_call(["git", "commit", "-m", "third commit"], cwd=remote_path)
class WstoolShallowCheckoutGitTest(AbstractSCMTest):
@classmethod
def setUpClass(self):
AbstractSCMTest.setUpClass()
remote_path = os.path.join(self.test_root_path, "remote")
os.makedirs(remote_path)
create_git_repo(remote_path)
self.rosinstall_filename = os.path.join(self.local_path, "shallow-test.rosinstall")
_add_to_file(self.rosinstall_filename, "- git: {local-name: clone, uri: \"file://" + remote_path + "\"}")
cmd = ["wstool", "init", "ws-without-shallow", self.rosinstall_filename]
os.chdir(self.test_root_path)
wstool_main(cmd)
cmd = ["wstool", "init", "--shallow", "ws-with-shallow", self.rosinstall_filename]
os.chdir(self.test_root_path)
wstool_main(cmd)
def test_history_without_shallow(self):
"""Test history of cloned repo without shallow option"""
clone_path = os.path.join(self.test_root_path, "ws-without-shallow", "clone")
output = subprocess.check_output(["git", "log", "--pretty=format:%s"], cwd=clone_path)
self.assertEqual(output.decode("ascii"), "third commit\nsecond commit\nfirst commit")
def test_history_with_shallow(self):
"""Test history of cloned repo with shallow option"""
clone_path = os.path.join(self.test_root_path, "ws-with-shallow", "clone")
output = subprocess.check_output(["git", "log", "--pretty=format:%s"], cwd=clone_path)
self.assertEqual(output.decode("ascii"), "third commit")
def test_compare_workspace(self):
"""Compare worktrees with/without shallow option"""
clone_path_without_shallow = os.path.join(self.test_root_path, "ws-without-shallow", "clone")
clone_path_with_shallow = os.path.join(self.test_root_path, "ws-with-shallow", "clone")
output = subprocess.check_output(["diff", "--exclude=.git", clone_path_without_shallow, clone_path_with_shallow], cwd=self.test_root_path)
self.assertEqual(output.decode("ascii"), "")
wstool-0.1.18/test/local/test_tarfile.py 0000664 0000000 0000000 00000002711 13542237257 0020253 0 ustar 00root root 0000000 0000000 import os
import copy
import yaml
import wstool
import wstool.helpers
from wstool.wstool_cli import wstool_main
from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict, _create_tar_file
class RosinstallTarTest(AbstractFakeRosBasedTest):
"""Tests for tarfile support"""
@classmethod
def setUpClass(self):
AbstractFakeRosBasedTest.setUpClass()
# create another repo in git
self.tar_path = os.path.join(self.test_root_path, "tarfile.tar.bz2")
_create_tar_file(self.tar_path)
self.simple_tar_rosinstall = os.path.join(self.test_root_path, "simple_changed_uri.rosinstall")
# same local name for gitrepo, different uri
_create_yaml_file([_create_config_elt_dict("tar", "temptar", uri=self.tar_path, version='temptar')],
self.simple_tar_rosinstall)
def test_install(self):
cmd = copy.copy(self.wstool_fn)
cmd.extend(["init", self.directory, self.simple_tar_rosinstall])
self.assertEquals(0, wstool_main(cmd))
self.assertTrue(os.path.isdir(os.path.join(self.directory, "temptar")))
self.assertTrue(os.path.isfile(os.path.join(self.directory, ".rosinstall")))
stream = open(os.path.join(self.directory, '.rosinstall'), 'r')
yamlsrc = yaml.safe_load(stream)
stream.close()
self.assertEqual(1, len(yamlsrc))
self.assertEqual('tar', list(yamlsrc[0].keys())[0])
wstool-0.1.18/test/scm_test_base.py 0000664 0000000 0000000 00000026630 13542237257 0017315 0 ustar 00root root 0000000 0000000 # 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 Willow Garage, Inc. 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.
from __future__ import unicode_literals
import os
import copy
import unittest
import subprocess
import tempfile
import shutil
def _add_to_file(path, content):
"""Util function to append to file to get a modification"""
with open(path, 'ab') as fhand:
fhand.write(content.encode('UTF-8'))
def _create_fake_ros_dir(root_path):
"""setup fake ros root within root_path/ros"""
ros_path = os.path.join(root_path, "ros")
os.makedirs(ros_path)
bin_path = os.path.join(ros_path, "bin")
os.makedirs(bin_path)
subprocess.check_call(["git", "init"], cwd=ros_path)
_add_to_file(os.path.join(ros_path, "stack.xml"), '')
_add_to_file(os.path.join(ros_path, "setup.sh"), 'export FOO_BAR=`pwd`')
_add_to_file(os.path.join(bin_path, "rosmake"), '#!/usr/bin/env sh')
_add_to_file(os.path.join(bin_path, "rospack"), '#!/usr/bin/env sh')
# even faking rosmake
subprocess.check_call(["chmod", "u+x", os.path.join(bin_path, "rosmake")])
subprocess.check_call(["chmod", "u+x", os.path.join(bin_path, "rospack")])
subprocess.check_call(["git", "add", "*"], cwd=ros_path)
subprocess.check_call(["git", "commit", "-m", "initial"], cwd=ros_path)
def _create_yaml_file(config_elements, path):
content = ''
for elt in list(config_elements):
content += "- %s:\n" % elt["type"]
if elt["uri"] is not None:
content += " uri: '%s'\n" % elt["uri"]
content += " local-name: '%s'\n" % elt["local-name"]
if elt["version"] is not None:
content += " version: '%s'\n" % elt["version"]
_add_to_file(path, content)
def _create_config_elt_dict(scmtype, localname, uri=None, version=None):
element = {}
element["type"] = scmtype
element["uri"] = uri
element["local-name"] = localname
element["version"] = version
return element
def _create_git_repo(git_path):
os.makedirs(git_path)
subprocess.check_call(["git", "init"], cwd=git_path)
subprocess.check_call(["touch", "gitfixed.txt"], cwd=git_path)
subprocess.check_call(["git", "add", "*"], cwd=git_path)
subprocess.check_call(["git", "commit", "-m", "initial"], cwd=git_path)
def _create_tar_file(tar_file):
parent_path = os.path.dirname(tar_file)
tar_path = os.path.join(parent_path, 'temptar')
os.makedirs(tar_path)
subprocess.check_call(["touch", "tarfixed.txt"], cwd=tar_path)
subprocess.check_call(["tar", "-czf", os.path.basename(tar_file), 'temptar'], cwd=parent_path)
def _create_hg_repo(hg_path):
os.makedirs(hg_path)
subprocess.check_call(["hg", "init"], cwd=hg_path)
subprocess.check_call(["touch", "hgfixed.txt"], cwd=hg_path)
subprocess.check_call(["hg", "add", "hgfixed.txt"], cwd=hg_path)
subprocess.check_call(["hg", "commit", "-m", "initial"], cwd=hg_path)
def _nth_line_split(n, output):
"""returns the last line as list of non-blank tokens"""
lines = output.splitlines()
if len(lines) > 0:
return lines[n].split()
else:
return []
def get_git_hash(git_path):
po = subprocess.Popen(["git", "rev-parse", "HEAD"], cwd=git_path,
stdout=subprocess.PIPE)
return po.stdout.read().decode('UTF-8').rstrip('"\n').lstrip('"\n')
# ROSINSTALL_CMD = os.path.join(os.getcwd(), 'scripts/rosinstall')
# ROSWS_CMD = os.path.join(os.getcwd(), 'scripts/rosws')
class AbstractRosinstallCLITest(unittest.TestCase):
"""Base class for cli tests"""
@classmethod
def setUpClass(self):
os.environ['GIT_AUTHOR_NAME'] = 'Your Name'
os.environ['GIT_COMMITTER_NAME'] = 'Your Name'
os.environ['GIT_AUTHOR_EMAIL'] = 'name@example.com'
os.environ['EMAIL'] = 'Your Name '
self.new_environ = copy.copy(os.environ)
self.new_environ["PYTHONPATH"] = os.path.join(os.getcwd(), "src")
if "ROS_WORKSPACE" in self.new_environ:
self.new_environ.pop("ROS_WORKSPACE")
class AbstractRosinstallBaseDirTest(AbstractRosinstallCLITest):
"""test class where each test method get its own fresh tempdir named self.directory"""
def setUp(self):
self.directories = {}
self.directory = tempfile.mkdtemp()
self.directories["base"] = self.directory
self.wstool_fn = ["wstool"]
def tearDown(self):
for d in self.directories:
shutil.rmtree(self.directories[d])
self.directories = {}
class AbstractFakeRosBasedTest(AbstractRosinstallBaseDirTest):
"""
creates some larger infrastructure for testing locally:
a root folder containing all other folders in self.test_root_path
a fake ros folder in self.ros_path
a git repo in self.git_path
a hg repo in self.hg_path
a file named self.simple_rosinstall with ros and gitrepo
a file named self.simple_changed_vcs_rosinstall with ros and hgrepo
"""
@classmethod
def setUpClass(self):
AbstractRosinstallBaseDirTest.setUpClass()
# create a dir mimicking ros
self.test_root_path = os.path.realpath(tempfile.mkdtemp())
_create_fake_ros_dir(self.test_root_path)
# create a repo in git
self.ros_path = os.path.join(self.test_root_path, "ros")
self.git_path = os.path.join(self.test_root_path, "gitrepo")
_create_git_repo(self.git_path)
# create a repo in hg
self.hg_path = os.path.join(self.test_root_path, "hgrepo")
_create_hg_repo(self.hg_path)
# create custom wstool files to use as input
self.simple_rosinstall = os.path.join(self.test_root_path, "simple.rosinstall")
_create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path),
_create_config_elt_dict("git", "gitrepo", self.git_path)],
self.simple_rosinstall)
self.simple_changed_vcs_rosinstall = os.path.join(self.test_root_path, "simple_changed_vcs.rosinstall")
_create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path),
_create_config_elt_dict("hg", "hgrepo", self.hg_path)],
self.simple_changed_vcs_rosinstall)
@classmethod
def tearDownClass(self):
shutil.rmtree(self.test_root_path)
class AbstractSCMTest(AbstractRosinstallCLITest):
"""Base class for diff tests, setting up a tempdir self.test_root_path for a whole class"""
@classmethod
def setUpClass(self):
"""creates a directory 'ros' mimicking to be a ROS root to rosinstall"""
AbstractRosinstallCLITest.setUpClass()
self.test_root_path = os.path.realpath(tempfile.mkdtemp())
self.directories = {}
self.directories["root"] = self.test_root_path
_create_fake_ros_dir(self.test_root_path)
self.local_path = os.path.join(self.test_root_path, "ws")
os.makedirs(self.local_path)
self.curdir = os.getcwd()
@classmethod
def tearDownClass(self):
os.chdir(self.curdir)
for d in self.directories:
shutil.rmtree(self.directories[d])
def assertStatusListEqual(self, listexpect, listactual):
"""helper fun to check scm status output while discarding file ordering differences"""
lines_expect = listexpect.splitlines()
lines_actual = listactual.splitlines()
for line in lines_expect:
self.assertTrue(line in lines_actual, 'Missing entry %s in output %s' % (line, listactual))
for line in lines_actual:
self.assertTrue(line in lines_expect, 'Superflous entry %s in output %s' % (line, listactual))
class UtilTest(unittest.TestCase):
"""test to check the methods run by unit test setups"""
def test_add_to_file(self):
self.test_root_path = tempfile.mkdtemp()
filepath = os.path.join(self.test_root_path, 'foofile')
self.assertFalse(os.path.exists(filepath))
_add_to_file(filepath, 'foo')
self.assertTrue(os.path.exists(filepath))
with open(filepath, 'r') as f:
read_data = f.read()
self.assertEqual(read_data, 'foo')
_add_to_file(filepath, 'bar')
with open(filepath, 'r') as f:
read_data = f.read()
self.assertEqual(read_data, 'foobar')
shutil.rmtree(self.test_root_path)
def test_create_fake_ros(self):
self.test_root_path = tempfile.mkdtemp()
rospath = os.path.join(self.test_root_path, 'ros')
self.assertFalse(os.path.exists(rospath))
_create_fake_ros_dir(self.test_root_path)
self.assertTrue(os.path.exists(rospath))
self.assertTrue(os.path.exists(os.path.join(rospath, "setup.sh")))
self.assertTrue(os.path.exists(os.path.join(rospath, "stack.xml")))
self.assertTrue(os.path.exists(os.path.join(rospath, ".git")))
shutil.rmtree(self.test_root_path)
def test_create_config_elt_dict(self):
scmtype = 'foo'
uri = 'bar'
localname = 'pip'
version = 'pop'
element = _create_config_elt_dict(scmtype, localname, uri, version)
self.assertEqual(element["type"], scmtype)
self.assertEqual(element["uri"], uri)
self.assertEqual(element["local-name"], localname)
self.assertEqual(element["version"], version)
def test_create_yaml_file(self):
self.test_root_path = tempfile.mkdtemp()
filepath = os.path.join(self.test_root_path, 'foofile')
config_elements = [
_create_config_elt_dict("other", "foo"),
_create_config_elt_dict("git", "foo", "foouri"),
_create_config_elt_dict("svn", "bar", "baruri", "barversion")]
_create_yaml_file(config_elements, filepath)
with open(filepath, 'r') as f:
read_data = f.read()
self.assertEqual(read_data, """- other:
local-name: 'foo'
- git:
uri: 'foouri'
local-name: 'foo'
- svn:
uri: 'baruri'
local-name: 'bar'
version: 'barversion'
""")
shutil.rmtree(self.test_root_path)
wstool-0.1.18/tox.ini 0000664 0000000 0000000 00000001132 13542237257 0014452 0 ustar 00root root 0000000 0000000 # Tox is the QA gateway before releasing
# it can run against multiple python versions
# it runs commands in virtualenvs prepared with python version and dependencies from setup.[py,cfg]
# While tox can be configured to do plenty of things, it is bothersome to work with quickly, and it is not a lightweight dependency, so prefer to write build logic in setup.py or other commands that run without tox.
[tox]
envlist = py27, py34, py35, py36
[testenv]
# flawed due to https://github.com/tox-dev/tox/issues/149
# deps = -rrequirements.txt
commands =
pip install .[test]
{envpython} setup.py test