├── .gitignore ├── .travis.yml ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── completion ├── _wstool └── wstool-completion.bash ├── doc ├── Makefile ├── changelog.rst ├── conf.py ├── developers_guide.rst ├── index.rst ├── make.bat ├── rosinstall_file_format.rst └── wstool_usage.rst ├── requirements-test.txt ├── requirements.txt ├── scripts └── wstool ├── setup.cfg ├── setup.py ├── src └── wstool │ ├── __init__.py │ ├── __version__.py │ ├── cli_common.py │ ├── common.py │ ├── config.py │ ├── config_elements.py │ ├── config_yaml.py │ ├── helpers.py │ ├── multiproject_cli.py │ ├── multiproject_cmd.py │ ├── ui.py │ └── wstool_cli.py ├── stdeb.cfg ├── test ├── __init__.py ├── example.yaml ├── example_dirs │ ├── ros │ │ └── stack.xml │ ├── ros_comm │ │ └── stack.xml │ └── roscpp │ │ └── manifest.xml ├── io_wrapper.py ├── local │ ├── __init__.py │ ├── mock_client.py │ ├── test_cli.py │ ├── test_config.py │ ├── test_config_elt.py │ ├── test_config_yaml.py │ ├── test_diff_functions_bzr.py │ ├── test_diff_functions_git.py │ ├── test_diff_functions_hg.py │ ├── test_diff_functions_multi.py │ ├── test_diff_functions_svn.py │ ├── test_export.py │ ├── test_interation.py │ ├── test_multiproject_functions.py │ ├── test_rosinstall_options.py │ ├── test_rosinstall_standalone_functions.py │ ├── test_shallow_checkout_git.py │ └── test_tarfile.py └── scm_test_base.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *# 2 | *.DS_Store 3 | *.coverage 4 | *.deb 5 | *.egg-info 6 | *.eggs 7 | *.log 8 | *.orig 9 | *.pyc 10 | *.swp 11 | *.tgz 12 | *~ 13 | .tox 14 | build/* 15 | description-pak 16 | dist 17 | doc-pak 18 | nosetests.xml 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.5" 5 | - "3.6" 6 | 7 | # Add test requirements; travis will do the rest 8 | before_install: 9 | - cat requirements-test.txt >> requirements.txt 10 | 11 | install: 12 | - sudo apt-get update 13 | - pip install -U setuptools tox tox-travis 14 | - echo $PYTHONPATH 15 | - python -c 'import sys; print(sys.path)' 16 | - python setup.py install 17 | - python -c 'import coverage' 18 | 19 | # command to run tests 20 | script: 21 | - tox 22 | 23 | # Install rosinstall from pip and perform a sanity check 24 | - pip install --upgrade rosinstall wstool 25 | - rosws 26 | - wstool --help 27 | 28 | notifications: 29 | email: false 30 | after_success: 31 | - coveralls 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing guide 2 | ================== 3 | 4 | Thanks for your interest in contributing to wstool. 5 | 6 | Any kinds of contributions are welcome: Bug reports, Documentation, Patches. 7 | 8 | The core functionality of abstracting over different version control systems is contained in the library project https://github.com/vcstools/vcstools. 9 | 10 | Developer Environment 11 | --------------------- 12 | 13 | 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:: 14 | 15 | 1. (Optional) Install multiple python versions 16 | 17 | 1. (Optional) Install [pyenv](https://github.com/pyenv/pyenv-installer) to manage python versions 18 | 2. (Optional) Using pyenv, install the python versions used in testing:: 19 | 20 | pyenv install 2.7.16 21 | pyenv install 3.6.8 22 | 23 | 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 `_:: 24 | 25 | $ virtualenv ~/wstool_venv 26 | $ source ~/wstool_venv/bin/activate 27 | 28 | Testing 29 | ------- 30 | 31 | Prerequisites: 32 | 33 | * The tests require git, mercurial, bazaar and subversion to be installed. 34 | 35 | Also you need to install python test support libraries:: 36 | 37 | # install python dependencies 38 | $ pip install .[test] 39 | # optional also use local vcstools sources directly 40 | $ pip install --editable /path/to/vcstools_source 41 | 42 | Then you can use different commands to run various test scopes:: 43 | 44 | # run all tests using nose 45 | $ nosetests 46 | # run one test using nose 47 | $ nosetests {testname} 48 | # run all tests with coverage check 49 | $ python setup.py test 50 | # run all tests using python3 51 | $ python3 setup.py test 52 | # run all tests against multiple python versions (same as in travis) 53 | $ tox 54 | 55 | Releasing 56 | --------- 57 | 58 | * Upgrade vcstools dependency version in `requirements.txt` 59 | * Update `src/vcstools/__version__.py` 60 | * Check `doc/changelog` is up to date 61 | * Check `stdeb.cfg` is up to date with OSRF buildfarm distros 62 | * prepare release dependencies:: 63 | 64 | pip install --upgrade setuptools wheel twine 65 | 66 | * Upload to testpypi:: 67 | 68 | python3 setup.py sdist bdist_wheel 69 | twine upload --repository testpypi dist/* 70 | # in fresh virtualenv 71 | pip install -i https://test.pypi.org/simple/ wstool 72 | 73 | * Check testpypi download files and documentation look ok 74 | * Actually release:: 75 | 76 | twine upload dist/* 77 | 78 | * Create and push tag:: 79 | 80 | git tag x.y.z 81 | git push 82 | git push --tags 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2010, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py 2 | include LICENSE 3 | recursive-include completion * 4 | recursive-include doc * 5 | include requirements.txt 6 | include requirements-test.txt 7 | 8 | global-exclude .tox 9 | global-exclude *~ 10 | global-exclude __pycache__ 11 | global-exclude .coverage 12 | global-exclude *.py[co] 13 | global-exclude *.db 14 | global-exclude .git* 15 | global-exclude *.orig 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all setup clean_dist distro clean install testsetup test 2 | 3 | NAME='wstool' 4 | VERSION=$(shell grep version ./src/wstool/__version__.py | sed 's,version = ,,') 5 | 6 | OUTPUT_DIR=deb_dist 7 | 8 | 9 | all: 10 | echo "noop for debbuild" 11 | 12 | setup: 13 | echo "building version ${VERSION}" 14 | 15 | clean_dist: 16 | -rm -rf src/wstool.egg-info 17 | -rm -rf dist 18 | -rm -rf deb_dist 19 | 20 | distro: setup clean_dist 21 | python setup.py sdist 22 | 23 | clean: clean_dist 24 | 25 | 26 | install: distro 27 | sudo checkinstall python setup.py install 28 | 29 | testsetup: 30 | echo "running tests" 31 | 32 | test: testsetup 33 | nosetests --with-coverage --cover-package=wstool 34 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Archived 2 | ======== 3 | This repository has been archived due to a lack of maintainance and no longer being used in the ROS project. 4 | For similar functionality see http://wiki.ros.org/vcstool. 5 | 6 | wstool 7 | ========== 8 | 9 | .. image:: https://travis-ci.org/vcstools/wstool.svg?branch=master 10 | :target: https://travis-ci.org/vcstools/wstool 11 | 12 | .. image:: https://coveralls.io/repos/github/wstool/wstool/badge.svg?branch=master 13 | :target: https://coveralls.io/github/wstool/wstool?branch=master 14 | 15 | .. image:: https://img.shields.io/pypi/v/wstool.svg 16 | :target: https://pypi.python.org/pypi/wstool 17 | 18 | .. image:: https://img.shields.io/pypi/pyversions/wstool.svg 19 | :target: https://pypi.python.org/pypi/wstool 20 | 21 | .. image:: https://img.shields.io/pypi/status/wstool.svg 22 | :target: https://pypi.python.org/pypi/wstool 23 | 24 | .. image:: https://img.shields.io/pypi/l/wstool.svg 25 | :target: https://pypi.python.org/pypi/wstool 26 | 27 | .. image:: https://img.shields.io/pypi/dd/wstool.svg 28 | :target: https://pypi.python.org/pypi/wstool 29 | 30 | .. image:: https://img.shields.io/pypi/dw/wstool.svg 31 | :target: https://pypi.python.org/pypi/wstool 32 | 33 | .. image:: https://img.shields.io/pypi/dm/wstool.svg 34 | :target: https://pypi.python.org/pypi/wstool 35 | 36 | Command-line tools for maintaining a workspace of projects from multiple version-control systems. 37 | 38 | Also see http://wiki.ros.org/wstool. 39 | 40 | Installing 41 | ---------- 42 | 43 | Using the pypi package:: 44 | 45 | $ pip install -U wstool 46 | -------------------------------------------------------------------------------- /completion/_wstool: -------------------------------------------------------------------------------- 1 | #compdef wstool 2 | # Software License Agreement (BSD License) 3 | # 4 | # Copyright (c) 2010, Willow Garage, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # * Neither the name of Willow Garage, Inc. nor the names of its 18 | # contributors may be used to endorse or promote products derived 19 | # from this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | 34 | _wstool () { 35 | local e 36 | e=$(dirname ${funcsourcetrace[1]%:*})/wstool-completion.bash 37 | if [ -f $e ]; then 38 | . $e 39 | fi 40 | } 41 | -------------------------------------------------------------------------------- /completion/wstool-completion.bash: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2010, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | # Programmable completion for the wstool command under bash. Source 34 | # this file (or on some systems add it to ~/.bash_completion and start a new 35 | # shell) 36 | 37 | # ZSH support 38 | if [[ -n ${ZSH_VERSION-} ]]; then 39 | autoload -U +X bashcompinit && bashcompinit 40 | fi 41 | 42 | # put here to be extendable 43 | if [ -z "$WSTOOL_BASE_COMMANDS" ]; then 44 | _WSTOOL_BASE_COMMANDS="help init set merge info remove diff status update export --version" 45 | fi 46 | 47 | # Based originally on the bzr/svn bash completition scripts. 48 | _wstool_complete() 49 | { 50 | local cur cmds cmdOpts opt helpCmds optBase i 51 | 52 | COMPREPLY=() 53 | cur=${COMP_WORDS[COMP_CWORD]} 54 | 55 | cmds=$_WSTOOL_BASE_COMMANDS 56 | 57 | if [[ $COMP_CWORD -eq 1 ]] ; then 58 | COMPREPLY=( $( compgen -W "$cmds" -- $cur ) ) 59 | return 0 60 | fi 61 | 62 | # if not typing an option, or if the previous option required a 63 | # parameter, then fallback on ordinary filename expansion 64 | helpCmds='help|--help|h|\?' 65 | if [[ ${COMP_WORDS[1]} != @@($helpCmds) ]] && \ 66 | [[ "$cur" != -* ]] ; then 67 | case ${COMP_WORDS[1]} in 68 | info|diff|di|status|st|remove|rm|update|up) 69 | cmdOpts=`wstool info --only=localname 2> /dev/null | sed 's,:, ,g'` 70 | COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) 71 | ;; 72 | set) 73 | if [[ $COMP_CWORD -eq 2 ]]; then 74 | cmdOpts=`wstool info --only=localname 2> /dev/null | sed 's,:, ,g'` 75 | COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) 76 | elif [[ $COMP_CWORD -eq 3 ]]; then 77 | cmdOpts=`wstool info ${COMP_WORDS[2]} --only=uri 2> /dev/null` 78 | COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) 79 | else 80 | if [[ ${COMP_WORDS[$(( $COMP_CWORD - 1 ))]} == "--version-new" ]]; then 81 | cmdOpts=`wstool info ${COMP_WORDS[2]} --only=version 2> /dev/null|sed 's/,$//'` 82 | COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) 83 | fi 84 | fi 85 | ;; 86 | esac 87 | return 0 88 | fi 89 | 90 | cmdOpts= 91 | case ${COMP_WORDS[1]} in 92 | status|st) 93 | cmdOpts="-t --target-workspace --untracked" 94 | ;; 95 | diff|di) 96 | cmdOpts="-t --target-workspace" 97 | ;; 98 | init) 99 | cmdOpts="-t --target-workspace --continue-on-error" 100 | ;; 101 | merge) 102 | cmdOpts="-t --target-workspace -y --confirm-all -r --merge-replace -k --merge-keep -a --merge-kill-append" 103 | ;; 104 | set) 105 | cmdOpts="-t --target-workspace --git --svn --bzr --hg --uri -v --version-new --detached -y --confirm" 106 | ;; 107 | remove|rm) 108 | cmdOpts="-t --target-workspace" 109 | ;; 110 | update|up) 111 | cmdOpts="-t --target-workspace --delete-changed-uris --abort-changed-uris --backup-changed-uris" 112 | ;; 113 | export) 114 | cmdOpts="-t --target-workspace -o --output --exact --spec" 115 | ;; 116 | info) 117 | cmdOpts="-t --target-workspace --data-only --no-pkg-path --pkg-path-only --only --yaml -u --untracked --fetch -s --short --root -m --managed-only" 118 | ;; 119 | *) 120 | ;; 121 | esac 122 | 123 | cmdOpts="$cmdOpts --help -h" 124 | 125 | # take out options already given 126 | for (( i=2; i<=$COMP_CWORD-1; ++i )) ; do 127 | opt=${COMP_WORDS[$i]} 128 | 129 | case $opt in 130 | --*) optBase=${opt/=*/} ;; 131 | -*) optBase=${opt:0:2} ;; 132 | esac 133 | 134 | cmdOpts=" $cmdOpts " 135 | cmdOpts=${cmdOpts/ ${optBase} / } 136 | 137 | # take out alternatives 138 | case $optBase in 139 | -h) cmdOpts=${cmdOpts/ --help / } ;; 140 | --help) cmdOpts=${cmdOpts/ -h / } ;; 141 | -t) cmdOpts=${cmdOpts/ --target-workspace / } ;; 142 | --target-workspace) cmdOpts=${cmdOpts/ -t / } ;; 143 | --delete-changed-uris) 144 | cmdOpts=${cmdOpts/ --abort-changed-uris / } 145 | cmdOpts=${cmdOpts/ --backup-changed-uris / } 146 | ;; 147 | --abort-changed-uris) 148 | cmdOpts=${cmdOpts/ --delete-changed-uris / } 149 | cmdOpts=${cmdOpts/ --backup-changed-uris / } 150 | ;; 151 | --backup-changed-uris) 152 | cmdOpts=${cmdOpts/ --delete-changed-uris / } 153 | cmdOpts=${cmdOpts/ --abort-changed-uris / } 154 | ;; 155 | # scm options 156 | --svn) 157 | cmdOpts=${cmdOpts/ --git / } 158 | cmdOpts=${cmdOpts/ --hg / } 159 | cmdOpts=${cmdOpts/ --bzr / } 160 | cmdOpts=${cmdOpts/ --detached / } 161 | ;; 162 | --git) 163 | cmdOpts=${cmdOpts/ --svn / } 164 | cmdOpts=${cmdOpts/ --hg / } 165 | cmdOpts=${cmdOpts/ --bzr / } 166 | cmdOpts=${cmdOpts/ --detached / } 167 | ;; 168 | --hg) 169 | cmdOpts=${cmdOpts/ --git / } 170 | cmdOpts=${cmdOpts/ --svn / } 171 | cmdOpts=${cmdOpts/ --bzr / } 172 | cmdOpts=${cmdOpts/ --detached / } 173 | ;; 174 | --bzr) 175 | cmdOpts=${cmdOpts/ --git / } 176 | cmdOpts=${cmdOpts/ --hg / } 177 | cmdOpts=${cmdOpts/ --svn / } 178 | cmdOpts=${cmdOpts/ --detached / } 179 | ;; 180 | --detached) 181 | cmdOpts=${cmdOpts/ --git / } 182 | cmdOpts=${cmdOpts/ --hg / } 183 | cmdOpts=${cmdOpts/ --bzr / } 184 | cmdOpts=${cmdOpts/ --svn / } 185 | ;; 186 | # merge options 187 | --merge-replace) 188 | cmdOpts=${cmdOpts/ --merge-keep / } 189 | cmdOpts=${cmdOpts/ --merge-kill-append / } 190 | cmdOpts=${cmdOpts/ -r / } 191 | cmdOpts=${cmdOpts/ -a / } 192 | cmdOpts=${cmdOpts/ -k / } 193 | ;; 194 | --merge-keep) 195 | cmdOpts=${cmdOpts/ --merge-replace / } 196 | cmdOpts=${cmdOpts/ --merge-kill-append / } 197 | cmdOpts=${cmdOpts/ -r / } 198 | cmdOpts=${cmdOpts/ -a / } 199 | cmdOpts=${cmdOpts/ -k / } 200 | ;; 201 | --merge-kill-append) 202 | cmdOpts=${cmdOpts/ --merge-keep / } 203 | cmdOpts=${cmdOpts/ --merge-replace / } 204 | cmdOpts=${cmdOpts/ -r / } 205 | cmdOpts=${cmdOpts/ -a / } 206 | cmdOpts=${cmdOpts/ -k / } 207 | ;; 208 | -r) 209 | cmdOpts=${cmdOpts/ --merge-keep / } 210 | cmdOpts=${cmdOpts/ --merge-kill-append / } 211 | cmdOpts=${cmdOpts/ --merge-replace / } 212 | cmdOpts=${cmdOpts/ -a / } 213 | cmdOpts=${cmdOpts/ -k / } 214 | ;; 215 | -a) 216 | cmdOpts=${cmdOpts/ --merge-keep / } 217 | cmdOpts=${cmdOpts/ --merge-kill-append / } 218 | cmdOpts=${cmdOpts/ --merge-replace / } 219 | cmdOpts=${cmdOpts/ -r / } 220 | cmdOpts=${cmdOpts/ -k / } 221 | ;; 222 | -k) 223 | cmdOpts=${cmdOpts/ --merge-keep / } 224 | cmdOpts=${cmdOpts/ --merge-kill-append / } 225 | cmdOpts=${cmdOpts/ --merge-replace / } 226 | cmdOpts=${cmdOpts/ -a / } 227 | cmdOpts=${cmdOpts/ -r / } 228 | ;; 229 | esac 230 | 231 | # skip next option if this one requires a parameter 232 | if [[ $opt == @@($optsParam) ]] ; then 233 | ((++i)) 234 | fi 235 | done 236 | 237 | COMPREPLY=( $( compgen -W "$cmdOpts" -- $cur ) ) 238 | 239 | return 0 240 | 241 | } 242 | complete -F _wstool_complete -o default wstool 243 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(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/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/wstool.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wstool.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/wstool" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wstool" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | upload: html 170 | # set write permission for group so that everybody can overwrite existing files on the webserver 171 | chmod -R g+w _build/html/ 172 | scp -pr _build/html/ rosbot@ros.osuosl.org:/home/rosbot/docs/independent/api/wstool 173 | 174 | xml: 175 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 176 | @echo 177 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 178 | 179 | pseudoxml: 180 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 181 | @echo 182 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 183 | -------------------------------------------------------------------------------- /doc/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 0.1.18 5 | ------ 6 | 7 | - fix warnings by replacing yaml.load() with safe_load() 8 | - Re-add snapshot command called 'export' (#117, #120) 9 | - fix '-t' option not working for wstool remove 10 | - upgrade vcstools library version to 0.1.41, with new fixes: 11 | 12 | - fix git submodule errors 13 | - fix export_upstream for git submodules 14 | - fix python3 incompatibility 15 | - fix git fast-forward failures 16 | - fix get_affected_files 17 | 18 | 0.1.17 19 | ------ 20 | 21 | - Reverted the snapshot command since it was breaking ``rosws`` until it can be fixed. 22 | 23 | 0.1.16 24 | ------ 25 | 26 | - Fixed some issues with new ``requirements.txt`` usage during the release process. 27 | 28 | 0.1.15 29 | ------ 30 | 31 | - Fixed an issue with the conditional dependency on ``ordereddict`` in the ``setup.py`` file. 32 | 33 | 0.1.14 34 | ------ 35 | 36 | - Fixed an issue which caused a traceback with invalid command line options. 37 | - Added a feature to "snapshot" the current commit hashes in the workspace as a rosinstall file. 38 | - Fixed option handling and documentation of the ``--untracked`` option. 39 | - Added ``--shallow`` option to ``wstool init`` for shallow checkouts with git. 40 | - Contributors: @cbandera, @rsinnet, @amiller27, @jayvdb, @at-wat 41 | 42 | 0.1.13 43 | ------ 44 | 45 | - Fix to avoid errors due to installing man pages with OS X's 10.11's new SIP settings. 46 | - Added option to show a simplified version info table. 47 | - Added the -m (timeout), -v (verbose), and -j (parallel jobs) options to each command. 48 | - Contributors: @NikolausDemmel, @wkentaro 49 | 50 | 0.1.12 51 | ------ 52 | 53 | - Fix command line arguments of ``wstool scrape``. 54 | 55 | 0.1.11 56 | ------ 57 | 58 | - Changed the way ``.bak`` files are created when overwriting existing configurations. 59 | - Added the Scrape command. 60 | - Added default git branch and status to ``wstool fetch --info``. 61 | - Added versioned dependency on vcstools ``0.1.38`` to make use of new API features. 62 | 63 | 0.1.10 64 | ------ 65 | 66 | - Fix regression which broke the -j option. 67 | - Enable pretty printing of the ``.rosinstall`` file's YAML. 68 | 69 | 0.1.9 70 | ----- 71 | 72 | - Fix for zsh completion. 73 | - Fixed version dependency on vcstools for debian. 74 | 75 | 0.1.8 76 | ----- 77 | 78 | - Fix for installation issue. 79 | 80 | 0.1.7 81 | ----- 82 | 83 | - Added installation of generated man pages. 84 | - Added installation of shell completion for wstool. 85 | - Improved output of wstool info with the new get_current_version_label in vcstools. 86 | - Added a foreach command. 87 | - Added a ``--root`` option to wstool info. 88 | - Enhanced the ``--update`` option for wstool set. 89 | - Now uses multiple threads for network operations by default. 90 | - Some other minor fixes and improvements and docs. 91 | 92 | 0.1.5 93 | ----- 94 | 95 | - Releasing to allow changes for new platform vivid. 96 | - Fix svn diff for change in output with svn 1.7.9. 97 | - info command shows information about unmanaged paths. 98 | 99 | 0.1.4 100 | ----- 101 | 102 | - Fix detection of path conflicts #24 (https://github.com/vcstools/wstool/pull/24). 103 | 104 | 0.0.3 105 | ----- 106 | 107 | - not using ROS_WORKSPACE anymore 108 | - fix to "wstool cmd --help" 109 | 110 | 0.0.2 111 | ----- 112 | 113 | - fix #2 creating "wstool2 file instaed of ".rosinstall" 114 | 115 | 0.0.1 116 | ----- 117 | 118 | - Initial creation based on functions inrosinstall 119 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # wstool documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Aug 11 08:44:18 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | import imp 19 | 20 | file = None 21 | try: 22 | file, pathname, description = imp.find_module('__version__', ['../src/wstool']) 23 | vermod = imp.load_module('__version__', file, pathname, description) 24 | version = vermod.version 25 | finally: 26 | if file is not None: 27 | file.close() 28 | 29 | # If extensions (or modules to document with autodoc) are in another directory, 30 | # add these directories to sys.path here. If the directory is relative to the 31 | # documentation root, use os.path.abspath to make it absolute, like shown here. 32 | 33 | # -- General configuration ------------------------------------------------ 34 | 35 | # If your documentation needs a minimal Sphinx version, state it here. 36 | # needs_sphinx = '1.0' 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be extensions 39 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 40 | extensions = ['sphinx.ext.intersphinx'] 41 | 42 | # Add any paths that contain templates here, relative to this directory. 43 | templates_path = ['_templates'] 44 | 45 | # The suffix of source filenames. 46 | source_suffix = '.rst' 47 | 48 | # The encoding of source files. 49 | # source_encoding = 'utf-8-sig' 50 | 51 | # The master toctree document. 52 | master_doc = 'index' 53 | 54 | # General information about the project. 55 | project = u'wstool' 56 | copyright = u'2011, Willow Garage' 57 | 58 | # The version info for the project you're documenting, acts as replacement for 59 | # |version| and |release|, also used in various other places throughout the 60 | # built documents. 61 | # 62 | # The short X.Y version. 63 | version = version 64 | # The full version, including alpha/beta/rc tags. 65 | release = version 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # language = None 70 | 71 | # There are two options for replacing |today|: either, you set today to some 72 | # non-false value, then it is used: 73 | # today = '' 74 | # Else, today_fmt is used as the format for a strftime call. 75 | # today_fmt = '%B %d, %Y' 76 | 77 | # List of patterns, relative to source directory, that match files and 78 | # directories to ignore when looking for source files. 79 | exclude_patterns = ['_build'] 80 | 81 | # The reST default role (used for this markup: `text`) to use for all documents. 82 | # default_role = None 83 | 84 | # If true, '()' will be appended to :func: etc. cross-reference text. 85 | # add_function_parentheses = True 86 | 87 | # If true, the current module name will be prepended to all description 88 | # unit titles (such as .. function::). 89 | # add_module_names = True 90 | 91 | # If true, sectionauthor and moduleauthor directives will be shown in the 92 | # output. They are ignored by default. 93 | # show_authors = False 94 | 95 | # The name of the Pygments (syntax highlighting) style to use. 96 | pygments_style = 'sphinx' 97 | 98 | # A list of ignored prefixes for module index sorting. 99 | # modindex_common_prefix = [] 100 | 101 | 102 | # -- Options for HTML output --------------------------------------------- 103 | 104 | # The theme to use for HTML and HTML Help pages. See the documentation for 105 | # a list of builtin themes. 106 | html_theme = 'haiku' 107 | 108 | # Theme options are theme-specific and customize the look and feel of a theme 109 | # further. For a list of options available for each theme, see the 110 | # documentation. 111 | # html_theme_options = {} 112 | 113 | # Add any paths that contain custom themes here, relative to this directory. 114 | # html_theme_path = [] 115 | 116 | # The name for this set of Sphinx documents. If None, it defaults to 117 | # " v documentation". 118 | # html_title = None 119 | 120 | # A shorter title for the navigation bar. Default is the same as html_title. 121 | # html_short_title = None 122 | 123 | # The name of an image file (relative to this directory) to place at the top 124 | # of the sidebar. 125 | # html_logo = None 126 | 127 | # The name of an image file (within the static path) to use as favicon of the 128 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 129 | # pixels large. 130 | # html_favicon = None 131 | 132 | # Add any paths that contain custom static files (such as style sheets) here, 133 | # relative to this directory. They are copied after the builtin static files, 134 | # so a file named "default.css" will overwrite the builtin "default.css". 135 | html_static_path = ['_static'] 136 | 137 | # Add any extra paths that contain custom files (such as robots.txt or 138 | # .htaccess) here, relative to this directory. These files are copied 139 | # directly to the root of the documentation. 140 | #html_extra_path = [] 141 | 142 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 143 | # using the given strftime format. 144 | # html_last_updated_fmt = '%b %d, %Y' 145 | 146 | # If true, SmartyPants will be used to convert quotes and dashes to 147 | # typographically correct entities. 148 | # html_use_smartypants = True 149 | 150 | # Custom sidebar templates, maps document names to template names. 151 | # html_sidebars = {} 152 | 153 | # Additional templates that should be rendered to pages, maps page names to 154 | # template names. 155 | # html_additional_pages = {} 156 | 157 | # If false, no module index is generated. 158 | # html_domain_indices = True 159 | 160 | # If false, no index is generated. 161 | # html_use_index = True 162 | 163 | # If true, the index is split into individual pages for each letter. 164 | # html_split_index = False 165 | 166 | # If true, links to the reST sources are added to the pages. 167 | # html_show_sourcelink = True 168 | 169 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 170 | # html_show_sphinx = True 171 | 172 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 173 | # html_show_copyright = True 174 | 175 | # If true, an OpenSearch description file will be output, and all pages will 176 | # contain a tag referring to it. The value of this option must be the 177 | # base URL from which the finished HTML is served. 178 | # html_use_opensearch = '' 179 | 180 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 181 | # html_file_suffix = None 182 | 183 | # Output file base name for HTML help builder. 184 | htmlhelp_basename = 'wstooldoc' 185 | 186 | 187 | # -- Options for LaTeX output --------------------------------------------- 188 | 189 | latex_elements = { 190 | # The paper size ('letterpaper' or 'a4paper'). 191 | #'papersize': 'letterpaper', 192 | 193 | # The font size ('10pt', '11pt' or '12pt'). 194 | #'pointsize': '10pt', 195 | 196 | # Additional stuff for the LaTeX preamble. 197 | #'preamble': '', 198 | } 199 | 200 | # Grouping the document tree into LaTeX files. List of tuples 201 | # (source start file, target name, title, 202 | # author, documentclass [howto, manual, or own class]). 203 | latex_documents = [ 204 | ('index', 'wstool.tex', u'wstool Documentation', 205 | u'Tully Foote, Thibault Kruse, Ken Conley, Brian Gerkey', 'manual'), 206 | ] 207 | 208 | # The name of an image file (relative to this directory) to place at the top of 209 | # the title page. 210 | # latex_logo = None 211 | 212 | # For "manual" documents, if this is true, then toplevel headings are parts, 213 | # not chapters. 214 | # latex_use_parts = False 215 | 216 | # If true, show page references after internal links. 217 | # latex_show_pagerefs = False 218 | 219 | # If true, show URL addresses after external links. 220 | # latex_show_urls = False 221 | 222 | # Additional stuff for the LaTeX preamble. 223 | # latex_preamble = '' 224 | 225 | # Documents to append as an appendix to all manuals. 226 | # latex_appendices = [] 227 | 228 | # If false, no module index is generated. 229 | # latex_domain_indices = True 230 | 231 | 232 | # -- Options for manual page output --------------------------------------- 233 | 234 | # One entry per manual page. List of tuples 235 | # (source start file, name, description, authors, manual section). 236 | man_pages = [ 237 | ('index', 'wstool', u'wstool Documentation', 238 | [u'Tully Foote, Thibault Kruse, Ken Conley, Brian Gerkey'], 1) 239 | ] 240 | 241 | # If true, show URL addresses after external links. 242 | #man_show_urls = False 243 | 244 | 245 | # -- Options for Texinfo output ------------------------------------------- 246 | 247 | # Grouping the document tree into Texinfo files. List of tuples 248 | # (source start file, target name, title, author, 249 | # dir menu entry, description, category) 250 | texinfo_documents = [ 251 | ('index', 'wstool', u'wstool Documentation', 252 | u'Tully Foote, Thibault Kruse, Ken Conley, Brian Gerkey', 'wstool', 'One line description of project.', 253 | 'Miscellaneous'), 254 | ] 255 | 256 | # Documents to append as an appendix to all manuals. 257 | #texinfo_appendices = [] 258 | 259 | # If false, no module index is generated. 260 | #texinfo_domain_indices = True 261 | 262 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 263 | #texinfo_show_urls = 'footnote' 264 | 265 | # If true, do not generate a @detailmenu in the "Top" node's menu. 266 | #texinfo_no_detailmenu = False 267 | 268 | 269 | # Example configuration for intersphinx: refer to the Python standard library. 270 | intersphinx_mapping = {'http://docs.python.org/': None} 271 | -------------------------------------------------------------------------------- /doc/developers_guide.rst: -------------------------------------------------------------------------------- 1 | Developer's Guide 2 | ================= 3 | 4 | Changelog 5 | --------- 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | changelog 11 | 12 | Bug reports and feature requests 13 | -------------------------------- 14 | 15 | - `Submit a bug report `_ 16 | 17 | Developer Setup 18 | --------------- 19 | 20 | The wstool source can be downloaded using Mercurial:: 21 | 22 | $ git clone https://github.com/vcstools/wstool.git 23 | 24 | You will also need vcstools, which you can either install using pip or download using:: 25 | 26 | $ git clone https://github.com/vcstools/vcstools.git 27 | $ cd vcstools 28 | $ python develop 29 | 30 | 31 | wstool uses `setuptools `_, 32 | which you will need to download and install in order to run the 33 | packaging. We use setuptools instead of distutils in order to be able 34 | use ``setup()`` keys like ``install_requires``. 35 | 36 | Configure your environment: 37 | 38 | $ cd wstool 39 | $ python develop 40 | 41 | Testing 42 | ------- 43 | 44 | Install test dependencies 45 | 46 | :: 47 | 48 | $ pip install nose 49 | $ pip install mock 50 | 51 | 52 | wstool uses `Python nose 53 | `_ for testing, which is 54 | a fairly simple and straightforward test framework. The wstool 55 | mainly use :mod:`unittest` to construct test fixtures, but with nose 56 | you can also just write a function that starts with the name ``test`` 57 | and use normal ``assert`` statements. 58 | 59 | wstool also uses `mock `_ 60 | to create mocks for testing. 61 | 62 | You can run the tests, including coverage, as follows: 63 | 64 | :: 65 | 66 | $ cd wstool 67 | $ make test 68 | 69 | 70 | Documentation 71 | ------------- 72 | 73 | Sphinx is used to provide API documentation for wstool. The documents 74 | are stored in the ``doc`` sub-directory. 75 | 76 | You can build the docs as follows: 77 | 78 | :: 79 | 80 | $ cd wstool/doc 81 | $ make html 82 | 83 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | wstool 2 | ====== 3 | 4 | .. module:: wstool 5 | .. moduleauthor:: Tully Foote , Thibault Kruse , Ken Conley 6 | 7 | Using wstool you can update several folders using a variety 8 | of SCMs (SVN, Mercurial, git, Bazaar) with just one command. 9 | 10 | That way you can more effectively manage source code workspaces. 11 | 12 | The wstool package provides a Python API for interacting with a 13 | source code workspace as well as a group of command line tools. 14 | Rosinstall leverages the :mod:`vcstools` package for source control and 15 | stores its state in .rosinstall files. 16 | 17 | 18 | Command Line Tools: 19 | =================== 20 | .. toctree:: 21 | :maxdepth: 2 22 | 23 | wstool_usage 24 | 25 | 26 | Installation 27 | ============ 28 | 29 | Ubuntu 30 | ------ 31 | 32 | On Ubuntu the recommended way to install rosinstall is to use apt. 33 | 34 | :: 35 | 36 | sudo apt-get install python-wstool 37 | 38 | Other Platforms 39 | --------------- 40 | 41 | On other platforms rosinstall is available on pypi and can be installed via ``pip`` 42 | :: 43 | 44 | pip install -U wstool 45 | 46 | or ``easy_install``: 47 | 48 | :: 49 | 50 | easy_install -U wstool vcstools 51 | 52 | 53 | 54 | 55 | Rosinstall File Format: 56 | ======================= 57 | .. toctree:: 58 | :maxdepth: 2 59 | 60 | rosinstall_file_format 61 | 62 | 63 | Advanced: rosinstall developers/contributors 64 | ============================================ 65 | 66 | .. toctree:: 67 | :maxdepth: 2 68 | 69 | developers_guide 70 | 71 | 72 | Indices and tables 73 | ================== 74 | 75 | * :ref:`genindex` 76 | * :ref:`modindex` 77 | * :ref:`search` 78 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\wstool.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\wstool.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /doc/rosinstall_file_format.rst: -------------------------------------------------------------------------------- 1 | rosinstall file format 2 | ====================== 3 | 4 | Format 5 | ------ 6 | 7 | The rosinstall file format is a yaml document. It is a list of 8 | top level dictionaries. Each top level dictionary is expected to have one of the vcs type keys and no other keys. 9 | 10 | 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. 11 | 12 | Each of the vcs type keys requires a ``uri`` key, and optionally takes a ``version`` key. 13 | 14 | Top Level Keys 15 | -------------- 16 | The valid keys are ``svn``, ``hg``, ``git``, ``bzr``. 17 | 18 | Each key represents a form of version control system to use. These are supported from the vcstools module. 19 | 20 | Example rosinstall syntax: 21 | -------------------------- 22 | 23 | Below is an example rosinstall syntax with examples of most of the 24 | possible permutations: 25 | 26 | :: 27 | 28 | - svn: {local-name: some/local/path2, uri: /some/local/uri} 29 | - hg: {local-name: some/local/path3, uri: http://some/uri, version: 123} 30 | - git: {local-name: /some/local/aboslute/path, uri: http://some/uri, version: 123} 31 | - bzr: {local-name: some/local/path4, uri: http://some/uri, version: 123} 32 | 33 | Things to note are: 34 | 35 | - ``version`` is optional though recommended. 36 | - Absolute or relative paths are valid for ``local-name`` 37 | - ``uri`` can be a local file path to a repository. 38 | -------------------------------------------------------------------------------- /doc/wstool_usage.rst: -------------------------------------------------------------------------------- 1 | wstool: A tool for managing source code workspaces 2 | ================================================== 3 | 4 | wstool allows manipulation of a set of version-controlled folders as 5 | specified in a workspace definition file. 6 | 7 | .. contents:: Contents 8 | :depth: 3 9 | 10 | 11 | Usage 12 | ----- 13 | 14 | :: 15 | 16 | wstool is a command to manipulate multiple version controlled folders. 17 | 18 | Official usage: 19 | wstool CMD [ARGS] [OPTIONS] 20 | 21 | wstool will try to infer install path from context 22 | 23 | Type 'wstool help' for usage. 24 | Options: 25 | help provide help for commands 26 | init set up a directory as workspace 27 | set add or changes one entry from your workspace config 28 | merge merges your workspace with another config set 29 | remove (rm) remove an entry from your workspace config, without deleting files 30 | update (up) update or check out some of your config elements 31 | info Overview of some entries 32 | status (st) print the change status of files in some SCM controlled entries 33 | diff (di) print a diff over some SCM controlled entries 34 | 35 | 36 | init 37 | ~~~~ 38 | 39 | set up a directory as workspace 40 | 41 | wstool init does the following: 42 | 43 | 1. Reads folder/file/web-uri SOURCE_PATH looking for a rosinstall yaml 44 | 2. Creates new .rosinstall file at TARGET-PATH configured 45 | 46 | SOURCE_PATH can e.g. be a folder like /opt/ros/electric 47 | If PATH is not given, uses current folder. 48 | 49 | :: 50 | 51 | Usage: wstool init [TARGET_PATH [SOURCE_PATH]]? 52 | 53 | Options:: 54 | 55 | -h, --help show this help message and exit 56 | --continue-on-error Continue despite checkout errors 57 | -j JOBS, --parallel=JOBS 58 | How many parallel threads to use for installing 59 | 60 | Examples:: 61 | 62 | $ wstool init ~/jade /opt/ros/jade 63 | 64 | 65 | set 66 | ~~~ 67 | 68 | add or changes one entry from your workspace config 69 | The command will infer whether you want to add or modify an entry. If 70 | you modify, it will only change the details you provide, keeping 71 | those you did not provide. if you only provide a uri, will use the 72 | basename of it as localname unless such an element already exists. 73 | 74 | The command only changes the configuration, to checkout or update 75 | the element, run wstool update afterwards. 76 | 77 | :: 78 | 79 | Usage: wstool set [localname] [SCM-URI]? [--(detached|svn|hg|git|bzr)] [--version=VERSION]] 80 | 81 | Options: 82 | -h, --help show this help message and exit 83 | --detached make an entry unmanaged (default for new element) 84 | -v VERSION, --version-new=VERSION 85 | point SCM to this version 86 | --git make an entry a git entry 87 | --svn make an entry a subversion entry 88 | --hg make an entry a mercurial entry 89 | --bzr make an entry a bazaar entry 90 | -y, --confirm Do not ask for confirmation 91 | -u, --update update repository after set 92 | -t WORKSPACE, --target-workspace=WORKSPACE 93 | which workspace to use 94 | 95 | Examples:: 96 | 97 | $ wstool set robot_model --hg https://kforge.ros.org/robotmodel/robot_model 98 | $ wstool set robot_model --version robot_model-1.7.1 99 | $ wstool set robot_model --detached 100 | 101 | 102 | 103 | merge 104 | ~~~~~ 105 | 106 | The command merges config with given other rosinstall element sets, from files 107 | or web uris. 108 | 109 | The default workspace will be inferred from context, you can specify one using 110 | -t. 111 | 112 | By default, when an element in an additional URI has the same 113 | local-name as an existing element, the existing element will be 114 | replaced. In order to ensure the ordering of elements is as 115 | provided in the URI, use the option ``--merge-kill-append``. 116 | 117 | :: 118 | 119 | Usage: wstool merge [URI] [OPTIONS] 120 | 121 | Options: 122 | -h, --help show this help message and exit 123 | -a, --merge-kill-append 124 | merge by deleting given entry and appending new one 125 | -k, --merge-keep (default) merge by keeping existing entry and 126 | discarding new one 127 | -r, --merge-replace merge by replacing given entry with new one 128 | maintaining ordering 129 | -y, --confirm-all do not ask for confirmation unless strictly necessary 130 | -t WORKSPACE, --target-workspace=WORKSPACE 131 | which workspace to use 132 | 133 | Examples:: 134 | 135 | $ wstool merge someother.rosinstall 136 | 137 | You can use '-' to pipe in input, as an example:: 138 | 139 | $ roslocate info robot_mode | wstool merge - 140 | 141 | 142 | update 143 | ~~~~~~ 144 | 145 | update or check out some of your config elements 146 | 147 | This command calls the SCM provider to pull changes from remote to 148 | your local filesystem. In case the url has changed, the command will 149 | ask whether to delete or backup the folder. 150 | 151 | :: 152 | 153 | Usage: wstool update [localname]* 154 | 155 | Options: 156 | -h, --help show this help message and exit 157 | --delete-changed-uris 158 | Delete the local copy of a directory before changing 159 | uri. 160 | --abort-changed-uris Abort if changed uri detected 161 | --continue-on-error Continue despite checkout errors 162 | --backup-changed-uris=BACKUP_CHANGED 163 | backup the local copy of a directory before changing 164 | uri to this directory. 165 | -j JOBS, --parallel=JOBS 166 | How many parallel threads to use for installing 167 | -v, --verbose Whether to print out more information 168 | -t WORKSPACE, --target-workspace=WORKSPACE 169 | which workspace to use 170 | 171 | 172 | Examples:: 173 | 174 | $ wstool update -t ~/jade 175 | $ wstool update robot_model geometry 176 | 177 | 178 | 179 | info 180 | ~~~~ 181 | 182 | Overview of some entries 183 | 184 | The Status (S) column shows 185 | x for missing 186 | L for uncommited (local) changes 187 | V for difference in version and/or remote URI 188 | C for difference in local and remote versions 189 | 190 | The 'Version-Spec' column shows what tag, branch or revision was given 191 | in the .rosinstall file. The 'UID' column shows the unique ID of the 192 | current (and specified) version. The 'URI' column shows the configured 193 | URL of the repo. 194 | 195 | If status is V, the difference between what was specified and what is 196 | real is shown in the respective column. For SVN entries, the url is 197 | split up according to standard layout (trunk/tags/branches). The 198 | ROS_PACKAGE_PATH follows the order of the table, earlier entries 199 | overlay later entries. 200 | 201 | When given one localname, just show the data of one element in list 202 | form. 203 | This also has the generic properties element which is usually empty. 204 | 205 | The ``--only`` option accepts keywords: ['path', 'localname', 'version', 206 | 'revision', 'cur_revision', 'uri', 'cur_uri', 'scmtype'] 207 | 208 | :: 209 | 210 | Usage: wstool info [localname]* [OPTIONS] 211 | 212 | 213 | Options: 214 | -h, --help show this help message and exit 215 | --root Show workspace root path 216 | --data-only Does not provide explanations 217 | --only=ONLY Shows comma-separated lists of only given comma- 218 | separated attribute(s). 219 | --yaml Shows only version of single entry. Intended for 220 | scripting. 221 | --fetch When used, retrieves version information from remote 222 | (takes longer). 223 | -u, --untracked Also show untracked files as modifications 224 | -t WORKSPACE, --target-workspace=WORKSPACE 225 | which workspace to use 226 | -m, --managed-only only show managed elements 227 | 228 | Examples:: 229 | 230 | $ wstool info -t ~/ros/jade 231 | $ wstool info robot_model 232 | $ wstool info --yaml 233 | $ wstool info --only=path,cur_uri,cur_revision robot_model geometry 234 | 235 | 236 | 237 | 238 | status 239 | ~~~~~~ 240 | 241 | print the change status of files in some SCM controlled entries. The status 242 | columns meanings are as the respective SCM defines them. 243 | 244 | :: 245 | 246 | Usage: wstool status [localname]* 247 | 248 | Options: 249 | -h, --help show this help message and exit 250 | -u, --untracked Also shows untracked files 251 | -t WORKSPACE, --target-workspace=WORKSPACE 252 | which workspace to use 253 | 254 | diff 255 | ~~~~ 256 | 257 | print a diff over some SCM controlled entries 258 | 259 | :: 260 | 261 | Usage: wstool diff [localname]* 262 | 263 | Options: 264 | -h, --help show this help message and exit 265 | -t WORKSPACE, --target-workspace=WORKSPACE 266 | which workspace to use 267 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | nose 2 | mock 3 | pep8 4 | # run checks in multiple environments 5 | tox 6 | tox-pyenv 7 | 8 | coverage~=4.5 9 | coveralls 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | vcstools>=0.1.42 2 | pyyaml 3 | -------------------------------------------------------------------------------- /scripts/wstool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Software License Agreement (BSD License) 3 | # 4 | # Copyright (c) 2010, Willow Garage, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # * Neither the name of Willow Garage, Inc. nor the names of its 18 | # contributors may be used to endorse or promote products derived 19 | # from this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | # 34 | # Revision $Id: rosws 14389 2011-07-20 18:38:40Z tfoote $ 35 | # $Author: tfoote $ 36 | 37 | """%(prog)s is a command to manipulate ROS workspaces. 38 | 39 | Official usage: 40 | %(prog)s CMD [ARGS] [OPTIONS] 41 | 42 | %(prog)s will try to infer install path from context 43 | "%(prog)s init" replaces the rosinstall command 44 | 45 | Type '%(prog)s --help' for usage. 46 | """ 47 | 48 | from __future__ import print_function 49 | import sys 50 | 51 | try: 52 | import wstool.wstool_cli 53 | from wstool.common import MultiProjectException 54 | except ImportError as exc: 55 | sys.exit("ERROR: Cannot find required rosinstall library version, \ 56 | check your installation (also of vcstools) is up-to-date. One frequent cause \ 57 | is that rosinstall 0.5 is still installed in /usr/local/lib.\n%s" % exc) 58 | 59 | 60 | if __name__ == "__main__": 61 | try: 62 | sys.exit(wstool.wstool_cli.wstool_main(sys.argv)) 63 | except MultiProjectException as mpe: 64 | sys.exit("ERROR in config: %s" % str(mpe)) 65 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [aliases] 5 | test = nosetests --with-coverage --cover-package=wstool --where=test --cover-min-percentage=80 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from distutils.command.build import build 3 | from distutils.command.build_py import build_py 4 | 5 | import os 6 | import sys 7 | import imp 8 | import argparse 9 | 10 | 11 | with open('README.rst') as readme_file: 12 | README = readme_file.read() 13 | 14 | 15 | def get_version(): 16 | ver_file = None 17 | try: 18 | ver_file, pathname, description = imp.find_module('__version__', ['src/wstool']) 19 | vermod = imp.load_module('__version__', ver_file, pathname, description) 20 | version = vermod.version 21 | return version 22 | finally: 23 | if ver_file is not None: 24 | ver_file.close() 25 | 26 | 27 | def _resolve_prefix(prefix, type): 28 | # install to outside of system if OS X to avoid issues caused by 29 | # System Integrity Protection on El Caption 30 | # issue: https://github.com/vcstools/wstool/issues/81 31 | osx_system_prefix = '/System/Library/Frameworks/Python.framework/Versions' 32 | if type == 'man': 33 | if prefix == '/usr': 34 | return '/usr/share' 35 | if sys.prefix.startswith(osx_system_prefix): 36 | return '/usr/local/share' 37 | elif type == 'bash_comp': 38 | if prefix == '/usr': 39 | return '/' 40 | if sys.prefix.startswith(osx_system_prefix): 41 | return '/usr/local' 42 | elif type == 'zsh_comp': 43 | if sys.prefix.startswith(osx_system_prefix): 44 | return '/usr/local' 45 | else: 46 | raise ValueError('not supported type') 47 | return prefix 48 | 49 | 50 | def get_data_files(prefix): 51 | data_files = [] 52 | bash_comp_dest = os.path.join(_resolve_prefix(prefix, 'bash_comp'), 53 | 'etc/bash_completion.d') 54 | data_files.append((bash_comp_dest, ['completion/wstool-completion.bash'])) 55 | zsh_comp_dest = os.path.join(_resolve_prefix(prefix, 'zsh_comp'), 56 | 'share/zsh/site-functions') 57 | data_files.append((zsh_comp_dest, ['completion/_wstool', 58 | 'completion/wstool-completion.bash'])) 59 | return data_files 60 | 61 | 62 | parser = argparse.ArgumentParser() 63 | parser.add_argument('--prefix', default=None, 64 | help='prefix to install data files') 65 | opts, _ = parser.parse_known_args(sys.argv) 66 | prefix = sys.prefix if opts.prefix == None else opts.prefix 67 | 68 | data_files = get_data_files(prefix) 69 | 70 | # At present setuptools has no methods to resolve dependencies at build time, 71 | # so we need to check if sphinx is installed. 72 | # See: https://github.com/pypa/pip/issues/2381 73 | try: 74 | from sphinx.setup_command import BuildDoc 75 | HAVE_SPHINX = True 76 | except: 77 | HAVE_SPHINX = False 78 | 79 | if HAVE_SPHINX: 80 | class WstoolBuildMan(BuildDoc): 81 | def initialize_options(self): 82 | BuildDoc.initialize_options(self) 83 | self.builder = 'man' 84 | 85 | class WstoolBuild(build): 86 | """Run additional commands before build command""" 87 | def run(self): 88 | self.run_command('build_man') 89 | build.run(self) 90 | 91 | class WstoolBuildPy(build_py): 92 | """Run additional commands before build_py command""" 93 | def run(self): 94 | self.run_command('build_man') 95 | build_py.run(self) 96 | cmdclass = dict( 97 | build=WstoolBuild, 98 | build_py=WstoolBuildPy, 99 | build_man=WstoolBuildMan, 100 | ) 101 | man_dest = os.path.join(_resolve_prefix(prefix, 'man'), 'man/man1') 102 | data_files.append((man_dest, ['build/sphinx/man/wstool.1'])) 103 | else: 104 | cmdclass = {} 105 | 106 | with open(os.path.join(os.path.dirname(__file__), 'requirements.txt')) as requirements: 107 | install_requires = requirements.read().splitlines() 108 | 109 | with open(os.path.join(os.path.dirname(__file__), 'requirements-test.txt')) as requirements: 110 | test_required = requirements.read().splitlines() 111 | 112 | setup(name='wstool', 113 | version=get_version(), 114 | packages=['wstool'], 115 | package_dir={'': 'src'}, 116 | data_files=data_files, 117 | cmdclass=cmdclass, 118 | # rosinstall dependency to be kept in order not to break ros hydro install instructions 119 | install_requires=install_requires, 120 | # extras_require allow pip install .[test] 121 | extras_require={ 122 | 'test': test_required 123 | }, 124 | # tests_require automatically installed when running python setup.py test 125 | tests_require=test_required, 126 | scripts=["scripts/wstool"], 127 | author="Tully Foote", 128 | author_email="tfoote@osrfoundation.org", 129 | url="http://wiki.ros.org/wstool", 130 | keywords=["ROS"], 131 | classifiers=["Programming Language :: Python", 132 | "Programming Language :: Python :: 2", 133 | "Programming Language :: Python :: 3", 134 | "Development Status :: 7 - Inactive", 135 | "License :: OSI Approved :: BSD License", 136 | "Topic :: Software Development :: Version Control" 137 | ], 138 | description="workspace multi-SCM commands", 139 | long_description=README, 140 | license="BSD") 141 | -------------------------------------------------------------------------------- /src/wstool/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vcstools/wstool/7f74a7997c7dee7c856516cbb4c59c15e2f72fee/src/wstool/__init__.py -------------------------------------------------------------------------------- /src/wstool/__version__.py: -------------------------------------------------------------------------------- 1 | version = '0.1.18' 2 | -------------------------------------------------------------------------------- /src/wstool/config.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | 34 | import os 35 | from wstool.config_elements import AVCSConfigElement, OtherConfigElement, SetupConfigElement 36 | from wstool.common import MultiProjectException, normabspath, realpath_relation, normalize_uri 37 | 38 | 39 | class Config: 40 | """ 41 | A config is a set of config elements, each of which defines a folder or file 42 | and possibly a VCS from which to update the folder. 43 | """ 44 | 45 | def __init__(self, path_specs, install_path, config_filename=None, extended_types=None, merge_strategy='KillAppend'): 46 | """ 47 | :param config_source_dict: A list (e.g. from yaml) describing the config, list of dict, each dict describing one element. 48 | :param config_filename: When given a folder, Config 49 | :param merge_strategy: how to deal with entries with equivalent path. See insert_element 50 | 51 | will look in folder for file of that name for more config source, str. 52 | """ 53 | assert install_path is not None, "Install path is None" 54 | if path_specs is None: 55 | raise MultiProjectException("Passed empty source to create config") 56 | # All API operations must grant that elements in trees have unique local_name and paths 57 | # Also managed (VCS) entries must be disjunct (meaning one cannot be in a child folder of another managed one) 58 | # The idea is that managed entries can safely be concurrently modified 59 | self.trees = [] 60 | self.base_path = os.path.abspath(install_path) 61 | 62 | self.config_filename = None 63 | if config_filename is not None: 64 | self.config_filename = os.path.basename(config_filename) 65 | # using a registry primarily for unit test design 66 | self.registry = {'svn': AVCSConfigElement, 67 | 'git': AVCSConfigElement, 68 | 'hg': AVCSConfigElement, 69 | 'bzr': AVCSConfigElement, 70 | 'tar': AVCSConfigElement} 71 | if extended_types is not None: 72 | self.registry = dict(list(self.registry.items()) + list(extended_types.items())) 73 | 74 | for path_spec in path_specs: 75 | action = self.add_path_spec(path_spec, merge_strategy) 76 | # Usual action in init should be 'Append', anything else is unusual 77 | if action == 'KillAppend': 78 | print("Replace existing entry %s by appending." % path_spec.get_local_name()) 79 | elif action == 'MergeReplace': 80 | print("Replace existing entry %s" % path_spec.get_local_name()) 81 | elif action == 'MergeKeep': 82 | print("Keep existing entry %s, discard later one" % path_spec.get_local_name()) 83 | 84 | def __str__(self): 85 | return str([str(x) for x in self.trees]) 86 | 87 | def add_path_spec(self, path_spec, merge_strategy='KillAppend'): 88 | """ 89 | add new element to this config with information provided in path spec 90 | 91 | :param merge_strategy: see insert_element 92 | :param path_specs: PathSpec objects 93 | :returns: merge action taken, see insert_element 94 | """ 95 | # compute the local_path for the config element 96 | local_path = normabspath( 97 | path_spec.get_local_name(), self.get_base_path()) 98 | if os.path.isfile(local_path): 99 | if path_spec.get_tags() is not None and 'setup-file' in path_spec.get_tags(): 100 | elem = SetupConfigElement(local_path, 101 | os.path.normpath(path_spec.get_local_name()), 102 | properties=path_spec.get_tags()) 103 | return self.insert_element(elem, merge_strategy) 104 | else: 105 | print("!!!!! Warning: Not adding file %s" % local_path) 106 | return None 107 | else: 108 | # scmtype == None kept for historic reasons here 109 | if (path_spec.get_scmtype() == None and 110 | self.config_filename is not None and 111 | os.path.exists(os.path.join(local_path, self.config_filename))): 112 | 113 | print("!!!!! Warning: Not recursing into other config folder %s containing file %s" % (local_path, self.config_filename)) 114 | return None 115 | 116 | if path_spec.get_scmtype() != None: 117 | return self._insert_vcs_path_spec(path_spec, local_path, merge_strategy) 118 | else: 119 | # keep the given local name (e.g. relative path) for other 120 | # elements, but normalize 121 | local_name = os.path.normpath(path_spec.get_local_name()) 122 | elem = OtherConfigElement(local_path, 123 | local_name, 124 | path_spec.get_uri(), 125 | path_spec.get_version(), 126 | properties=path_spec.get_tags()) 127 | return self.insert_element(elem, merge_strategy) 128 | 129 | def _insert_vcs_path_spec(self, path_spec, local_path, merge_strategy='KillAppend'): 130 | # Get the version and source_uri elements 131 | source_uri = normalize_uri(path_spec.get_uri(), self.get_base_path()) 132 | 133 | version = path_spec.get_version() 134 | try: 135 | local_name = os.path.normpath(path_spec.get_local_name()) 136 | elem = self._create_vcs_config_element( 137 | path_spec.get_scmtype(), 138 | local_path, 139 | local_name, 140 | source_uri, 141 | version, 142 | properties=path_spec.get_tags()) 143 | return self.insert_element(elem, merge_strategy) 144 | except LookupError as ex: 145 | raise MultiProjectException( 146 | "Abstracted VCS Config failed. Exception: %s" % ex) 147 | 148 | def insert_element(self, new_config_elt, merge_strategy='KillAppend'): 149 | """ 150 | Insert ConfigElement to self.trees, checking for duplicate 151 | local-name or path first. In case local_name matches, follow 152 | given strategy 153 | 154 | - KillAppend (default): remove old element, append new at the end 155 | - MergeReplace: remove first hit, insert new at that position. 156 | - MergeKeep: Discard new element 157 | 158 | In case local path matches but local name does not, raise Exception 159 | 160 | :returns: the action performed None, 'Append', 'KillAppend', 161 | 'MergeReplace', 'MergeKeep' 162 | """ 163 | removals = [] 164 | replaced = False 165 | for index, loop_elt in enumerate(self.trees): 166 | # if paths are os.path.realpath, no symlink problems. 167 | relationship = realpath_relation(loop_elt.get_path(), 168 | new_config_elt.get_path()) 169 | if relationship == 'SAME_AS': 170 | if os.path.normpath(loop_elt.get_local_name()) != os.path.normpath(new_config_elt.get_local_name()): 171 | raise MultiProjectException("Elements with different local_name target the same path: %s, %s" % (loop_elt, new_config_elt)) 172 | else: 173 | if (loop_elt == new_config_elt): 174 | return None 175 | if (merge_strategy == 'MergeReplace' or 176 | (merge_strategy == 'KillAppend' and 177 | index == len(self.trees) - 1)): 178 | self.trees[index] = new_config_elt 179 | # keep looping to check for overlap when replacing non- 180 | # scm with scm entry 181 | replaced = True 182 | if (loop_elt.is_vcs_element or not new_config_elt.is_vcs_element): 183 | return 'MergeReplace' 184 | elif merge_strategy == 'KillAppend': 185 | removals.append(loop_elt) 186 | elif merge_strategy == 'MergeKeep': 187 | return 'MergeKeep' 188 | else: 189 | raise LookupError( 190 | "No such merge strategy: %s" % str(merge_strategy)) 191 | elif ((relationship == 'CHILD_OF' and new_config_elt.is_vcs_element()) 192 | or (relationship == 'PARENT_OF' and loop_elt.is_vcs_element())): 193 | # we do not allow any elements to be children of scm elements 194 | # to allow for parallel updates and because wstool may 195 | # delete scm folders on update, and thus subfolders can be 196 | # deleted with their parents 197 | raise MultiProjectException( 198 | "Managed Element paths overlap: %s, %s" % (loop_elt, 199 | new_config_elt)) 200 | if replaced: 201 | return 'MergeReplace' 202 | for loop_elt in removals: 203 | self.trees.remove(loop_elt) 204 | self.trees.append(new_config_elt) 205 | if len(removals) > 0: 206 | return 'KillAppend' 207 | return 'Append' 208 | 209 | def remove_element(self, local_name): 210 | """ 211 | Removes element in the tree with the given local name (should be only one) 212 | 213 | :returns: True if such an element was found 214 | """ 215 | removals = [] 216 | for tree_el in self.trees: 217 | if tree_el.get_local_name() == local_name: 218 | removals.append(tree_el) 219 | if len(removals) > 0: 220 | for tree_el in removals: 221 | self.trees.remove(tree_el) 222 | return True 223 | return False 224 | 225 | def _create_vcs_config_element(self, scmtype, path, local_name, uri, version='', properties=None): 226 | try: 227 | eclass = self.registry[scmtype] 228 | except LookupError: 229 | raise MultiProjectException( 230 | "No VCS client registered for vcs type %s" % scmtype) 231 | return eclass(scmtype=scmtype, 232 | path=path, 233 | local_name=local_name, 234 | uri=uri, 235 | version=version, 236 | properties=properties) 237 | 238 | def get_base_path(self): 239 | return self.base_path 240 | 241 | def get_config_filename(self): 242 | return self.config_filename 243 | 244 | def get_source(self, versioned=False, vcs_only=False): 245 | """ 246 | :param versioned: True returns sources as versioned PathSpec, False 247 | returns sources as simple PathSpec 248 | :param vcs_only: True returns only version-controlled sources, False 249 | returns all sources 250 | :returns: all elements that got added by user keystrokes 251 | (CLI and changed .rosinstall) 252 | """ 253 | source_aggregate = [] 254 | for tree_el in self.trees: 255 | if vcs_only and not tree_el.is_vcs_element(): 256 | continue 257 | 258 | if not versioned or not tree_el.is_vcs_element(): 259 | source_aggregate.append(tree_el.get_path_spec()) 260 | else: 261 | source_aggregate.append(tree_el.get_versioned_path_spec()) 262 | return source_aggregate 263 | 264 | def get_config_elements(self): 265 | source_aggregate = [] 266 | for tree_el in self.trees: 267 | source_aggregate.append(tree_el) 268 | return source_aggregate 269 | -------------------------------------------------------------------------------- /src/wstool/helpers.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2010, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import os 34 | import sys 35 | import codecs 36 | import subprocess 37 | from wstool.config_elements import SetupConfigElement 38 | 39 | ROSINSTALL_FILENAME = ".rosinstall" 40 | 41 | def get_ros_package_path(config): 42 | """ Return the simplifed ROS_PACKAGE_PATH """ 43 | code_trees = [] 44 | for tree_el in reversed(config.get_config_elements()): 45 | if not os.path.isfile(tree_el.get_path()): 46 | code_trees.append(tree_el.get_path()) 47 | return code_trees 48 | -------------------------------------------------------------------------------- /src/wstool/ui.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import sys 34 | 35 | 36 | class Ui(object): 37 | """ 38 | wrap user interaction, such that client libraries may provide own 39 | implementation 40 | """ 41 | 42 | # For now, primarily define this for replacement in unittests 43 | GLOBAL_UI = None 44 | 45 | @staticmethod 46 | def get_ui(): 47 | if Ui.GLOBAL_UI is None: 48 | return Ui() 49 | return Ui.GLOBAL_UI 50 | 51 | @staticmethod 52 | def set_ui(uiarg): 53 | Ui.GLOBAL_UI = uiarg 54 | 55 | def __init__(self): 56 | pass 57 | 58 | def get_backup_path(self): 59 | """Interactive function asking the user to choose a path for backup""" 60 | backup_path = self.get_input("Please enter backup pathname: ") 61 | print(("backing up to %s" % backup_path)) 62 | return backup_path 63 | 64 | def get_input(self, prompt): 65 | if sys.hexversion > 0x03000000: 66 | return input(prompt) 67 | else: 68 | return raw_input(prompt) 69 | 70 | def prompt_del_abort_retry(self, 71 | prompt, 72 | allow_skip=False, 73 | allow_inplace=False): 74 | """ 75 | Interactive function asking the user to choose a conflict resolution 76 | :param prompt: message to display, str 77 | :param allow_skip: whether to display skip option, bool 78 | :param inplace: whether to show option for inplace replacing (symlinks) 79 | :return: user choice one of backup, delete, abort, inplace, skip 80 | """ 81 | valid_modes = ['(d)elete and replace', 82 | '(a)bort'] 83 | if allow_inplace: 84 | valid_modes.append('(i)nplace delete and replace at symlink') 85 | else: 86 | valid_modes.append('(b)ackup and replace') 87 | if allow_skip: 88 | valid_modes.append('(s)kip') 89 | 90 | mode = "" 91 | full_prompt = "%s\n %s: " % (prompt, ", ".join(valid_modes)) 92 | while mode == "": 93 | mode_input = self.get_input(full_prompt) 94 | if not allow_inplace and mode_input == 'b': 95 | mode = 'backup' 96 | elif mode_input == 'd': 97 | mode = 'delete' 98 | elif mode_input == 'a': 99 | mode = 'abort' 100 | elif allow_inplace and mode_input == 'i': 101 | mode = 'inplace' 102 | elif allow_skip and mode_input == 's': 103 | mode = 'skip' 104 | return mode 105 | -------------------------------------------------------------------------------- /src/wstool/wstool_cli.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2010, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | """%(prog)s is a command to manipulate ROS workspaces. %(prog)s replaces its predecessor rosws. 34 | 35 | Official usage: 36 | %(prog)s CMD [ARGS] [OPTIONS] 37 | 38 | %(prog)s will try to infer install path from context 39 | 40 | Type '%(prog)s help' for usage. 41 | """ 42 | 43 | from __future__ import print_function 44 | import os 45 | import sys 46 | 47 | from optparse import OptionParser 48 | 49 | from wstool.cli_common import get_info_table, get_workspace 50 | import wstool.multiproject_cmd as multiproject_cmd 51 | import wstool.__version__ 52 | 53 | from wstool.helpers import ROSINSTALL_FILENAME 54 | from wstool.common import MultiProjectException 55 | from wstool.multiproject_cli import MultiprojectCLI, \ 56 | __MULTIPRO_CMD_DICT__, __MULTIPRO_CMD_ALIASES__, \ 57 | __MULTIPRO_CMD_HELP_LIST__, IndentedHelpFormatterWithNL, \ 58 | list_usage, get_header 59 | 60 | ## This file adds or extends commands from multiproject_cli where ROS 61 | ## specific output has to be generated. 62 | 63 | 64 | _PROGNAME = 'wstool' 65 | 66 | 67 | class WstoolCLI(MultiprojectCLI): 68 | 69 | def __init__(self, config_filename=ROSINSTALL_FILENAME, progname=_PROGNAME): 70 | 71 | def config_generator(config, filename, header=None, spec=True, 72 | exact=False, vcs_only=False): 73 | wstool.multiproject_cmd.cmd_persist_config( 74 | config, 75 | filename, 76 | header, 77 | pretty=True, 78 | sort_with_localname=True, 79 | spec=spec, 80 | exact=exact, 81 | vcs_only=vcs_only) 82 | 83 | MultiprojectCLI.__init__( 84 | self, 85 | progname=progname, 86 | config_filename=config_filename, 87 | config_generator=config_generator) 88 | 89 | 90 | def wstool_main(argv=None, usage=None): 91 | """ 92 | Calls the function corresponding to the first argument. 93 | 94 | :param argv: sys.argv by default 95 | :param usage: function printing usage string, multiproject_cli.list_usage by default 96 | """ 97 | if argv is None: 98 | argv = sys.argv 99 | if (sys.argv[0] == '-c'): 100 | sys.argv = [_PROGNAME] + sys.argv[1:] 101 | if '--version' in argv: 102 | print("%s: \t%s\n%s" % (_PROGNAME, 103 | wstool.__version__.version, 104 | multiproject_cmd.cmd_version())) 105 | sys.exit(0) 106 | 107 | if not usage: 108 | usage = lambda: print(list_usage(progname=_PROGNAME, 109 | description=__doc__, 110 | command_keys=__MULTIPRO_CMD_HELP_LIST__, 111 | command_helps=__MULTIPRO_CMD_DICT__, 112 | command_aliases=__MULTIPRO_CMD_ALIASES__)) 113 | workspace = None 114 | if len(argv) < 2: 115 | try: 116 | workspace = get_workspace(argv, 117 | os.getcwd(), 118 | config_filename=ROSINSTALL_FILENAME) 119 | argv.append('info') 120 | except MultiProjectException as e: 121 | print(str(e)) 122 | usage() 123 | return 0 124 | 125 | if argv[1] in ['--help', '-h']: 126 | usage() 127 | return 0 128 | 129 | try: 130 | command = argv[1] 131 | args = argv[2:] 132 | 133 | if command == 'help': 134 | if len(argv) < 3: 135 | usage() 136 | return 0 137 | 138 | else: 139 | command = argv[2] 140 | args = argv[3:] 141 | args.insert(0, "--help") 142 | # help help 143 | if command == 'help': 144 | usage() 145 | return 0 146 | cli = WstoolCLI(progname=_PROGNAME) 147 | 148 | # commands for which we do not infer target workspace 149 | commands = {'init': cli.cmd_init} 150 | # commands which work on a workspace 151 | ws_commands = { 152 | 'info': cli.cmd_info, 153 | 'remove': cli.cmd_remove, 154 | 'set': cli.cmd_set, 155 | 'merge': cli.cmd_merge, 156 | 'export': cli.cmd_snapshot, 157 | 'diff': cli.cmd_diff, 158 | 'foreach': cli.cmd_foreach, 159 | 'scrape': cli.cmd_scrape, 160 | 'status': cli.cmd_status, 161 | 'update': cli.cmd_update} 162 | for label in list(ws_commands.keys()): 163 | if label in __MULTIPRO_CMD_ALIASES__: 164 | ws_commands[__MULTIPRO_CMD_ALIASES__[label]] = ws_commands[label] 165 | 166 | if command not in commands and command not in ws_commands: 167 | if os.path.exists(command): 168 | args = ['-t', command] + args 169 | command = 'info' 170 | else: 171 | if command.startswith('-'): 172 | print("First argument must be name of a command: %s" % command) 173 | else: 174 | print("Error: unknown command: %s" % command) 175 | usage() 176 | return 1 177 | 178 | if command in commands: 179 | return commands[command](args) 180 | else: 181 | if workspace is None and not '--help' in args and not '-h' in args: 182 | workspace = get_workspace(args, 183 | os.getcwd(), 184 | config_filename=ROSINSTALL_FILENAME) 185 | return ws_commands[command](workspace, args) 186 | 187 | except KeyboardInterrupt: 188 | return 1 189 | -------------------------------------------------------------------------------- /stdeb.cfg: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | Depends: subversion, mercurial, git-core, bzr, python-yaml, python-vcstools (>= 0.1.38) 3 | Depends3: subversion, mercurial, git-core, bzr, python3-yaml, python3-vcstools (>= 0.1.38) 4 | Conflicts: python3-wstool 5 | Conflicts3: python-wstool 6 | Suite: oneiric precise quantal raring saucy trusty utopic vivid wily xenial yakkety zesty artful bionic wheezy jessie stretch buster 7 | X-Python3-Version: >= 3.2 8 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vcstools/wstool/7f74a7997c7dee7c856516cbb4c59c15e2f72fee/test/__init__.py -------------------------------------------------------------------------------- /test/example.yaml: -------------------------------------------------------------------------------- 1 | text: foobar 2 | number: 2 3 | -------------------------------------------------------------------------------- /test/example_dirs/ros/stack.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vcstools/wstool/7f74a7997c7dee7c856516cbb4c59c15e2f72fee/test/example_dirs/ros/stack.xml -------------------------------------------------------------------------------- /test/example_dirs/ros_comm/stack.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vcstools/wstool/7f74a7997c7dee7c856516cbb4c59c15e2f72fee/test/example_dirs/ros_comm/stack.xml -------------------------------------------------------------------------------- /test/example_dirs/roscpp/manifest.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vcstools/wstool/7f74a7997c7dee7c856516cbb4c59c15e2f72fee/test/example_dirs/roscpp/manifest.xml -------------------------------------------------------------------------------- /test/io_wrapper.py: -------------------------------------------------------------------------------- 1 | class StringIO: 2 | """ 3 | StringIO.StringIO does not exist in python3 4 | io.StringIO cannot cope with unicode 5 | """ 6 | 7 | def __init__(self): 8 | self.stream = '' 9 | 10 | def write(self, data): 11 | self.stream += data 12 | 13 | def flush(self): 14 | pass 15 | 16 | def __getattr__(self, attr): 17 | return getattr(self.stream, attr) 18 | 19 | def getvalue(self): 20 | return self.stream 21 | -------------------------------------------------------------------------------- /test/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vcstools/wstool/7f74a7997c7dee7c856516cbb4c59c15e2f72fee/test/local/__init__.py -------------------------------------------------------------------------------- /test/local/mock_client.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | 34 | class MockVcsClient(): 35 | """ 36 | Mocked vcs client. TODO: we should be using pathon magic mock instead. 37 | """ 38 | 39 | 40 | class MockVcsClient(): 41 | 42 | def __init__(self, 43 | scmtype='mocktype', 44 | path_exists=False, 45 | checkout_success=True, 46 | update_success=True, 47 | vcs_presence=False, 48 | url="mockurl", 49 | actualversion=None, 50 | specversion=None, 51 | remoteversion=None): 52 | self.scmtype = scmtype 53 | self.path_exists_flag = path_exists 54 | self.checkout_success = checkout_success 55 | self.update_success = update_success 56 | self.vcs_presence = vcs_presence 57 | self.mockurl = url 58 | self.checkedout = vcs_presence 59 | self.updated = False 60 | self.actualversion = actualversion 61 | self.specversion = specversion 62 | self.remoteversion = remoteversion 63 | 64 | def get_vcs_type_name(self): 65 | return self.scmtype 66 | 67 | def get_diff(self, basepath=None): 68 | return self.scmtype + "mockdiff%s" % basepath 69 | 70 | def get_version(self, revision=None): 71 | if revision == None: 72 | return self.actualversion 73 | else: 74 | return self.specversion 75 | 76 | def get_remote_version(self, fetch=False): 77 | return self.remoteversion 78 | 79 | def get_current_version_label(self): 80 | return self.scmtype + "mockcurrentversionlabel" 81 | 82 | def get_status(self, basepath=None, untracked=False): 83 | return self.scmtype + " mockstatus%s,%s" % (basepath, untracked) 84 | 85 | def path_exists(self): 86 | return self.path_exists_flag 87 | 88 | def checkout(self, uri=None, version=None, verbose=False, timeout=None, shallow=False): 89 | self.checkedout = True 90 | return self.checkout_success 91 | 92 | def update(self, version, verbose=False, timeout=None): 93 | self.updated = True 94 | return self.update_success 95 | 96 | def detect_presence(self): 97 | return self.vcs_presence 98 | 99 | def get_url(self): 100 | return self.mockurl 101 | 102 | def url_matches(self, url, url_or_shortcut): 103 | return (url == url_or_shortcut or 104 | url_or_shortcut is None or 105 | url_or_shortcut.endswith('_shortcut')) 106 | -------------------------------------------------------------------------------- /test/local/test_config_elt.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import unittest 34 | 35 | import wstool.config 36 | from wstool.common import MultiProjectException 37 | from . import mock_client 38 | 39 | 40 | class ConfigElements_Test(unittest.TestCase): 41 | 42 | def test_simple_config_element_API(self): 43 | path = "some/path" 44 | localname = "some/local/name" 45 | other1 = wstool.config_elements.ConfigElement(path, localname) 46 | self.assertEqual(path, other1.get_path()) 47 | self.assertEqual(localname, other1.get_local_name()) 48 | self.assertFalse(other1.is_vcs_element()) 49 | other1 = wstool.config_elements.OtherConfigElement(path, localname) 50 | self.assertEqual(path, other1.get_path()) 51 | self.assertEqual(localname, other1.get_local_name()) 52 | self.assertEqual({'other': {'local-name': 'some/local/name'}}, other1.get_path_spec().get_legacy_yaml()) 53 | self.assertFalse(other1.is_vcs_element()) 54 | other1 = wstool.config_elements.SetupConfigElement(path, localname) 55 | self.assertEqual(path, other1.get_path()) 56 | self.assertEqual(localname, other1.get_local_name()) 57 | self.assertEqual({'setup-file': {'local-name': 'some/local/name'}}, other1.get_path_spec().get_legacy_yaml()) 58 | self.assertFalse(other1.is_vcs_element()) 59 | other1 = wstool.config_elements.OtherConfigElement(path, localname, properties=[{}]) 60 | self.assertEqual(path, other1.get_path()) 61 | self.assertEqual(localname, other1.get_local_name()) 62 | self.assertEqual({'other': {'local-name': 'some/local/name'}}, other1.get_path_spec().get_legacy_yaml()) 63 | self.assertFalse(other1.is_vcs_element()) 64 | other1 = wstool.config_elements.OtherConfigElement(path, localname, properties=['meta']) 65 | self.assertEqual(path, other1.get_path()) 66 | self.assertEqual(localname, other1.get_local_name()) 67 | self.assertEqual({'other': {'local-name': 'some/local/name', 'meta': None}}, other1.get_path_spec().get_legacy_yaml()) 68 | self.assertFalse(other1.is_vcs_element()) 69 | other1 = wstool.config_elements.OtherConfigElement(path, localname, properties=[{'meta': {'repo-name': 'skynetish-ros-pkg'}}]) 70 | self.assertEqual(path, other1.get_path()) 71 | self.assertEqual(localname, other1.get_local_name()) 72 | self.assertEqual({'other': {'local-name': 'some/local/name', 'meta': {'repo-name': 'skynetish-ros-pkg'}}}, other1.get_path_spec().get_legacy_yaml()) 73 | self.assertFalse(other1.is_vcs_element()) 74 | 75 | def test_mock_vcs_config_element_init(self): 76 | path = "some/path" 77 | localname = "some/local/name" 78 | try: 79 | wstool.config_elements.AVCSConfigElement("mock", None, None, None) 80 | self.fail("Exception expected") 81 | except MultiProjectException: 82 | pass 83 | try: 84 | wstool.config_elements.AVCSConfigElement("mock", "path", None, None) 85 | self.fail("Exception expected") 86 | except MultiProjectException: 87 | pass 88 | try: 89 | wstool.config_elements.AVCSConfigElement("mock", None, None, "some/uri") 90 | self.fail("Exception expected") 91 | except MultiProjectException: 92 | pass 93 | path = "some/path" 94 | localname = "some/local/name" 95 | uri = 'some/uri' 96 | version = 'some.version' 97 | vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, vcsc=mock_client.MockVcsClient()) 98 | self.assertEqual(path, vcsc.get_path()) 99 | self.assertEqual(localname, vcsc.get_local_name()) 100 | self.assertEqual(uri, vcsc.uri) 101 | self.assertTrue(vcsc.is_vcs_element()) 102 | self.assertEqual("mocktypemockdiffNone", vcsc.get_diff()) 103 | self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status()) 104 | self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri'}}, vcsc.get_path_spec().get_legacy_yaml()) 105 | self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri', }}, vcsc.get_versioned_path_spec().get_legacy_yaml()) 106 | 107 | vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, None, vcsc=mock_client.MockVcsClient()) 108 | self.assertEqual(path, vcsc.get_path()) 109 | self.assertEqual(localname, vcsc.get_local_name()) 110 | self.assertEqual(uri, vcsc.uri) 111 | self.assertTrue(vcsc.is_vcs_element()) 112 | self.assertEqual("mocktypemockdiffNone", vcsc.get_diff()) 113 | self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status()) 114 | self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri'}}, vcsc.get_path_spec().get_legacy_yaml()) 115 | self.assertEqual({'mock': {'local-name': 'some/local/name', 'uri': 'some/uri', }}, vcsc.get_versioned_path_spec().get_legacy_yaml()) 116 | 117 | vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, version, vcsc=mock_client.MockVcsClient()) 118 | self.assertEqual(path, vcsc.get_path()) 119 | self.assertEqual(localname, vcsc.get_local_name()) 120 | self.assertEqual(uri, vcsc.uri) 121 | self.assertTrue(vcsc.is_vcs_element()) 122 | self.assertEqual("mocktypemockdiffNone", vcsc.get_diff()) 123 | self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status()) 124 | self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri'}}, vcsc.get_path_spec().get_legacy_yaml()) 125 | self.assertEqual({'mock': {'local-name': 'some/local/name', 'version': 'some.version', 'uri': 'some/uri'}}, vcsc.get_versioned_path_spec().get_legacy_yaml()) 126 | 127 | vcsc = wstool.config_elements.AVCSConfigElement( 128 | "mock", path, localname, uri, version, 129 | vcsc=mock_client.MockVcsClient(), 130 | properties=[{'meta': {'repo-name': 'skynetish-ros-pkg'}}]) 131 | self.assertEqual(path, vcsc.get_path()) 132 | self.assertEqual(localname, vcsc.get_local_name()) 133 | self.assertEqual(uri, vcsc.uri) 134 | self.assertTrue(vcsc.is_vcs_element()) 135 | self.assertEqual("mocktypemockdiffNone", vcsc.get_diff()) 136 | self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status()) 137 | 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()) 138 | 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()) 139 | 140 | # this time using 'uri_shortcut' in mock_client.MockVcsClient, get special treatment un url_matches() 141 | uri2 = 'some/uri2' 142 | vcsc = wstool.config_elements.AVCSConfigElement( 143 | "mock", path, localname, uri2, version, 144 | vcsc=mock_client.MockVcsClient(url='url_shortcut'), 145 | properties=[{'meta': {'repo-name': 'skynetish-ros-pkg'}}]) 146 | self.assertEqual(path, vcsc.get_path()) 147 | self.assertEqual(localname, vcsc.get_local_name()) 148 | self.assertEqual(uri2, vcsc.uri) 149 | self.assertTrue(vcsc.is_vcs_element()) 150 | self.assertEqual("mocktypemockdiffNone", vcsc.get_diff()) 151 | self.assertEqual("mocktype mockstatusNone,False", vcsc.get_status()) 152 | 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()) 153 | 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()) 154 | 155 | def test_mock_install(self): 156 | path = "some/path" 157 | localname = "some/local/name" 158 | uri = 'some/uri' 159 | version = 'some.version' 160 | mockclient = mock_client.MockVcsClient(url=uri) 161 | vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, None, vcsc=mockclient) 162 | vcsc.install() 163 | self.assertTrue(mockclient.checkedout) 164 | self.assertFalse(mockclient.updated) 165 | # checkout failure 166 | mockclient = mock_client.MockVcsClient(url=uri, checkout_success=False) 167 | try: 168 | vcsc = wstool.config_elements.AVCSConfigElement("mock", path, localname, uri, None, vcsc=mockclient) 169 | vcsc.install() 170 | self.fail("should have raised Exception") 171 | except MultiProjectException: 172 | pass 173 | -------------------------------------------------------------------------------- /test/local/test_diff_functions_bzr.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | from __future__ import unicode_literals 34 | 35 | import os 36 | import sys 37 | from test.io_wrapper import StringIO 38 | import subprocess 39 | 40 | import wstool 41 | import wstool.helpers 42 | import wstool.wstool_cli 43 | from wstool.wstool_cli import WstoolCLI 44 | from wstool.wstool_cli import wstool_main 45 | 46 | import test.scm_test_base 47 | from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split 48 | 49 | 50 | def create_bzr_repo(remote_path): 51 | # create a "remote" repo 52 | subprocess.check_call(["bzr", "init"], cwd=remote_path) 53 | subprocess.check_call(["touch", "fixed.txt"], cwd=remote_path) 54 | subprocess.check_call(["touch", "modified.txt"], cwd=remote_path) 55 | subprocess.check_call(["touch", "modified-fs.txt"], cwd=remote_path) 56 | subprocess.check_call(["touch", "deleted.txt"], cwd=remote_path) 57 | subprocess.check_call(["touch", "deleted-fs.txt"], cwd=remote_path) 58 | subprocess.check_call(["bzr", "add", "fixed.txt"], cwd=remote_path) 59 | subprocess.check_call(["bzr", "add", "modified.txt"], cwd=remote_path) 60 | subprocess.check_call(["bzr", "add", "modified-fs.txt"], cwd=remote_path) 61 | subprocess.check_call(["bzr", "add", "deleted.txt"], cwd=remote_path) 62 | subprocess.check_call(["bzr", "add", "deleted-fs.txt"], cwd=remote_path) 63 | subprocess.check_call(["bzr", "commit", "-m", "modified"], cwd=remote_path) 64 | 65 | 66 | def modify_bzr_repo(clone_path): 67 | # make local modifications 68 | subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path) 69 | subprocess.check_call(["bzr", "rm", "deleted.txt"], cwd=clone_path) 70 | _add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n") 71 | _add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n") 72 | _add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n") 73 | _add_to_file(os.path.join(clone_path, "added.txt"), "flam\n") 74 | subprocess.check_call(["bzr", "add", "added.txt"], cwd=clone_path) 75 | 76 | 77 | class WstoolDiffBzrTest(AbstractSCMTest): 78 | 79 | @classmethod 80 | def setUpClass(self): 81 | AbstractSCMTest.setUpClass() 82 | remote_path = os.path.join(self.test_root_path, "remote") 83 | os.makedirs(remote_path) 84 | 85 | create_bzr_repo(remote_path) 86 | 87 | # wstool the remote repo and fake ros 88 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), 89 | "- other: {local-name: ../ros}\n- bzr: {local-name: clone, uri: %s}" % remote_path) 90 | cmd = ["wstool", "update", "-t", "ws"] 91 | os.chdir(self.test_root_path) 92 | wstool_main(cmd) 93 | 94 | clone_path = os.path.join(self.local_path, "clone") 95 | 96 | modify_bzr_repo(clone_path) 97 | 98 | def check_diff_output(self, output): 99 | # uncomment following line for easiest way to get actual output with escapes 100 | # self.assertEqual(None, output); 101 | 102 | # bzr writes date-time of file into diff 103 | self.assertTrue(output.startswith("=== added file 'added.txt'\n--- clone/added.txt"), msg=0) 104 | self.assertTrue(0 < output.find("+++ clone/added.txt"), msg=1) 105 | self.assertTrue(0 < output.find("@@ -0,0 +1,1 @@\n+flam\n\n"), msg=2) 106 | 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) 107 | self.assertTrue(0 < output.find("@@ -0,0 +1,1 @@\n+foo\n\n=== modified file 'modified.txt'\n--- clone/modified.txt"), msg=4) 108 | 109 | def test_wstool_diff_bzr_outside(self): 110 | """Test diff output for bzr when run outside workspace""" 111 | cmd = ["wstool", "diff", "-t", "ws"] 112 | os.chdir(self.test_root_path) 113 | sys.stdout = output = StringIO() 114 | wstool_main(cmd) 115 | sys.stdout = sys.__stdout__ 116 | output = output.getvalue() 117 | self.check_diff_output(output) 118 | 119 | cli = WstoolCLI() 120 | self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), [])) 121 | 122 | 123 | def test_wstool_diff_bzr_inside(self): 124 | """Test diff output for bzr when run inside workspace""" 125 | directory = self.test_root_path + "/ws" 126 | 127 | cmd = ["wstool", "diff"] 128 | os.chdir(directory) 129 | sys.stdout = output = StringIO() 130 | wstool_main(cmd) 131 | output = output.getvalue() 132 | sys.stdout = sys.__stdout__ 133 | self.check_diff_output(output) 134 | 135 | cli = WstoolCLI() 136 | self.assertEqual(0, cli.cmd_diff(directory, [])) 137 | 138 | def test_wstool_status_bzr_inside(self): 139 | """Test status output for bzr when run inside workspace""" 140 | directory = self.test_root_path + "/ws" 141 | 142 | cmd = ["wstool", "status"] 143 | os.chdir(directory) 144 | sys.stdout = output = StringIO() 145 | wstool_main(cmd) 146 | output = output.getvalue() 147 | sys.stdout = sys.__stdout__ 148 | 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) 149 | 150 | cli = WstoolCLI() 151 | self.assertEqual(0, cli.cmd_status(directory, [])) 152 | 153 | def test_wstool_status_bzr_outside(self): 154 | """Test status output for bzr when run outside workspace""" 155 | 156 | cmd = ["wstool", "status", "-t", "ws"] 157 | os.chdir(self.test_root_path) 158 | sys.stdout = output = StringIO() 159 | wstool_main(cmd) 160 | sys.stdout = sys.__stdout__ 161 | output = output.getvalue() 162 | 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) 163 | 164 | cli = WstoolCLI() 165 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), [])) 166 | 167 | def test_wstool_status_bzr_untracked(self): 168 | """Test status output for bzr when run outside workspace""" 169 | 170 | cmd = ["wstool", "status", "-t", "ws", "--untracked"] 171 | os.chdir(self.test_root_path) 172 | sys.stdout = output = StringIO() 173 | wstool_main(cmd) 174 | sys.stdout = sys.__stdout__ 175 | output = output.getvalue() 176 | 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) 177 | 178 | cli = WstoolCLI() 179 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"])) 180 | 181 | def test_wstool_info_bzr(self): 182 | cmd = ["wstool", "info", "-t", "ws"] 183 | os.chdir(self.test_root_path) 184 | sys.stdout = output = StringIO() 185 | wstool_main(cmd) 186 | output = output.getvalue() 187 | tokens = _nth_line_split(-2, output) 188 | self.assertEqual(['clone', 'M', 'bzr'], tokens[0:3], output) 189 | 190 | cli = WstoolCLI() 191 | self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), [])) 192 | 193 | 194 | class WstoolInfoBzrTest(AbstractSCMTest): 195 | 196 | def setUp(self): 197 | AbstractSCMTest.setUp(self) 198 | remote_path = os.path.join(self.test_root_path, "remote") 199 | os.makedirs(remote_path) 200 | 201 | # create a "remote" repo 202 | subprocess.check_call(["bzr", "init"], cwd=remote_path) 203 | subprocess.check_call(["touch", "test.txt"], cwd=remote_path) 204 | subprocess.check_call(["bzr", "add", "test.txt"], cwd=remote_path) 205 | subprocess.check_call(["bzr", "commit", "-m", "modified"], cwd=remote_path) 206 | self.version_init = "1" 207 | subprocess.check_call(["bzr", "tag", "footag"], cwd=remote_path) 208 | subprocess.check_call(["touch", "test2.txt"], cwd=remote_path) 209 | subprocess.check_call(["bzr", "add", "test2.txt"], cwd=remote_path) 210 | subprocess.check_call(["bzr", "commit", "-m", "modified"], cwd=remote_path) 211 | self.version_end = "2" 212 | 213 | # wstool the remote repo and fake ros 214 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- bzr: {local-name: clone, uri: ../remote}") 215 | 216 | cmd = ["wstool", "update"] 217 | os.chdir(self.local_path) 218 | sys.stdout = output = StringIO() 219 | wstool_main(cmd) 220 | output = output.getvalue() 221 | sys.stdout = sys.__stdout__ 222 | 223 | def test_rosinstall_detailed_locapath_info(self): 224 | cmd = ["wstool", "info", "-t", "ws"] 225 | os.chdir(self.test_root_path) 226 | sys.stdout = output = StringIO() 227 | wstool_main(cmd) 228 | output = output.getvalue() 229 | 230 | tokens = _nth_line_split(-2, output) 231 | self.assertEqual(['clone', 'bzr', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens) 232 | 233 | clone_path = os.path.join(self.local_path, "clone") 234 | # make local modifications check 235 | subprocess.check_call(["rm", "test2.txt"], cwd=clone_path) 236 | sys.stdout = output = StringIO() 237 | wstool_main(cmd) 238 | output = output.getvalue() 239 | tokens = _nth_line_split(-2, output) 240 | self.assertEqual(['clone', 'M', 'bzr', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens) 241 | 242 | subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path) 243 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- bzr: {local-name: clone, uri: ../remote, version: \"footag\"}") 244 | os.chdir(self.test_root_path) 245 | sys.stdout = output = StringIO() 246 | wstool_main(cmd) 247 | output = output.getvalue() 248 | tokens = _nth_line_split(-2, output) 249 | self.assertEqual(['clone', 'MV', 'bzr', 'footag', self.version_end, "(%s)" % self.version_init, os.path.join(self.test_root_path, 'remote')], tokens) 250 | 251 | subprocess.check_call(["rm", "-rf", "clone"], cwd=self.local_path) 252 | os.chdir(self.test_root_path) 253 | sys.stdout = output = StringIO() 254 | wstool_main(cmd) 255 | output = output.getvalue() 256 | tokens = _nth_line_split(-2, output) 257 | self.assertEqual(['clone', 'x', 'bzr', 'footag', os.path.join(self.test_root_path, 'remote')], tokens) 258 | -------------------------------------------------------------------------------- /test/local/test_diff_functions_hg.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | from __future__ import unicode_literals 34 | 35 | import os 36 | import sys 37 | from test.io_wrapper import StringIO 38 | import subprocess 39 | 40 | import wstool 41 | import wstool.helpers 42 | import wstool.wstool_cli 43 | from wstool.wstool_cli import WstoolCLI 44 | from wstool.wstool_cli import wstool_main 45 | 46 | import test.scm_test_base 47 | from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split 48 | 49 | 50 | def create_hg_repo(remote_path): 51 | # create a "remote" repo 52 | subprocess.check_call(["hg", "init"], cwd=remote_path) 53 | subprocess.check_call(["touch", "fixed.txt"], cwd=remote_path) 54 | subprocess.check_call(["touch", "modified.txt"], cwd=remote_path) 55 | subprocess.check_call(["touch", "modified-fs.txt"], cwd=remote_path) 56 | subprocess.check_call(["touch", "deleted.txt"], cwd=remote_path) 57 | subprocess.check_call(["touch", "deleted-fs.txt"], cwd=remote_path) 58 | subprocess.check_call(["hg", "add", "fixed.txt"], cwd=remote_path) 59 | subprocess.check_call(["hg", "add", "modified.txt"], cwd=remote_path) 60 | subprocess.check_call(["hg", "add", "modified-fs.txt"], cwd=remote_path) 61 | subprocess.check_call(["hg", "add", "deleted.txt"], cwd=remote_path) 62 | subprocess.check_call(["hg", "add", "deleted-fs.txt"], cwd=remote_path) 63 | subprocess.check_call(["hg", "commit", "-m", "modified"], cwd=remote_path) 64 | 65 | 66 | def modify_hg_repo(clone_path): 67 | # make local modifications 68 | subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path) 69 | subprocess.check_call(["hg", "rm", "deleted.txt"], cwd=clone_path) 70 | _add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n") 71 | _add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n") 72 | _add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n") 73 | _add_to_file(os.path.join(clone_path, "added.txt"), "flam\n") 74 | subprocess.check_call(["hg", "add", "added.txt"], cwd=clone_path) 75 | 76 | 77 | class WstoolDiffHgTest(AbstractSCMTest): 78 | 79 | @classmethod 80 | def setUpClass(self): 81 | AbstractSCMTest.setUpClass() 82 | remote_path = os.path.join(self.test_root_path, "remote") 83 | os.makedirs(remote_path) 84 | 85 | create_hg_repo(remote_path) 86 | 87 | # wstool the remote repo and fake ros 88 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- hg: {local-name: clone, uri: ../remote}") 89 | 90 | cmd = ["wstool", "update", "-t", "ws"] 91 | os.chdir(self.test_root_path) 92 | wstool_main(cmd) 93 | 94 | clone_path = os.path.join(self.local_path, "clone") 95 | 96 | modify_hg_repo(clone_path) 97 | 98 | def check_diff_output(self, output): 99 | # sha ids are always same with hg 100 | 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) 101 | 102 | def test_wstool_diff_hg_outside(self): 103 | """Test diff output for hg when run outside workspace""" 104 | 105 | cmd = ["wstool", "diff", "-t", "ws"] 106 | os.chdir(self.test_root_path) 107 | sys.stdout = output = StringIO() 108 | wstool_main(cmd) 109 | sys.stdout = sys.__stdout__ 110 | output = output.getvalue() 111 | self.check_diff_output(output) 112 | 113 | cli = WstoolCLI() 114 | self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), [])) 115 | 116 | def test_wstool_diff_hg_inside(self): 117 | """Test diff output for hg when run inside workspace""" 118 | directory = self.test_root_path + "/ws" 119 | 120 | cmd = ["wstool", "diff"] 121 | os.chdir(directory) 122 | sys.stdout = output = StringIO() 123 | wstool_main(cmd) 124 | output = output.getvalue() 125 | sys.stdout = sys.__stdout__ 126 | self.check_diff_output(output) 127 | 128 | cli = WstoolCLI() 129 | self.assertEqual(0, cli.cmd_status(directory, [])) 130 | 131 | def test_wstool_status_hg_inside(self): 132 | """Test status output for hg when run inside workspace""" 133 | directory = self.test_root_path + "/ws" 134 | 135 | cmd = ["wstool", "status"] 136 | os.chdir(directory) 137 | sys.stdout = output = StringIO() 138 | wstool_main(cmd) 139 | output = output.getvalue() 140 | sys.stdout = sys.__stdout__ 141 | 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) 142 | 143 | cli = WstoolCLI() 144 | self.assertEqual(0, cli.cmd_diff(directory, [])) 145 | 146 | def test_wstool_status_hg_outside(self): 147 | """Test status output for hg when run outside workspace""" 148 | 149 | cmd = ["wstool", "status", "-t", "ws"] 150 | os.chdir(self.test_root_path) 151 | sys.stdout = output = StringIO() 152 | wstool_main(cmd) 153 | sys.stdout = sys.__stdout__ 154 | output = output.getvalue() 155 | 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) 156 | 157 | cli = WstoolCLI() 158 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), [])) 159 | 160 | def test_wstool_status_hg_untracked(self): 161 | """Test untracked status output for hg when run outside workspace""" 162 | cmd = ["wstool", "status", "-t", "ws", "--untracked"] 163 | os.chdir(self.test_root_path) 164 | sys.stdout = output = StringIO() 165 | wstool_main(cmd) 166 | sys.stdout = sys.__stdout__ 167 | output = output.getvalue() 168 | 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) 169 | 170 | cli = WstoolCLI() 171 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"])) 172 | 173 | def test_wstool_info_hg(self): 174 | cmd = ["wstool", "info", "-t", "ws"] 175 | os.chdir(self.test_root_path) 176 | sys.stdout = output = StringIO() 177 | wstool_main(cmd) 178 | output = output.getvalue() 179 | tokens = _nth_line_split(-2, output) 180 | self.assertEqual(['clone', 'M', 'hg'], tokens[0:3], output) 181 | 182 | cli = WstoolCLI() 183 | self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), [])) 184 | 185 | 186 | class WstoolInfoHgTest(AbstractSCMTest): 187 | 188 | def setUp(self): 189 | AbstractSCMTest.setUp(self) 190 | remote_path = os.path.join(self.test_root_path, "remote") 191 | os.makedirs(remote_path) 192 | 193 | # create a "remote" repo 194 | subprocess.check_call(["hg", "init"], cwd=remote_path) 195 | subprocess.check_call(["touch", "test.txt"], cwd=remote_path) 196 | subprocess.check_call(["hg", "add", "test.txt"], cwd=remote_path) 197 | subprocess.check_call(["hg", "commit", "-m", "modified"], cwd=remote_path) 198 | po = subprocess.Popen(["hg", "log", "--template", "'{node|short}'", "-l1"], cwd=remote_path, stdout=subprocess.PIPE) 199 | self.version_init = po.stdout.read().decode('UTF-8').rstrip("'").lstrip("'") 200 | subprocess.check_call(["hg", "tag", "footag"], cwd=remote_path) 201 | subprocess.check_call(["touch", "test2.txt"], cwd=remote_path) 202 | subprocess.check_call(["hg", "add", "test2.txt"], cwd=remote_path) 203 | subprocess.check_call(["hg", "commit", "-m", "modified"], cwd=remote_path) 204 | po = subprocess.Popen(["hg", "log", "--template", "'{node|short}'", "-l1"], cwd=remote_path, stdout=subprocess.PIPE) 205 | self.version_end = po.stdout.read().decode('UTF-8').rstrip("'").lstrip("'") 206 | 207 | # wstool the remote repo and fake ros 208 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- hg: {local-name: clone, uri: ../remote}") 209 | 210 | cmd = ["wstool", "update"] 211 | os.chdir(self.local_path) 212 | sys.stdout = output = StringIO() 213 | wstool_main(cmd) 214 | output = output.getvalue() 215 | sys.stdout = sys.__stdout__ 216 | 217 | def test_rosinstall_detailed_locapath_info(self): 218 | cmd = ["wstool", "info", "-t", "ws"] 219 | os.chdir(self.test_root_path) 220 | sys.stdout = output = StringIO() 221 | wstool_main(cmd) 222 | output = output.getvalue() 223 | tokens = _nth_line_split(-2, output) 224 | self.assertEqual(['clone', 'hg', 'default', '(-)', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens) 225 | 226 | clone_path = os.path.join(self.local_path, "clone") 227 | # make local modifications check 228 | subprocess.check_call(["hg", "rm", "test2.txt"], cwd=clone_path) 229 | os.chdir(self.test_root_path) 230 | sys.stdout = output = StringIO() 231 | wstool_main(cmd) 232 | output = output.getvalue() 233 | tokens = _nth_line_split(-2, output) 234 | self.assertEqual(['clone', 'M', 'hg', 'default', '(-)', self.version_end, os.path.join(self.test_root_path, 'remote')], tokens) 235 | 236 | subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path) 237 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- hg: {local-name: clone, uri: ../remote, version: \"footag\"}") 238 | os.chdir(self.test_root_path) 239 | sys.stdout = output = StringIO() 240 | wstool_main(cmd) 241 | output = output.getvalue() 242 | tokens = _nth_line_split(-2, output) 243 | self.assertEqual(['clone', 'MV', 'hg', 'default', '(footag)', self.version_end, "(%s)" % self.version_init, os.path.join(self.test_root_path, 'remote')], tokens) 244 | 245 | subprocess.check_call(["rm", "-rf", "clone"], cwd=self.local_path) 246 | os.chdir(self.test_root_path) 247 | sys.stdout = output = StringIO() 248 | wstool_main(cmd) 249 | output = output.getvalue() 250 | tokens = _nth_line_split(-2, output) 251 | self.assertEqual(['clone', 'x', 'hg', '(footag)', os.path.join(self.test_root_path, 'remote')], tokens) 252 | -------------------------------------------------------------------------------- /test/local/test_diff_functions_multi.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | from __future__ import unicode_literals 34 | 35 | import os 36 | import sys 37 | from test.io_wrapper import StringIO 38 | 39 | import wstool 40 | import wstool.helpers 41 | import wstool.wstool_cli 42 | from wstool.wstool_cli import WstoolCLI 43 | from wstool.wstool_cli import wstool_main 44 | 45 | from test.scm_test_base import AbstractSCMTest, _add_to_file 46 | 47 | from test.local.test_diff_functions_svn import create_svn_repo, modify_svn_repo 48 | from test.local.test_diff_functions_git import create_git_repo, modify_git_repo 49 | from test.local.test_diff_functions_hg import create_hg_repo, modify_hg_repo 50 | from test.local.test_diff_functions_bzr import create_bzr_repo, modify_bzr_repo 51 | 52 | 53 | class WstoolDiffMultiTest(AbstractSCMTest): 54 | 55 | @classmethod 56 | def setUpClass(self): 57 | AbstractSCMTest.setUpClass() 58 | remote_path_svn = os.path.join(self.test_root_path, "remote_svn") 59 | remote_path_git = os.path.join(self.test_root_path, "remote_git") 60 | remote_path_bzr = os.path.join(self.test_root_path, "remote_bzr") 61 | remote_path_hg = os.path.join(self.test_root_path, "remote_hg") 62 | os.makedirs(remote_path_git) 63 | os.makedirs(remote_path_svn) 64 | os.makedirs(remote_path_hg) 65 | os.makedirs(remote_path_bzr) 66 | 67 | filler_path = os.path.join(self.test_root_path, "filler") 68 | svn_uri = "file://localhost"+remote_path_svn 69 | 70 | create_svn_repo(self.test_root_path, remote_path_svn, filler_path, svn_uri) 71 | create_git_repo(remote_path_git) 72 | create_hg_repo(remote_path_hg) 73 | create_bzr_repo(remote_path_bzr) 74 | 75 | # wstool the remote repo and fake ros (using git twice to check all overlaps) 76 | rosinstall_spec = """- other: {local-name: ../ros} 77 | - git: {local-name: clone_git, uri: ../remote_git} 78 | - svn: {local-name: clone_svn, uri: '%s'} 79 | - hg: {local-name: clone_hg, uri: ../remote_hg} 80 | - bzr: {local-name: clone_bzr, uri: ../remote_bzr} 81 | - git: {local-name: clone_git2, uri: ../remote_git}""" % svn_uri 82 | 83 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), rosinstall_spec) 84 | 85 | cmd = ["rosws", "update", "-t", "ws"] 86 | os.chdir(self.test_root_path) 87 | wstool_main(cmd) 88 | 89 | clone_path_git = os.path.join(self.local_path, "clone_git") 90 | clone_path_git2 = os.path.join(self.local_path, "clone_git2") 91 | clone_path_svn = os.path.join(self.local_path, "clone_svn") 92 | clone_path_hg = os.path.join(self.local_path, "clone_hg") 93 | clone_path_bzr = os.path.join(self.local_path, "clone_bzr") 94 | 95 | modify_git_repo(clone_path_git2) 96 | modify_git_repo(clone_path_git) 97 | modify_svn_repo(clone_path_svn) 98 | modify_hg_repo(clone_path_hg) 99 | modify_bzr_repo(clone_path_bzr) 100 | 101 | def check_diff_output(self, output): 102 | # this tests that there are proper newlines between diff outputs 103 | # for svn, the order varies, so we check two known variants 104 | self.assertTrue("\nIndex: clone_svn/added.txt" in output, output) 105 | self.assertTrue("\nIndex: clone_svn/added.txt" in output, output) 106 | self.assertTrue("\nIndex: clone_svn/modified.txt" in output, output) 107 | self.assertTrue("\ndiff --git clone_hg/added.txt" in output, output) 108 | self.assertTrue("\n=== added file 'added.txt'\n--- clone_bzr/added.txt" in output, output) 109 | self.assertTrue("\ndiff --git clone_git2/added.txt" in output, output) 110 | 111 | def test_multi_diff_rosinstall_outside(self): 112 | '''Test wstool diff output from outside workspace. 113 | In particular asserts that there are newlines between diffs, and no overlaps''' 114 | cmd = ["wstool", "diff", "-t", "ws"] 115 | os.chdir(self.test_root_path) 116 | sys.stdout = output = StringIO() 117 | wstool_main(cmd) 118 | sys.stdout = sys.__stdout__ 119 | output = output.getvalue() 120 | self.check_diff_output(output) 121 | 122 | def test_multi_diff_wstool_outside(self): 123 | '''Test wstool diff output from outside workspace. 124 | In particular asserts that there are newlines between diffs, and no overlaps''' 125 | cmd = ["wstool", "diff", "-t", "ws"] 126 | os.chdir(self.test_root_path) 127 | sys.stdout = output = StringIO() 128 | wstool_main(cmd) 129 | sys.stdout = sys.__stdout__ 130 | output = output.getvalue() 131 | self.check_diff_output(output) 132 | 133 | cli = WstoolCLI() 134 | self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), [])) 135 | 136 | def test_multi_diff_rosinstall_inside(self): 137 | '''Test wstool diff output from inside workspace. 138 | In particular asserts that there are newlines between diffs, and no overlaps''' 139 | directory = self.test_root_path + "/ws" 140 | cmd = ["wstool", "diff"] 141 | os.chdir(directory) 142 | sys.stdout = output = StringIO() 143 | wstool_main(cmd) 144 | output = output.getvalue() 145 | self.check_diff_output(output) 146 | 147 | def test_multi_diff_wstool_inside(self): 148 | '''Test wstool diff output from inside workspace. 149 | In particular asserts that there are newlines between diffs, and no overlaps''' 150 | directory = self.test_root_path + "/ws" 151 | cmd = ["wstool", "diff"] 152 | os.chdir(directory) 153 | sys.stdout = output = StringIO() 154 | wstool_main(cmd) 155 | output = output.getvalue() 156 | sys.stdout = sys.__stdout__ 157 | self.check_diff_output(output) 158 | 159 | cli = WstoolCLI() 160 | self.assertEqual(0, cli.cmd_diff(directory, [])) 161 | 162 | def test_multi_status_rosinstall_inside(self): 163 | """Test wstool status output when run inside workspace. 164 | In particular asserts that there are newlines between statuses, and no overlaps""" 165 | directory = self.test_root_path + "/ws" 166 | cmd = ["wstool", "status"] 167 | os.chdir(directory) 168 | sys.stdout = output = StringIO() 169 | wstool_main(cmd) 170 | output = output.getvalue() 171 | 172 | 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) 173 | 174 | def test_multi_status_wstool_inside(self): 175 | """Test wstool status output when run inside workspace. 176 | In particular asserts that there are newlines between statuses, and no overlaps""" 177 | directory = self.test_root_path + "/ws" 178 | cmd = ["wstool", "status"] 179 | os.chdir(directory) 180 | sys.stdout = output = StringIO() 181 | wstool_main(cmd) 182 | output = output.getvalue() 183 | sys.stdout = sys.__stdout__ 184 | 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) 185 | 186 | cli = WstoolCLI() 187 | self.assertEqual(0, cli.cmd_diff(directory, [])) 188 | 189 | def test_multi_status_rosinstall_outside(self): 190 | """Test wstool status output when run outside workspace. 191 | In particular asserts that there are newlines between statuses, and no overlaps""" 192 | cmd = ["rosinstall", "status", "-t", "ws"] 193 | os.chdir(self.test_root_path) 194 | sys.stdout = output = StringIO() 195 | wstool_main(cmd) 196 | sys.stdout = output = StringIO() 197 | wstool_main(cmd) 198 | sys.stdout = sys.__stdout__ 199 | output = output.getvalue() 200 | 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) 201 | 202 | def test_multi_status_wstool_outside(self): 203 | """Test wstool status output when run outside workspace. 204 | In particular asserts that there are newlines between statuses, and no overlaps""" 205 | cmd = ["wstool", "status", "-t", "ws"] 206 | os.chdir(self.test_root_path) 207 | sys.stdout = output = StringIO() 208 | wstool_main(cmd) 209 | sys.stdout = sys.__stdout__ 210 | output = output.getvalue() 211 | 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) 212 | 213 | cli = WstoolCLI() 214 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), [])) 215 | 216 | def test_multi_status_untracked(self): 217 | '''tests status output for --untracked. 218 | In particular asserts that there are newlines between statuses, and no overlaps''' 219 | cmd = ["wstool", "status", "-t", "ws", "--untracked"] 220 | os.chdir(self.test_root_path) 221 | sys.stdout = output = StringIO() 222 | wstool_main(cmd) 223 | sys.stdout = sys.__stdout__ 224 | output = output.getvalue() 225 | 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) 226 | 227 | cmd = ["wstool", "status", "-t", "ws", "--untracked"] 228 | os.chdir(self.test_root_path) 229 | sys.stdout = output = StringIO() 230 | wstool_main(cmd) 231 | sys.stdout = sys.__stdout__ 232 | output = output.getvalue() 233 | 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) 234 | 235 | cli = WstoolCLI() 236 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"])) 237 | -------------------------------------------------------------------------------- /test/local/test_diff_functions_svn.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | from __future__ import unicode_literals 34 | 35 | import os 36 | import sys 37 | from test.io_wrapper import StringIO 38 | import subprocess 39 | import re 40 | 41 | import wstool 42 | import wstool.helpers 43 | import wstool.wstool_cli 44 | from wstool.wstool_cli import WstoolCLI 45 | from wstool.wstool_cli import wstool_main 46 | 47 | import test.scm_test_base 48 | from test.scm_test_base import AbstractSCMTest, _add_to_file, _nth_line_split 49 | 50 | 51 | def create_svn_repo(test_root_path, remote_path, filler_path, svn_uri): 52 | # create a "remote" repo 53 | subprocess.check_call(["svnadmin", "create", remote_path], cwd=test_root_path) 54 | subprocess.check_call(["svn", "checkout", svn_uri, filler_path], cwd=test_root_path) 55 | subprocess.check_call(["touch", "fixed.txt"], cwd=filler_path) 56 | subprocess.check_call(["touch", "modified.txt"], cwd=filler_path) 57 | subprocess.check_call(["touch", "modified-fs.txt"], cwd=filler_path) 58 | subprocess.check_call(["touch", "deleted.txt"], cwd=filler_path) 59 | subprocess.check_call(["touch", "deleted-fs.txt"], cwd=filler_path) 60 | subprocess.check_call(["svn", "add", "fixed.txt"], cwd=filler_path) 61 | subprocess.check_call(["svn", "add", "modified.txt"], cwd=filler_path) 62 | subprocess.check_call(["svn", "add", "modified-fs.txt"], cwd=filler_path) 63 | subprocess.check_call(["svn", "add", "deleted.txt"], cwd=filler_path) 64 | subprocess.check_call(["svn", "add", "deleted-fs.txt"], cwd=filler_path) 65 | subprocess.check_call(["svn", "commit", "-m", "modified"], cwd=filler_path) 66 | 67 | 68 | def modify_svn_repo(clone_path): 69 | # make local modifications 70 | subprocess.check_call(["rm", "deleted-fs.txt"], cwd=clone_path) 71 | subprocess.check_call(["svn", "rm", "deleted.txt"], cwd=clone_path) 72 | 73 | #_add_to_file(os.path.join(clone_path, "modified-fs.txt"), "foo\n") 74 | _add_to_file(os.path.join(clone_path, "modified.txt"), "foo\n") 75 | _add_to_file(os.path.join(clone_path, "added-fs.txt"), "tada\n") 76 | _add_to_file(os.path.join(clone_path, "added.txt"), "flam\n") 77 | subprocess.check_call(["svn", "add", "--no-auto-props", "added.txt"], cwd=clone_path) 78 | 79 | 80 | class WstoolDiffSvnTest(AbstractSCMTest): 81 | 82 | @classmethod 83 | def setUpClass(self): 84 | AbstractSCMTest.setUpClass() 85 | remote_path = os.path.join(self.test_root_path, "remote") 86 | filler_path = os.path.join(self.test_root_path, "filler") 87 | 88 | svn_uri = "file://localhost" + remote_path 89 | 90 | create_svn_repo(self.test_root_path, remote_path, filler_path, svn_uri) 91 | 92 | # wstool the remote repo and fake ros 93 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- svn: {local-name: clone, uri: '" + svn_uri + "'}") 94 | 95 | cmd = ["wstool", "update", "-t", "ws"] 96 | os.chdir(self.test_root_path) 97 | wstool_main(cmd) 98 | clone_path = os.path.join(self.local_path, "clone") 99 | 100 | modify_svn_repo(clone_path) 101 | 102 | def check_diff_output(self, output): 103 | # svn 1.9 added the "nonexistent" output, replace it with the 104 | # revision 0 that the test results expect. 105 | output_fixed = re.sub("\(nonexistent\)", "(revision 0)", output) 106 | # svn output order varies between versions 107 | expected = ["""\ 108 | Index: clone/added.txt 109 | =================================================================== 110 | --- clone/added.txt\t(revision 0) 111 | +++ clone/added.txt\t""", 112 | """@@ -0,0 +1 @@ 113 | +flam""", 114 | """\ 115 | Index: clone/modified.txt 116 | =================================================================== 117 | --- clone/modified.txt\t(revision 1) 118 | +++ clone/modified.txt\t(working copy) 119 | @@ -0,0 +1 @@ 120 | +foo"""] 121 | for snippet in expected: 122 | for line in snippet.splitlines(): 123 | # assertIn is not supported in Python2.6 124 | self.assertTrue(line in output_fixed, output) 125 | 126 | def test_wstool_diff_svn_outside(self): 127 | """Test diff output for svn when run outside workspace""" 128 | 129 | cmd = ["wstool", "diff", "-t", "ws"] 130 | os.chdir(self.test_root_path) 131 | sys.stdout = output = StringIO() 132 | wstool_main(cmd) 133 | sys.stdout = sys.__stdout__ 134 | output = output.getvalue() 135 | self.check_diff_output(output) 136 | 137 | cli = WstoolCLI() 138 | self.assertEqual(0, cli.cmd_diff(os.path.join(self.test_root_path, 'ws'), [])) 139 | 140 | def test_wstool_diff_svn_inside(self): 141 | """Test diff output for svn when run inside workspace""" 142 | directory = self.test_root_path + "/ws" 143 | 144 | cmd = ["wstool", "diff"] 145 | os.chdir(directory) 146 | sys.stdout = output = StringIO() 147 | wstool_main(cmd) 148 | output = output.getvalue() 149 | sys.stdout = sys.__stdout__ 150 | self.check_diff_output(output) 151 | 152 | cli = WstoolCLI() 153 | self.assertEqual(0, cli.cmd_status(directory, [])) 154 | 155 | def test_wstool_status_svn_inside(self): 156 | """Test status output for svn when run inside workspace""" 157 | directory = self.test_root_path + "/ws" 158 | 159 | cmd = ["wstool", "status"] 160 | os.chdir(directory) 161 | sys.stdout = output = StringIO() 162 | wstool_main(cmd) 163 | output = output.getvalue() 164 | sys.stdout = sys.__stdout__ 165 | 166 | self.assertStatusListEqual('A clone/added.txt\nD clone/deleted.txt\n! clone/deleted-fs.txt\nM clone/modified.txt\n', output) 167 | 168 | cli = WstoolCLI() 169 | self.assertEqual(0, cli.cmd_diff(directory, [])) 170 | 171 | def test_wstool_status_svn_outside(self): 172 | """Test status output for svn when run outside workspace""" 173 | 174 | cmd = ["wstool", "status", "-t", "ws"] 175 | os.chdir(self.test_root_path) 176 | sys.stdout = output = StringIO() 177 | wstool_main(cmd) 178 | sys.stdout = sys.__stdout__ 179 | output = output.getvalue() 180 | self.assertStatusListEqual('A clone/added.txt\nD clone/deleted.txt\n! clone/deleted-fs.txt\nM clone/modified.txt\n', output) 181 | 182 | cli = WstoolCLI() 183 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), [])) 184 | 185 | def test_wstool_status_svn_untracked(self): 186 | """Test status output for svn when run outside workspace""" 187 | 188 | cmd = ["wstool", "status", "-t", "ws", "--untracked"] 189 | os.chdir(self.test_root_path) 190 | sys.stdout = output = StringIO() 191 | wstool_main(cmd) 192 | sys.stdout = sys.__stdout__ 193 | output = output.getvalue() 194 | 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) 195 | 196 | cli = WstoolCLI() 197 | self.assertEqual(0, cli.cmd_status(os.path.join(self.test_root_path, 'ws'), ["--untracked"])) 198 | 199 | def test_wstool_info_svn(self): 200 | cmd = ["wstool", "info", "-t", "ws"] 201 | os.chdir(self.test_root_path) 202 | sys.stdout = output = StringIO() 203 | wstool_main(cmd) 204 | output = output.getvalue() 205 | tokens = _nth_line_split(-2, output) 206 | self.assertEqual(['clone', 'M', 'svn'], tokens[0:3]) 207 | 208 | cli = WstoolCLI() 209 | self.assertEqual(0, cli.cmd_info(os.path.join(self.test_root_path, 'ws'), [])) 210 | 211 | 212 | class WstoolInfoSvnTest(AbstractSCMTest): 213 | 214 | def setUp(self): 215 | AbstractSCMTest.setUp(self) 216 | remote_path = os.path.join(self.test_root_path, "remote") 217 | filler_path = os.path.join(self.test_root_path, "filler") 218 | self.svn_uri = "file://localhost" + remote_path 219 | 220 | # create a "remote" repo 221 | subprocess.check_call(["svnadmin", "create", remote_path], cwd=self.test_root_path) 222 | subprocess.check_call(["svn", "checkout", self.svn_uri, filler_path], cwd=self.test_root_path) 223 | subprocess.check_call(["touch", "test.txt"], cwd=filler_path) 224 | subprocess.check_call(["svn", "add", "test.txt"], cwd=filler_path) 225 | subprocess.check_call(["svn", "commit", "-m", "modified"], cwd=filler_path) 226 | subprocess.check_call(["touch", "test2.txt"], cwd=filler_path) 227 | subprocess.check_call(["svn", "add", "test2.txt"], cwd=filler_path) 228 | subprocess.check_call(["svn", "commit", "-m", "modified"], cwd=filler_path) 229 | 230 | self.version_init = "-r1" 231 | self.version_end = "-r2" 232 | 233 | # wstool the remote repo and fake ros 234 | _add_to_file(os.path.join(self.local_path, ".rosinstall"), "- other: {local-name: ../ros}\n- svn: {local-name: clone, uri: '" + self.svn_uri + "'}") 235 | 236 | cmd = ["wstool", "update"] 237 | os.chdir(self.local_path) 238 | sys.stdout = output = StringIO() 239 | wstool_main(cmd) 240 | output = output.getvalue() 241 | sys.stdout = sys.__stdout__ 242 | 243 | def test_rosinstall_detailed_locapath_info(self): 244 | cmd = ["wstool", "info", "-t", "ws"] 245 | os.chdir(self.test_root_path) 246 | sys.stdout = output = StringIO() 247 | wstool_main(cmd) 248 | output = output.getvalue() 249 | tokens = _nth_line_split(-2, output) 250 | self.assertEqual(['clone', 'svn', self.version_end, self.svn_uri], tokens) 251 | 252 | clone_path = os.path.join(self.local_path, "clone") 253 | # make local modifications check 254 | subprocess.check_call(["touch", "test3.txt"], cwd=clone_path) 255 | subprocess.check_call(["svn", "add", "test3.txt"], cwd=clone_path) 256 | os.chdir(self.test_root_path) 257 | sys.stdout = output = StringIO() 258 | wstool_main(cmd) 259 | output = output.getvalue() 260 | tokens = _nth_line_split(-2, output) 261 | self.assertEqual(['clone', 'M', 'svn', self.version_end, self.svn_uri], tokens) 262 | 263 | subprocess.check_call(["rm", ".rosinstall"], cwd=self.local_path) 264 | _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\"}") 265 | os.chdir(self.test_root_path) 266 | sys.stdout = output = StringIO() 267 | wstool_main(cmd) 268 | output = output.getvalue() 269 | tokens = _nth_line_split(-2, output) 270 | self.assertEqual(['clone', 'MV', 'svn', '1', '(-)', self.version_end, "(%s)" % self.version_init, self.svn_uri], tokens) 271 | 272 | subprocess.check_call(["rm", "-rf", "clone"], cwd=self.local_path) 273 | os.chdir(self.test_root_path) 274 | sys.stdout = output = StringIO() 275 | wstool_main(cmd) 276 | output = output.getvalue() 277 | tokens = _nth_line_split(-2, output) 278 | self.assertEqual(['clone', 'x', 'svn', '(-)', self.svn_uri], tokens) 279 | -------------------------------------------------------------------------------- /test/local/test_export.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import datetime 34 | import os 35 | import sys 36 | import subprocess 37 | 38 | from test.io_wrapper import StringIO 39 | 40 | from wstool.wstool_cli import wstool_main 41 | 42 | from test.scm_test_base import AbstractSCMTest, _add_to_file, get_git_hash 43 | from test.local.test_diff_functions_git import create_git_repo, modify_git_repo 44 | 45 | 46 | class WstoolExportTest(AbstractSCMTest): 47 | 48 | @classmethod 49 | def setUpClass(self): 50 | AbstractSCMTest.setUpClass() 51 | self.remote_path = os.path.join(self.test_root_path, 'remote') 52 | self.new_remote_path = os.path.join(self.test_root_path, 'fooo') 53 | self.version = 'master' 54 | self.branch = 'test_branch' 55 | self.date = datetime.date.today().isoformat() 56 | os.makedirs(self.remote_path) 57 | 58 | create_git_repo(self.remote_path) 59 | 60 | # wstool the remote repo and fake ros 61 | entry = '''\ 62 | - other: {local-name: ../ros} 63 | - git: {local-name: clone, uri: ../remote, version: %s} 64 | ''' % self.version 65 | _add_to_file(os.path.join(self.local_path, '.rosinstall'), entry) 66 | 67 | cmd = ['wstool', 'update', '-t', 'ws'] 68 | os.chdir(self.test_root_path) 69 | wstool_main(cmd) 70 | 71 | self.clone_path = os.path.join(self.local_path, 'clone') 72 | 73 | modify_git_repo(self.clone_path) 74 | 75 | subprocess.check_call(['git', 'checkout', '-b', self.branch], 76 | cwd=self.clone_path) 77 | subprocess.check_call(['git', 'remote', 'set-url', 'origin', 78 | self.new_remote_path], cwd=self.clone_path) 79 | 80 | @staticmethod 81 | def exp(*args): 82 | return '''\ 83 | # THIS IS AN AUTOGENERATED FILE, LAST GENERATED USING wstool ON %s 84 | - git: 85 | local-name: clone 86 | uri: %s 87 | version: %s 88 | 89 | ''' % args 90 | 91 | def helper(self, spec, exact, expected_output): 92 | cmd = ['wstool', 'export', '-t', 'ws'] 93 | if spec: 94 | cmd += ['--spec'] 95 | if exact: 96 | cmd += ['--exact'] 97 | 98 | os.chdir(self.test_root_path) 99 | sys.stdout = output = StringIO() 100 | wstool_main(cmd) 101 | sys.stdout = sys.__stdout__ 102 | output = output.getvalue().encode('utf-8') 103 | expected_output = expected_output.encode('utf-8') 104 | self.assertEqual(expected_output, output) 105 | 106 | def test_wstool_export(self): 107 | o = self.exp(self.date, self.new_remote_path, self.branch) 108 | self.helper(False, False, o) 109 | 110 | def test_wstool_export_spec(self): 111 | o = self.exp(self.date, self.remote_path, self.version) 112 | self.helper(True, False, o) 113 | 114 | def test_wstool_export_exact(self): 115 | uuid = get_git_hash(self.clone_path) 116 | o = self.exp(self.date, self.new_remote_path, uuid) 117 | self.helper(False, True, o) 118 | 119 | def test_wstool_export_spec_exact(self): 120 | uuid = get_git_hash(self.clone_path) 121 | o = self.exp(self.date, self.remote_path, uuid) 122 | self.helper(True, True, o) 123 | -------------------------------------------------------------------------------- /test/local/test_interation.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import os 34 | 35 | import wstool 36 | import wstool.multiproject_cmd 37 | import wstool.ui 38 | 39 | 40 | from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict 41 | 42 | 43 | class FakeUi(wstool.ui.Ui): 44 | def __init__(self, path='', mode='skip', prompt_result='y'): 45 | self.path = path 46 | self.mode = mode 47 | 48 | def get_backup_path(self): 49 | return path 50 | 51 | def prompt_del_abort_retry(self, prompt, allow_skip=False): 52 | return mode 53 | 54 | def get_input(self, prompt): 55 | return prompt_result 56 | 57 | 58 | class RosinstallInteractive(AbstractFakeRosBasedTest): 59 | """tests with possible User Interaction, using mock to simulate user input""" 60 | 61 | def setUp(self): 62 | self.old_ui = wstool.ui.Ui.get_ui() 63 | wstool.ui.Ui.set_ui(FakeUi()) 64 | 65 | def tearDown(self): 66 | wstool.ui.Ui.set_ui(self.old_ui) 67 | 68 | def test_twice_with_relpath(self): 69 | """runs wstool with generated self.simple_rosinstall to create local wstool env 70 | and creates a directory for a second local wstool env""" 71 | AbstractFakeRosBasedTest.setUp(self) 72 | 73 | self.rel_uri_rosinstall = os.path.join(self.test_root_path, "rel_uri.rosinstall") 74 | _create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path), 75 | _create_config_elt_dict("git", "gitrepo", os.path.relpath(self.git_path, self.directory))], 76 | self.rel_uri_rosinstall) 77 | 78 | config = wstool.multiproject_cmd.get_config(self.directory, [self.rel_uri_rosinstall, self.ros_path]) 79 | wstool.multiproject_cmd.cmd_info(config) 80 | wstool.multiproject_cmd.cmd_find_unmanaged_repos 81 | wstool.multiproject_cmd.cmd_install_or_update(config) 82 | 83 | config = wstool.multiproject_cmd.get_config(self.directory, [self.rel_uri_rosinstall, self.ros_path]) 84 | wstool.multiproject_cmd.cmd_install_or_update(config) 85 | 86 | self.rel_uri_rosinstall2 = os.path.join(self.test_root_path, "rel_uri.wstool2") 87 | # switch URIs to confuse config 88 | _create_yaml_file([_create_config_elt_dict("git", "ros", os.path.relpath(self.git_path, self.directory)), 89 | _create_config_elt_dict("git", "gitrepo", self.ros_path)], 90 | self.rel_uri_rosinstall2) 91 | 92 | config = wstool.multiproject_cmd.get_config(self.directory, [self.rel_uri_rosinstall, self.ros_path]) 93 | wstool.multiproject_cmd.cmd_install_or_update(config) 94 | -------------------------------------------------------------------------------- /test/local/test_multiproject_functions.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import os 34 | import unittest 35 | 36 | from wstool.common import DistributedWork, WorkerThread, normabspath,\ 37 | is_web_uri, select_elements, select_element, normalize_uri, realpath_relation,\ 38 | conditional_abspath, string_diff, MultiProjectException 39 | 40 | 41 | class FooThing: 42 | def __init__(self, el, result=None): 43 | self.element = el 44 | self.done = False 45 | self.result = result 46 | 47 | def do_work(self): 48 | self.done = True 49 | return self.result 50 | 51 | def get_path_spec(self): 52 | return self.element 53 | 54 | def get_local_name(self): 55 | return 'bar' 56 | 57 | 58 | class MockElement: 59 | def __init__(self, localname, path): 60 | self.localname = localname 61 | self.path = path 62 | 63 | def get_local_name(self): 64 | return self.localname 65 | 66 | def get_path(self): 67 | return self.path 68 | 69 | 70 | class FunctionsTest(unittest.TestCase): 71 | 72 | def test_normabspath(self): 73 | base = "/foo/bar" 74 | self.assertEqual("/foo/bar", normabspath('.', base)) 75 | self.assertEqual("/foo/bar", normabspath('foo/..', base)) 76 | self.assertEqual("/foo/bar", normabspath(base, base)) 77 | self.assertEqual("/foo", normabspath("/foo", base)) 78 | self.assertEqual("/foo/bar/bim", normabspath('bim', base)) 79 | self.assertEqual("/foo", normabspath('..', base)) 80 | 81 | def test_is_web_uri(self): 82 | self.assertTrue(is_web_uri('http://foo.com')) 83 | self.assertTrue(is_web_uri('http://foo.com/bar')) 84 | self.assertTrue(is_web_uri('http://foo.com:42')) 85 | self.assertTrue(is_web_uri('http://foo.com:42/bar')) 86 | self.assertTrue(is_web_uri('ssh://foo.com')) 87 | self.assertTrue(is_web_uri('lp:foo.com')) 88 | self.assertTrue(is_web_uri('git://foo.com')) 89 | self.assertTrue(is_web_uri('git+ssh://foo.com:foo')) 90 | self.assertTrue(is_web_uri('user@foo:foo/bar')) 91 | self.assertFalse(is_web_uri('foo/bar')) 92 | self.assertFalse(is_web_uri('bar')) 93 | self.assertFalse(is_web_uri('')) 94 | self.assertFalse(is_web_uri(None)) 95 | 96 | def test_normalize_uri(self): 97 | self.assertEqual('/foo', normalize_uri('/foo', None)) 98 | self.assertEqual(None, normalize_uri(None, None)) 99 | self.assertEqual('/bar/foo', normalize_uri('foo', '/bar')) 100 | self.assertEqual('http://foo.com', normalize_uri('http://foo.com', None)) 101 | 102 | def test_string_diff(self): 103 | self.assertEqual('', string_diff(None, None)) 104 | self.assertEqual('foo', string_diff('foo', 'foo')) 105 | self.assertEqual('foo3', string_diff('foo', 'foo3')) 106 | self.assertEqual( 107 | '...7890foo3', 108 | string_diff('12345678901234567890foo', 109 | '12345678901234567890foo3')) 110 | self.assertEqual( 111 | '...7890foo3', 112 | string_diff('12345678901234567890foo4', 113 | '12345678901234567890foo3')) 114 | self.assertEqual( 115 | '...7890foo3', 116 | string_diff('12345678901234567890foo45', 117 | '12345678901234567890foo3')) 118 | self.assertEqual( 119 | '...4567890foo123456789123456789', 120 | string_diff('12345678901234567890', 121 | '12345678901234567890foo123456789123456789')) 122 | 123 | self.assertEqual("['foo']", string_diff(['foo'], ['foo'])) 124 | self.assertEqual("['bar']", string_diff(['foo'], ['bar'])) 125 | 126 | def test_conditional_abspath(self): 127 | path = "foo" 128 | self.assertEqual(os.path.normpath(os.path.join(os.getcwd(), path)), conditional_abspath(path)) 129 | path = "http://someuri.com" 130 | self.assertEqual("http://someuri.com", conditional_abspath(path)) 131 | 132 | def test_abspath_overlap(self): 133 | base = "/foo/bar" 134 | # simple 135 | self.assertEqual('SAME_AS', realpath_relation("/foo", "/foo")) 136 | self.assertEqual('SAME_AS', realpath_relation("/", "/")) 137 | # denormalized 138 | self.assertEqual('SAME_AS', realpath_relation("/foo/.", "/foo/bar/../")) 139 | # subdir 140 | self.assertEqual('PARENT_OF', realpath_relation("/foo", "/foo/bar/baz/bam")) 141 | self.assertEqual('CHILD_OF', realpath_relation("/foo/bar/baz/bam", "/foo")) 142 | ## Negatives 143 | self.assertEqual(None, realpath_relation("/foo", "/bar")) 144 | self.assertEqual(None, realpath_relation("/foo", "/foo2")) 145 | self.assertEqual(None, realpath_relation("/foo/bar", "/foo/ba")) 146 | self.assertEqual(None, realpath_relation("/foo/ba", "/foo/bar/baz")) 147 | self.assertEqual(None, realpath_relation("/foo/bar/baz", "/foo/ba")) 148 | 149 | def test_select_element(self): 150 | self.assertEqual(None, select_element(None, None)) 151 | self.assertEqual(None, select_element([], None)) 152 | mock1 = MockElement('foo', '/test/path1') 153 | mock2 = MockElement('bar', '/test/path2') 154 | mock3 = MockElement('baz', '/test/path3') 155 | self.assertEqual(None, select_element([], 'pin')) 156 | self.assertEqual(None, select_element([mock1], 'pin')) 157 | self.assertEqual(None, select_element([mock1, mock3], 'pin')) 158 | 159 | self.assertEqual('bar', select_element([mock1, mock2, mock3], 'bar').get_local_name()) 160 | self.assertEqual('bar', select_element([mock1, mock2, mock3], '/test/path2').get_local_name()) 161 | self.assertEqual('bar', select_element([mock1, mock2, mock3], '/test/../foo/../test/path2/').get_local_name()) 162 | 163 | def test_worker_thread(self): 164 | try: 165 | w = WorkerThread(None, None, None) 166 | self.fail("expected Exception") 167 | except MultiProjectException: 168 | pass 169 | try: 170 | w = WorkerThread(FooThing(el=None), 2, 3) 171 | self.fail("expected Exception") 172 | except MultiProjectException: 173 | pass 174 | thing = FooThing(FooThing(None)) 175 | result = [None] 176 | w = WorkerThread(thing, result, 0) 177 | self.assertEqual(thing.done, False) 178 | w.run() 179 | self.assertEqual(thing.done, True, result) 180 | self.assertEqual(True, 'error' in result[0]) 181 | 182 | thing = FooThing(FooThing(None), result={'done': True}) 183 | result = [None] 184 | w = WorkerThread(thing, result, 0) 185 | self.assertEqual(thing.done, False) 186 | w.run() 187 | self.assertEqual(thing.done, True, result) 188 | self.assertEqual(False, 'error' in result[0], result) 189 | 190 | def test_distributed_work_init(self): 191 | work = DistributedWork(capacity=200) 192 | self.assertEqual(10, work.num_threads) 193 | work = DistributedWork(capacity=3, num_threads=5) 194 | self.assertEqual(3, work.num_threads) 195 | work = DistributedWork(capacity=5, num_threads=3) 196 | self.assertEqual(3, work.num_threads) 197 | work = DistributedWork(capacity=3, num_threads=-1) 198 | self.assertEqual(3, work.num_threads) 199 | 200 | def test_distributed_work(self): 201 | work = DistributedWork(3) 202 | 203 | thing1 = FooThing(FooThing(FooThing(None)), result={'done': True}) 204 | thing2 = FooThing(FooThing(FooThing(None)), result={'done': True}) 205 | thing3 = FooThing(FooThing(FooThing(None)), result={'done': True}) 206 | self.assertEqual(3, len(work.outputs)) 207 | work.add_thread(thing1) 208 | self.assertEqual(1, len(work.threads)) 209 | work.add_thread(thing2) 210 | self.assertEqual(2, len(work.threads)) 211 | work.add_thread(thing3) 212 | self.assertEqual(3, len(work.threads)) 213 | self.assertEqual(thing1.done, False) 214 | self.assertEqual(thing2.done, False) 215 | self.assertEqual(thing3.done, False) 216 | output = work.run() 217 | self.assertEqual(False, 'error' in output[0], output) 218 | self.assertEqual(False, 'error' in output[1], output) 219 | self.assertEqual(False, 'error' in output[2], output) 220 | 221 | def test_select_elements(self): 222 | self.assertEqual([], select_elements(None, None)) 223 | mock1 = MockElement('foo', '/test/path1') 224 | mock2 = MockElement('bar', '/test/path2') 225 | mock3 = MockElement('baz', '/test/path3') 226 | 227 | class FakeConfig(): 228 | def get_config_elements(self): 229 | return [mock1, mock2, mock3] 230 | 231 | def get_base_path(self): 232 | return '/foo/bar' 233 | self.assertEqual([mock1, mock2, mock3], 234 | select_elements(FakeConfig(), None)) 235 | self.assertEqual([mock2], 236 | select_elements(FakeConfig(), ['bar'])) 237 | self.assertEqual([mock1, mock2, mock3], 238 | select_elements(FakeConfig(), ['/foo/bar'])) 239 | self.assertRaises(MultiProjectException, select_elements, FakeConfig(), ['bum']) 240 | self.assertRaises(MultiProjectException, select_elements, FakeConfig(), ['foo', 'bum', 'bar']) 241 | self.assertRaises(MultiProjectException, select_elements, FakeConfig(), ['bu*']) 242 | -------------------------------------------------------------------------------- /test/local/test_rosinstall_options.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import os 34 | import copy 35 | import tempfile 36 | 37 | import wstool 38 | from wstool.wstool_cli import wstool_main 39 | from wstool.common import MultiProjectException 40 | 41 | from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict, _create_git_repo 42 | 43 | 44 | class RosinstallOptionsTest(AbstractFakeRosBasedTest): 45 | """Test command line option for failure behavior""" 46 | 47 | @classmethod 48 | def setUpClass(self): 49 | AbstractFakeRosBasedTest.setUpClass() 50 | 51 | # create another repo in git 52 | self.git_path2 = os.path.join(self.test_root_path, "gitrepo2") 53 | _create_git_repo(self.git_path2) 54 | self.simple_changed_uri_rosinstall = os.path.join(self.test_root_path, "simple_changed_uri.rosinstall") 55 | # same local name for gitrepo, different uri 56 | _create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path), 57 | _create_config_elt_dict("git", "gitrepo", self.git_path2)], 58 | self.simple_changed_uri_rosinstall) 59 | 60 | # create a broken config 61 | self.broken_rosinstall = os.path.join(self.test_root_path, "broken.rosinstall") 62 | _create_yaml_file([_create_config_elt_dict("other", self.ros_path), 63 | _create_config_elt_dict("hg", "hgrepo", self.hg_path + "invalid")], 64 | self.broken_rosinstall) 65 | 66 | def test_Rosinstall_help(self): 67 | cmd = copy.copy(self.wstool_fn) 68 | cmd.append("-h") 69 | self.assertEqual(0, wstool_main(cmd)) 70 | 71 | def test_rosinstall_delete_changes(self): 72 | cmd = copy.copy(self.wstool_fn) 73 | cmd.extend(["init", self.directory, self.simple_rosinstall]) 74 | self.assertEqual(0, wstool_main(cmd)) 75 | cmd = copy.copy(self.wstool_fn) 76 | cmd.extend(["merge", "-t", self.directory, self.simple_changed_uri_rosinstall, "--merge-replace", "-y"]) 77 | self.assertEqual(0, wstool_main(cmd)) 78 | cmd = copy.copy(self.wstool_fn) 79 | cmd.extend(["update", "-t", self.directory, "--delete-changed-uri"]) 80 | self.assertEqual(0, wstool_main(cmd)) 81 | 82 | def test_rosinstall_abort_changes(self): 83 | cmd = copy.copy(self.wstool_fn) 84 | cmd.extend(["init", self.directory, self.simple_rosinstall]) 85 | self.assertEqual(0, wstool_main(cmd)) 86 | cmd = copy.copy(self.wstool_fn) 87 | cmd.extend(["merge", "-t", self.directory, self.simple_changed_uri_rosinstall, "--merge-replace", "-y"]) 88 | self.assertEqual(0, wstool_main(cmd)) 89 | cmd = copy.copy(self.wstool_fn) 90 | cmd.extend(["update", "-t", self.directory, "--abort-changed-uri"]) 91 | try: 92 | wstool_main(cmd) 93 | self.fail("expected exception") 94 | except MultiProjectException: 95 | pass 96 | 97 | def test_rosinstall_backup_changes(self): 98 | cmd = copy.copy(self.wstool_fn) 99 | cmd.extend(["init", self.directory, self.simple_rosinstall]) 100 | self.assertEqual(0, wstool_main(cmd)) 101 | directory1 = tempfile.mkdtemp() 102 | self.directories["backup1"] = directory1 103 | cmd = copy.copy(self.wstool_fn) 104 | cmd.extend(["merge", "-t", self.directory, self.simple_changed_uri_rosinstall, "--merge-replace", "-y"]) 105 | self.assertEqual(0, wstool_main(cmd)) 106 | cmd = copy.copy(self.wstool_fn) 107 | cmd.extend(["update", "-t", self.directory, "--backup-changed-uri=%s" % directory1]) 108 | self.assertEqual(0, wstool_main(cmd)) 109 | self.assertEqual(len(os.listdir(directory1)), 1) 110 | 111 | def test_rosinstall_change_vcs_type(self): 112 | cmd = copy.copy(self.wstool_fn) 113 | cmd.extend(["init", self.directory, self.simple_rosinstall]) 114 | self.assertEqual(0, wstool_main(cmd)) 115 | cmd = copy.copy(self.wstool_fn) 116 | cmd.extend(["merge", "-t", self.directory, self.simple_changed_vcs_rosinstall, "--merge-replace", "-y"]) 117 | self.assertEqual(0, wstool_main(cmd)) 118 | cmd = copy.copy(self.wstool_fn) 119 | cmd.extend(["update", "-t", self.directory, "--delete-changed-uri"]) 120 | self.assertEqual(0, wstool_main(cmd)) 121 | 122 | def test_rosinstall_invalid_fail(self): 123 | cmd = copy.copy(self.wstool_fn) 124 | cmd.extend([self.directory, "init", self.broken_rosinstall]) 125 | try: 126 | wstool_main(cmd) 127 | self.fail("expected exception") 128 | except MultiProjectException: 129 | pass 130 | 131 | def test_rosinstall_invalid_continue(self): 132 | cmd = copy.copy(self.wstool_fn) 133 | cmd.extend(["-t", self.directory, "merge", self.broken_rosinstall, "--continue-on-error"]) 134 | self.assertTrue(wstool_main(cmd)) 135 | -------------------------------------------------------------------------------- /test/local/test_rosinstall_standalone_functions.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | import os 34 | import unittest 35 | import subprocess 36 | 37 | import wstool.helpers 38 | from wstool.config import Config 39 | from wstool.config_yaml import PathSpec 40 | 41 | from mock import Mock 42 | 43 | 44 | class FunctionsTest(unittest.TestCase): 45 | 46 | def test_get_ros_package_path(self): 47 | config = Config([PathSpec("foo"), 48 | PathSpec("bar")], 49 | ".", 50 | None) 51 | self.assertEqual(list(map(os.path.abspath, ['bar', 52 | 'foo'])), 53 | wstool.helpers.get_ros_package_path(config)) 54 | -------------------------------------------------------------------------------- /test/local/test_shallow_checkout_git.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # Copyright (c) 2017, wstool authors 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright 12 | # notice, this list of conditions and the following disclaimer. 13 | # * Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # * Neither the name of Willow Garage, Inc. nor the names of its 18 | # contributors may be used to endorse or promote products derived 19 | # from this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | 34 | import os 35 | import subprocess 36 | 37 | from wstool.wstool_cli import wstool_main 38 | 39 | from test.scm_test_base import AbstractSCMTest, _add_to_file 40 | 41 | 42 | def create_git_repo(remote_path): 43 | # create a "remote" repo 44 | subprocess.check_call(["git", "init"], cwd=remote_path) 45 | subprocess.check_call(["touch", "1.txt"], cwd=remote_path) 46 | subprocess.check_call(["git", "add", "."], cwd=remote_path) 47 | subprocess.check_call(["git", "commit", "-m", "first commit"], cwd=remote_path) 48 | subprocess.check_call(["touch", "2.txt"], cwd=remote_path) 49 | subprocess.check_call(["git", "add", "."], cwd=remote_path) 50 | subprocess.check_call(["git", "commit", "-m", "second commit"], cwd=remote_path) 51 | subprocess.check_call(["touch", "3.txt"], cwd=remote_path) 52 | subprocess.check_call(["git", "add", "."], cwd=remote_path) 53 | subprocess.check_call(["git", "commit", "-m", "third commit"], cwd=remote_path) 54 | 55 | 56 | class WstoolShallowCheckoutGitTest(AbstractSCMTest): 57 | 58 | @classmethod 59 | def setUpClass(self): 60 | AbstractSCMTest.setUpClass() 61 | remote_path = os.path.join(self.test_root_path, "remote") 62 | os.makedirs(remote_path) 63 | 64 | create_git_repo(remote_path) 65 | 66 | self.rosinstall_filename = os.path.join(self.local_path, "shallow-test.rosinstall") 67 | _add_to_file(self.rosinstall_filename, "- git: {local-name: clone, uri: \"file://" + remote_path + "\"}") 68 | 69 | cmd = ["wstool", "init", "ws-without-shallow", self.rosinstall_filename] 70 | os.chdir(self.test_root_path) 71 | wstool_main(cmd) 72 | 73 | cmd = ["wstool", "init", "--shallow", "ws-with-shallow", self.rosinstall_filename] 74 | os.chdir(self.test_root_path) 75 | wstool_main(cmd) 76 | 77 | def test_history_without_shallow(self): 78 | """Test history of cloned repo without shallow option""" 79 | 80 | clone_path = os.path.join(self.test_root_path, "ws-without-shallow", "clone") 81 | 82 | output = subprocess.check_output(["git", "log", "--pretty=format:%s"], cwd=clone_path) 83 | self.assertEqual(output.decode("ascii"), "third commit\nsecond commit\nfirst commit") 84 | 85 | def test_history_with_shallow(self): 86 | """Test history of cloned repo with shallow option""" 87 | 88 | clone_path = os.path.join(self.test_root_path, "ws-with-shallow", "clone") 89 | 90 | output = subprocess.check_output(["git", "log", "--pretty=format:%s"], cwd=clone_path) 91 | self.assertEqual(output.decode("ascii"), "third commit") 92 | 93 | def test_compare_workspace(self): 94 | """Compare worktrees with/without shallow option""" 95 | 96 | clone_path_without_shallow = os.path.join(self.test_root_path, "ws-without-shallow", "clone") 97 | clone_path_with_shallow = os.path.join(self.test_root_path, "ws-with-shallow", "clone") 98 | 99 | output = subprocess.check_output(["diff", "--exclude=.git", clone_path_without_shallow, clone_path_with_shallow], cwd=self.test_root_path) 100 | self.assertEqual(output.decode("ascii"), "") 101 | -------------------------------------------------------------------------------- /test/local/test_tarfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import copy 3 | import yaml 4 | 5 | import wstool 6 | import wstool.helpers 7 | from wstool.wstool_cli import wstool_main 8 | 9 | from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict, _create_tar_file 10 | 11 | 12 | class RosinstallTarTest(AbstractFakeRosBasedTest): 13 | """Tests for tarfile support""" 14 | 15 | @classmethod 16 | def setUpClass(self): 17 | AbstractFakeRosBasedTest.setUpClass() 18 | 19 | # create another repo in git 20 | 21 | self.tar_path = os.path.join(self.test_root_path, "tarfile.tar.bz2") 22 | _create_tar_file(self.tar_path) 23 | 24 | self.simple_tar_rosinstall = os.path.join(self.test_root_path, "simple_changed_uri.rosinstall") 25 | # same local name for gitrepo, different uri 26 | _create_yaml_file([_create_config_elt_dict("tar", "temptar", uri=self.tar_path, version='temptar')], 27 | self.simple_tar_rosinstall) 28 | 29 | def test_install(self): 30 | cmd = copy.copy(self.wstool_fn) 31 | cmd.extend(["init", self.directory, self.simple_tar_rosinstall]) 32 | self.assertEquals(0, wstool_main(cmd)) 33 | 34 | self.assertTrue(os.path.isdir(os.path.join(self.directory, "temptar"))) 35 | self.assertTrue(os.path.isfile(os.path.join(self.directory, ".rosinstall"))) 36 | stream = open(os.path.join(self.directory, '.rosinstall'), 'r') 37 | yamlsrc = yaml.safe_load(stream) 38 | stream.close() 39 | self.assertEqual(1, len(yamlsrc)) 40 | self.assertEqual('tar', list(yamlsrc[0].keys())[0]) 41 | -------------------------------------------------------------------------------- /test/scm_test_base.py: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD License) 2 | # 3 | # Copyright (c) 2009, Willow Garage, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # * Neither the name of Willow Garage, Inc. nor the names of its 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | # POSSIBILITY OF SUCH DAMAGE. 32 | 33 | from __future__ import unicode_literals 34 | 35 | import os 36 | import copy 37 | import unittest 38 | import subprocess 39 | import tempfile 40 | import shutil 41 | 42 | 43 | def _add_to_file(path, content): 44 | """Util function to append to file to get a modification""" 45 | with open(path, 'ab') as fhand: 46 | fhand.write(content.encode('UTF-8')) 47 | 48 | 49 | def _create_fake_ros_dir(root_path): 50 | """setup fake ros root within root_path/ros""" 51 | ros_path = os.path.join(root_path, "ros") 52 | os.makedirs(ros_path) 53 | bin_path = os.path.join(ros_path, "bin") 54 | os.makedirs(bin_path) 55 | subprocess.check_call(["git", "init"], cwd=ros_path) 56 | _add_to_file(os.path.join(ros_path, "stack.xml"), '') 57 | _add_to_file(os.path.join(ros_path, "setup.sh"), 'export FOO_BAR=`pwd`') 58 | _add_to_file(os.path.join(bin_path, "rosmake"), '#!/usr/bin/env sh') 59 | _add_to_file(os.path.join(bin_path, "rospack"), '#!/usr/bin/env sh') 60 | # even faking rosmake 61 | subprocess.check_call(["chmod", "u+x", os.path.join(bin_path, "rosmake")]) 62 | subprocess.check_call(["chmod", "u+x", os.path.join(bin_path, "rospack")]) 63 | subprocess.check_call(["git", "add", "*"], cwd=ros_path) 64 | subprocess.check_call(["git", "commit", "-m", "initial"], cwd=ros_path) 65 | 66 | 67 | def _create_yaml_file(config_elements, path): 68 | content = '' 69 | for elt in list(config_elements): 70 | content += "- %s:\n" % elt["type"] 71 | if elt["uri"] is not None: 72 | content += " uri: '%s'\n" % elt["uri"] 73 | content += " local-name: '%s'\n" % elt["local-name"] 74 | if elt["version"] is not None: 75 | content += " version: '%s'\n" % elt["version"] 76 | _add_to_file(path, content) 77 | 78 | 79 | def _create_config_elt_dict(scmtype, localname, uri=None, version=None): 80 | element = {} 81 | element["type"] = scmtype 82 | element["uri"] = uri 83 | element["local-name"] = localname 84 | element["version"] = version 85 | return element 86 | 87 | 88 | def _create_git_repo(git_path): 89 | os.makedirs(git_path) 90 | subprocess.check_call(["git", "init"], cwd=git_path) 91 | subprocess.check_call(["touch", "gitfixed.txt"], cwd=git_path) 92 | subprocess.check_call(["git", "add", "*"], cwd=git_path) 93 | subprocess.check_call(["git", "commit", "-m", "initial"], cwd=git_path) 94 | 95 | 96 | def _create_tar_file(tar_file): 97 | parent_path = os.path.dirname(tar_file) 98 | tar_path = os.path.join(parent_path, 'temptar') 99 | os.makedirs(tar_path) 100 | subprocess.check_call(["touch", "tarfixed.txt"], cwd=tar_path) 101 | subprocess.check_call(["tar", "-czf", os.path.basename(tar_file), 'temptar'], cwd=parent_path) 102 | 103 | 104 | def _create_hg_repo(hg_path): 105 | os.makedirs(hg_path) 106 | subprocess.check_call(["hg", "init"], cwd=hg_path) 107 | subprocess.check_call(["touch", "hgfixed.txt"], cwd=hg_path) 108 | subprocess.check_call(["hg", "add", "hgfixed.txt"], cwd=hg_path) 109 | subprocess.check_call(["hg", "commit", "-m", "initial"], cwd=hg_path) 110 | 111 | 112 | def _nth_line_split(n, output): 113 | """returns the last line as list of non-blank tokens""" 114 | lines = output.splitlines() 115 | if len(lines) > 0: 116 | return lines[n].split() 117 | else: 118 | return [] 119 | 120 | 121 | def get_git_hash(git_path): 122 | po = subprocess.Popen(["git", "rev-parse", "HEAD"], cwd=git_path, 123 | stdout=subprocess.PIPE) 124 | return po.stdout.read().decode('UTF-8').rstrip('"\n').lstrip('"\n') 125 | 126 | 127 | # ROSINSTALL_CMD = os.path.join(os.getcwd(), 'scripts/rosinstall') 128 | # ROSWS_CMD = os.path.join(os.getcwd(), 'scripts/rosws') 129 | 130 | 131 | class AbstractRosinstallCLITest(unittest.TestCase): 132 | 133 | """Base class for cli tests""" 134 | @classmethod 135 | def setUpClass(self): 136 | os.environ['GIT_AUTHOR_NAME'] = 'Your Name' 137 | os.environ['GIT_COMMITTER_NAME'] = 'Your Name' 138 | os.environ['GIT_AUTHOR_EMAIL'] = 'name@example.com' 139 | os.environ['EMAIL'] = 'Your Name ' 140 | self.new_environ = copy.copy(os.environ) 141 | self.new_environ["PYTHONPATH"] = os.path.join(os.getcwd(), "src") 142 | if "ROS_WORKSPACE" in self.new_environ: 143 | self.new_environ.pop("ROS_WORKSPACE") 144 | 145 | 146 | class AbstractRosinstallBaseDirTest(AbstractRosinstallCLITest): 147 | """test class where each test method get its own fresh tempdir named self.directory""" 148 | 149 | def setUp(self): 150 | self.directories = {} 151 | self.directory = tempfile.mkdtemp() 152 | self.directories["base"] = self.directory 153 | self.wstool_fn = ["wstool"] 154 | 155 | def tearDown(self): 156 | for d in self.directories: 157 | shutil.rmtree(self.directories[d]) 158 | self.directories = {} 159 | 160 | 161 | class AbstractFakeRosBasedTest(AbstractRosinstallBaseDirTest): 162 | """ 163 | creates some larger infrastructure for testing locally: 164 | a root folder containing all other folders in self.test_root_path 165 | a fake ros folder in self.ros_path 166 | a git repo in self.git_path 167 | a hg repo in self.hg_path 168 | a file named self.simple_rosinstall with ros and gitrepo 169 | a file named self.simple_changed_vcs_rosinstall with ros and hgrepo 170 | """ 171 | 172 | @classmethod 173 | def setUpClass(self): 174 | AbstractRosinstallBaseDirTest.setUpClass() 175 | # create a dir mimicking ros 176 | self.test_root_path = os.path.realpath(tempfile.mkdtemp()) 177 | _create_fake_ros_dir(self.test_root_path) 178 | # create a repo in git 179 | self.ros_path = os.path.join(self.test_root_path, "ros") 180 | self.git_path = os.path.join(self.test_root_path, "gitrepo") 181 | _create_git_repo(self.git_path) 182 | # create a repo in hg 183 | self.hg_path = os.path.join(self.test_root_path, "hgrepo") 184 | _create_hg_repo(self.hg_path) 185 | # create custom wstool files to use as input 186 | self.simple_rosinstall = os.path.join(self.test_root_path, "simple.rosinstall") 187 | _create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path), 188 | _create_config_elt_dict("git", "gitrepo", self.git_path)], 189 | self.simple_rosinstall) 190 | self.simple_changed_vcs_rosinstall = os.path.join(self.test_root_path, "simple_changed_vcs.rosinstall") 191 | _create_yaml_file([_create_config_elt_dict("git", "ros", self.ros_path), 192 | _create_config_elt_dict("hg", "hgrepo", self.hg_path)], 193 | self.simple_changed_vcs_rosinstall) 194 | 195 | @classmethod 196 | def tearDownClass(self): 197 | shutil.rmtree(self.test_root_path) 198 | 199 | 200 | class AbstractSCMTest(AbstractRosinstallCLITest): 201 | """Base class for diff tests, setting up a tempdir self.test_root_path for a whole class""" 202 | @classmethod 203 | def setUpClass(self): 204 | """creates a directory 'ros' mimicking to be a ROS root to rosinstall""" 205 | AbstractRosinstallCLITest.setUpClass() 206 | self.test_root_path = os.path.realpath(tempfile.mkdtemp()) 207 | self.directories = {} 208 | self.directories["root"] = self.test_root_path 209 | 210 | _create_fake_ros_dir(self.test_root_path) 211 | self.local_path = os.path.join(self.test_root_path, "ws") 212 | os.makedirs(self.local_path) 213 | self.curdir = os.getcwd() 214 | 215 | @classmethod 216 | def tearDownClass(self): 217 | os.chdir(self.curdir) 218 | for d in self.directories: 219 | shutil.rmtree(self.directories[d]) 220 | 221 | def assertStatusListEqual(self, listexpect, listactual): 222 | """helper fun to check scm status output while discarding file ordering differences""" 223 | lines_expect = listexpect.splitlines() 224 | lines_actual = listactual.splitlines() 225 | for line in lines_expect: 226 | self.assertTrue(line in lines_actual, 'Missing entry %s in output %s' % (line, listactual)) 227 | for line in lines_actual: 228 | self.assertTrue(line in lines_expect, 'Superflous entry %s in output %s' % (line, listactual)) 229 | 230 | 231 | class UtilTest(unittest.TestCase): 232 | """test to check the methods run by unit test setups""" 233 | 234 | def test_add_to_file(self): 235 | self.test_root_path = tempfile.mkdtemp() 236 | filepath = os.path.join(self.test_root_path, 'foofile') 237 | self.assertFalse(os.path.exists(filepath)) 238 | _add_to_file(filepath, 'foo') 239 | self.assertTrue(os.path.exists(filepath)) 240 | with open(filepath, 'r') as f: 241 | read_data = f.read() 242 | self.assertEqual(read_data, 'foo') 243 | _add_to_file(filepath, 'bar') 244 | with open(filepath, 'r') as f: 245 | read_data = f.read() 246 | self.assertEqual(read_data, 'foobar') 247 | shutil.rmtree(self.test_root_path) 248 | 249 | def test_create_fake_ros(self): 250 | self.test_root_path = tempfile.mkdtemp() 251 | rospath = os.path.join(self.test_root_path, 'ros') 252 | self.assertFalse(os.path.exists(rospath)) 253 | _create_fake_ros_dir(self.test_root_path) 254 | self.assertTrue(os.path.exists(rospath)) 255 | self.assertTrue(os.path.exists(os.path.join(rospath, "setup.sh"))) 256 | self.assertTrue(os.path.exists(os.path.join(rospath, "stack.xml"))) 257 | self.assertTrue(os.path.exists(os.path.join(rospath, ".git"))) 258 | shutil.rmtree(self.test_root_path) 259 | 260 | def test_create_config_elt_dict(self): 261 | scmtype = 'foo' 262 | uri = 'bar' 263 | localname = 'pip' 264 | version = 'pop' 265 | element = _create_config_elt_dict(scmtype, localname, uri, version) 266 | self.assertEqual(element["type"], scmtype) 267 | self.assertEqual(element["uri"], uri) 268 | self.assertEqual(element["local-name"], localname) 269 | self.assertEqual(element["version"], version) 270 | 271 | def test_create_yaml_file(self): 272 | self.test_root_path = tempfile.mkdtemp() 273 | filepath = os.path.join(self.test_root_path, 'foofile') 274 | config_elements = [ 275 | _create_config_elt_dict("other", "foo"), 276 | _create_config_elt_dict("git", "foo", "foouri"), 277 | _create_config_elt_dict("svn", "bar", "baruri", "barversion")] 278 | _create_yaml_file(config_elements, filepath) 279 | with open(filepath, 'r') as f: 280 | read_data = f.read() 281 | self.assertEqual(read_data, """- other: 282 | local-name: 'foo' 283 | - git: 284 | uri: 'foouri' 285 | local-name: 'foo' 286 | - svn: 287 | uri: 'baruri' 288 | local-name: 'bar' 289 | version: 'barversion' 290 | """) 291 | shutil.rmtree(self.test_root_path) 292 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox is the QA gateway before releasing 2 | # it can run against multiple python versions 3 | # it runs commands in virtualenvs prepared with python version and dependencies from setup.[py,cfg] 4 | # 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. 5 | 6 | [tox] 7 | envlist = py27, py34, py35, py36 8 | 9 | [testenv] 10 | # flawed due to https://github.com/tox-dev/tox/issues/149 11 | # deps = -rrequirements.txt 12 | 13 | commands = 14 | pip install .[test] 15 | {envpython} setup.py test 16 | py36: {envpython} setup.py sdist bdist_wheel 17 | --------------------------------------------------------------------------------