diff --git a/_mvn b/_mvn new file mode 100644 index 0000000..2359014 --- /dev/null +++ b/_mvn @@ -0,0 +1,411 @@ +#compdef mvn mvnDebug +# ------------------------------------------------------------------------------ +# Copyright (c) 2010-2011 zsh-users +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ------------------------------------------------------------------------------ +# Authors +# ------- +# +# * Julien Nicoulaud +# +# ------------------------------------------------------------------------------ +# Status +# ------------ +# +# See FIXME and TODO tags. +# +# ------------------------------------------------------------------------------ +# How to tweak +# ------------ +# +# * By default only a few common utility plugins are displayed. To define your +# own list of plugins: +# +# maven_plugins=( +# 'jboss' +# 'tomcat' +# 'gwt:Maven plugin for the Google Web Toolkit' +# 'android:Maven Plugin for Android' +# ) +# zstyle ':completion:*:mvn:*' plugins $maven_plugins +# +# * By default advanced phases are not displayed. To have them displayed: +# +# zstyle ':completion:*:mvn:*' show-advanced-phases true +# +# * To have a better presentation of completions: +# +# zstyle ':completion:*:*:mvn:*:matches' group 'yes' +# zstyle ':completion:*:*:mvn:*:options' description 'yes' +# zstyle ':completion:*:*:mvn:*:options' auto-description '%d' +# zstyle ':completion:*:*:mvn:*:descriptions' format $'\e[1m -- %d --\e[22m' +# zstyle ':completion:*:*:mvn:*:messages' format $'\e[1m -- %d --\e[22m' +# zstyle ':completion:*:*:mvn:*:warnings' format $'\e[1m -- No matches found --\e[22m' +# +# ------------------------------------------------------------------------------ +# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- +# vim: ft=zsh sw=2 ts=2 et +# ------------------------------------------------------------------------------ + + +_mvn() +{ + local curcontext="$curcontext" state line cmds ret=1 maven_version excl_opts + + excl_opts=(-h --help -v --version -ep --encrypt-password -emp --encrypt-master-password) + + _pick_variant -r maven_version maven3='Maven 3' maven2='Maven 2' unknown --version + if [[ $maven_version == 'maven3' ]]; then + opts=( + "($excl_opts -T --threads)"{-T,--threads}'[thread count, for instance 2.0C where C is core multiplied]:thread count:_mvn_thread_counts' + "($excl_opts -t --toolchains)"{-t,--toolchains}'[alternate path for the user toolchains file]:toolchains file:_mvn_toolchains_files' + "($excl_opts -l --log-file)"{-l,--log-file}'[log file to where all build output will go]:log file:_mvn_log_files' + ) + elif [[ $maven_version == 'maven2' ]]; then + opts=( + "($excl_opts -cpu --check-plugin-updates -up --update-plugins -npu --no-plugin-updates -o --offline)"{-cpu,--check-plugin-updates,-up,--update-plugins}'[force upToDate check for any relevant registered plugins]' + "($excl_opts -npr --no-plugin-registry)"{-npr,--no-plugin-registry}'[don'\''t use plugin-registry.xml for plugin versions]' + "($excl_opts -npu --no-plugin-updates -cpu --check-plugin-updates -up --update-plugins)"{-npu,--no-plugin-updates}'[suppress upToDate check for any relevant registered plugins]' + "($excl_opts -r --reactor)"{-r,--reactor}'[dynamically build reactor from subdirectories]:reactor:_mvn_reactors' + ) + else + return ret + fi + + [[ -n ${(M)words:#"-pl"} || -n ${(M)words:#"--projects"} ]] && opts+=( + "($excl_opts -am --also-make)"{-am,--also-make}'[if project list is specified, also build projects required by the list]' + "($excl_opts -amd --also-make-dependents)"{-amd,--also-make-dependents}'[if project list is specified, also build projects that depend on projects on the list]' + ) + + _arguments \ + "(- : *)"{-h,--help}'[display help information]' \ + "(- : *)"{-v,--version}'[display version information]' \ + "(- : *)"{-emp,--encrypt-master-password}'[encrypt master security password]:master password:_mvn_passwords' \ + "(- : *)"{-ep,--encrypt-password}'[encrypt server password]:password:_mvn_passwords' \ + "($excl_opts -B --batch-mode)"{-B,--batch-mode}'[run in non-interactive (batch) mode]' \ + "($excl_opts -V --show-version)"{-V,--show-version}'[display version information without stopping build]' \ + "($excl_opts -q --quiet -X --debug)"{-q,--quiet}'[quiet output, only show errors]' \ + "($excl_opts -X --debug -q --quiet)"{-X,--debug}'[produce execution debug output]' \ + "($excl_opts -N --non-recursive)"{-N,--non-recursive}'[do not recurse into sub-projects]' \ + "($excl_opts -C --strict-checksums -c --lax-checksums)"{-C,--strict-checksums}'[fail the build if checksums don'\''t match]' \ + "($excl_opts -c --lax-checksums -C --strict-checksums)"{-c,--lax-checksums}'[warn if checksums don'\''t match]' \ + "($excl_opts -e --errors)"{-e,--errors}'[produce execution error messages]' \ + "($excl_opts -f --file)"{-f,--file}'[force the use of an alternate POM file]:POM file:_mvn_pom_files' \ + "($excl_opts -s --settings)"{-s,--settings}'[alternate path for the user settings file]:settings file:_mvn_settings_files' \ + "($excl_opts -gs --global-settings)"{-gs,--global-settings}'[alternate path for the global settings file]:global settings file:_mvn_settings_files' \ + "($excl_opts -fae --fail-at-end -ff --fail-fast -fn --fail-never)"{-fae,--fail-at-end}'[only fail the build afterwards, allow all non-impacted builds to continue]' \ + "($excl_opts -ff --fail-fast -fae --fail-at-end -fn --fail-never)"{-ff,--fail-fast}'[stop at first failure in reactorized builds]' \ + "($excl_opts -fn --fail-never -fae --fail-at-end -ff --fail-fast)"{-fn,--fail-never}'[nerver fail the build, regardless of project result]' \ + "($excl_opts -P --activate-profiles)"{-P,--activate-profiles}'[comma-delimited list of profiles to activate]:profile:_mvn_profiles' \ + "($excl_opts -pl --projects)"{-pl,--projects}'[build specified reactor projects instead of all projects]:project list:_mvn_projects -s ,' \ + "($excl_opts -rf --resume-from)"{-rf,--resume-from}'[resume reactor from specified project]:project:_mvn_projects' \ + "($excl_opts -o --offline -U --update-snapshots -cpu --check-plugin-updates -up --update-plugins)"{-o,--offline}'[work offline]' \ + "($excl_opts -U --update-snapshots -nsu --no-snapshot-updates -o --offline)"{-U,--update-snapshots}'[force a check for updated releases and snapshots on remote repositories]' \ + "($excl_opts -nsu --no-snapshot-updates -U --update-snapshots -o --offline)"{-nsu,--no-snapshot-updates}'[Supress SNAPSHOT updates]' \ + "($excl_opts)"{-D-,--define}'[define a system property]:property:_mvn_properties' \ + "$opts[@]" \ + "($excl_opts)*: :_mvn_args" \ + && ret=0 + + return ret +} + +_mvn_args() +{ + local ret=1 + _alternative \ + 'phases:phase:_mvn_phases' \ + 'goals:goal:_mvn_plugin_goals' \ + && ret=0 + return ret +} + +_mvn_phases() +{ + local ret=1 phases + phases=( + 'clean:remove all files generated by the previous build' + 'compile:compile the source code of the project' + 'test:run tests using a suitable unit testing framework' + 'package:take the compiled code and package it in its distributable format, such as a JAR' + 'integration-test:process and deploy the package if necessary into an environment where integration tests can be run' + 'verify:run any checks to verify the package is valid and meets quality criteria' + 'install:install the package into the local repository, for use as a dependency in other projects locally' + 'deploy:done in an integration or release environment, copies the final package to the remote repository' + 'site:generates the projects site documentation' + 'site-deploy:deploys the generated site documentation to the specified web server' + ) + if zstyle -t ":completion:${curcontext}:" show-advanced-phases; then + phases+=( + 'pre-clean:executes processes needed prior to the actual project cleaning' + 'post-clean:executes processes needed to finalize the project cleaning' + 'validate:validate the project is correct and all necessary information is available' + 'initialize:initialize build state, e.g. set properties or create directories' + 'generate-sources:generate any source code for inclusion in compilation' + 'process-sources:process the source code, for example to filter any values' + 'generate-resources:generate resources for inclusion in the package' + 'process-resources:copy and process the resources into the destination directory, ready for packaging' + 'process-classes:post-process the generated files from compilation' + 'generate-test-sources:generate any test source code for inclusion in compilation' + 'process-test-sources:process the test source code, for example to filter any values' + 'generate-test-resources:create resources for testing' + 'process-test-resources:copy and process the resources into the test destination directory' + 'test-compile:compile the test source code into the test destination directory' + 'process-test-classes:post-process the generated files from test compilation' + 'prepare-package:perform any operations necessary to prepare a package before the actual packaging' + 'pre-integration-test:perform actions required before integration tests are executed' + 'post-integration-test:perform actions required after integration tests have been executed' + 'pre-site:executes processes needed prior to the actual project site generation.' + 'post-site:executes processes needed to finalize the site generation, and to prepare for site deployment' + ) + fi + _describe -t 'phases' "phase" phases && ret=0 + return ret +} + +_mvn_plugin_goals() +{ + local ret=1 + if compset -P '*:'; then + _mvn_goals "${IPREFIX%:}" && ret=0 + else + _mvn_plugins && ret=0 + fi + return ret +} + +_mvn_plugins() +{ + local ret=1 plugins + zstyle -a ":completion:${curcontext}:" plugins plugins + [[ ${#plugins} -gt 0 ]] || plugins=( + 'assembly:create archives of your projects sources, classes, dependencies etc. from flexible assembly descriptors' + 'release:release a project with Maven, saving a lot of repetitive, manual work' + 'help:provides goals aimed at helping to make sense out of the build environment.' + 'versions:diagnose and update the versions of components in the POM' + 'deploy:add your artifact(s) to a remote repository for sharing with other developers and project' + 'dependency:provides the capability to manipulate artifacts' + 'archetype:create a Maven project from an existing template called an archetype' + 'site:generate a site for the project' + ) + _describe -t 'plugin' "plugin" plugins -S ':' && ret=0 + return ret +} + +_mvn_goals_caching_policy() +{ + # Rebuild if cache is older than one month. + local -a oldp + oldp=( "$1"(NmM+1) ) + (( $#oldp )) +} + +_mvn_goals() +{ + local ret=1 plugin="$@" update_policy + + zstyle -s ":completion:${curcontext}:" cache-policy update_policy + [[ -z "$update_policy" ]] && zstyle ":completion:${curcontext}:" cache-policy _mvn_goals_caching_policy + + unset goals + if ( [[ ${+goals} -eq 0 ]] || _cache_invalid "mvn/plugins/$plugin" ) && ! _retrieve_cache "mvn/plugins/$plugin"; then + setopt localoptions extendedglob + goals=(${(s:,,,:)${${${(f)${${${(f)${${${${(F)${(S)${(f)"$(_call_program goals $words[1] -N org.apache.maven.plugins:maven-help-plugin:2.1.1:describe -Dplugin=$plugin)"}//#$plugin:/,,,}}:#*BUILD FAILURE*}#*This plugin has*goals#:}%For more information, run \'mvn help:describe*}}//:/\\:}}}// ##/ }// Description\\: /:}}) + [[ ${#goals} -gt 0 ]] && _store_cache "mvn/plugins/$plugin" goals + fi + + [[ ${#goals} -gt 0 ]] && _describe -t "plugin-$plugin-goals" "$plugin plugin goal" goals && ret=0 + + return +} + +_mvn_reactors() +{ + # FIXME No idea what kind of value the "--reactor" option is supposed to take + local ret=1 + _message -e reactors 'reactor' && ret=0 + return ret +} + +_mvn_passwords() +{ + local ret=1 + _message -e passwords 'password' && ret=0 + return ret +} + +_mvn_thread_counts() +{ + local ret=1 thread_counts + thread_counts=( + '1:build with 1 thread' '1C:build with 1 thread per CPU core' + '2:build with 2 threads' '2C:build with 2 threads per CPU core' + '3:build with 3 threads' '3C:build with 3 threads per CPU core' + '4:build with 4 threads' '4C:build with 4 threads per CPU core' + '5:build with 5 threads' '5C:build with 5 threads per CPU core' + '6:build with 6 threads' '6C:build with 6 threads per CPU core' + '7:build with 7 threads' '7C:build with 7 threads per CPU core' + '8:build with 8 threads' '8C:build with 8 threads per CPU core' + ) + _describe -t 'thread-counts' "thread count" thread_counts && ret=0 + return ret +} + +_mvn_pom_files() +{ + local ret=1 + _files -g '*pom*\.xml*' && ret=0 + return ret +} + +_mvn_toolchains_files() +{ + local ret=1 + _files && ret=0 + return ret +} + +_mvn_settings_files() +{ + local ret=1 + _files -g '*settings*\.xml*' && ret=0 + return ret +} + +_mvn_log_files() +{ + local ret=1 + _files && ret=0 + return ret +} + +_mvn_profiles_caching_policy() +{ + # Resolve the cache directory. + local cache_dir + zstyle -s ":completion:${curcontext}:" cache-path cache_dir + : ${cache_dir:=${ZDOTDIR:-$HOME}/.zcompcache} + + # Rebuild if cached file more recent than cache. + local cached_file="${1#$cache_dir}" + [[ -f $cached_file && $cached_file -nt "$1" ]] && return 0 + + # Rebuild if cache is older than one week. + local -a oldp + oldp=( "$1"(Nmw+1) ) + (( $#oldp )) && return 0 + + return 1 +} + +_mvn_profiles() +{ + # FIXME Use "mvn help:all-profiles" output instead of parsing... + # Blocked on http://jira.codehaus.org/browse/MPH-82 and http://jira.codehaus.org/browse/MPH-83 + local ret=1 profs update_policy index settings_file files parent_pom_file cache_name profiles_section + + zstyle -s ":completion:${curcontext}:" cache-policy update_policy + [[ -z "$update_policy" ]] && zstyle ":completion:${curcontext}:" cache-policy _mvn_profiles_caching_policy + + profs=() + + index=$words[(i)-s] + [[ $index -gt ${#words} ]] && index=$words[(i)--settings] + [[ $index -gt ${#words} ]] && settings_file=~/.m2/settings.xml || settings_file=$words[$index+1] + + if [[ -f $settings_file ]]; then + unset profiles + cache_name="mvn/profiles${settings_file:A}" + if ( [[ ${+profiles} -eq 0 ]] || _cache_invalid "$cache_name" ) && ! _retrieve_cache "$cache_name"; then + profiles=() + profiles_section="${(M)${(f)$(<$settings_file)}:#**}" + if [[ -n "$profiles_section" ]]; then + for profile in ${(s:,,,:)${${${(S)${(S)${(S)${(S)${${profiles_section#*}%*}//*<\/repositories>}//*<\/pluginRepositories>}//*<\/build>}//<\/id>*/,,,}##*}%%*}}; do + [[ -z ${(M)profiles:#"$profile"*} ]] && profiles+=("$profile"'['"in settings file"']') + done + fi + [[ ${#profiles} -gt 0 ]] && _store_cache "$cache_name" profiles + fi + profs+=($profiles) + fi + + index=$words[(i)-f] + [[ $index -gt ${#words} ]] && index=$words[(i)--file] + [[ $index -gt ${#words} ]] && parent_pom_file=pom.xml || parent_pom_file=$words[$index+1] + while [[ -f ${parent_pom_file:a:h:h}/pom.xml ]]; do parent_pom_file=${parent_pom_file:a:h:h}/pom.xml; done + + if [[ -f $parent_pom_file ]]; then + unset profiles + cache_name="mvn/profiles${parent_pom_file:A}" + if ( [[ ${+profiles} -eq 0 ]] || _cache_invalid "$cache_name" ) && ! _retrieve_cache "$cache_name"; then + profiles=() + setopt localoptions extendedglob + for file in ${parent_pom_file:h}/**/pom.xml~*target\/*; do + profiles_section="${(M)${(f)$(<$file)}:#**}" + if [[ -n "$profiles_section" ]]; then + for profile in ${(s:,,,:)${${${(S)${(S)${(S)${(S)${${profiles_section#*}%*}//*<\/repositories>}//*<\/pluginRepositories>}//*<\/build>}//<\/id>*/,,,}##*}%%*}}; do + [[ -z ${(M)profiles:#"$profile"*} ]] && profiles+=("$profile"'['"in ${file#${parent_pom_file:h}\/}"']') + done + fi + done + [[ ${#profiles} -gt 0 ]] && _store_cache "$cache_name" profiles + fi + profs+=($profiles) + fi + + [[ ${#profs} -gt 0 ]] && _values -s ',' 'profile' "$profs[@]" && ret=0 + + return ret +} + +_mvn_projects() +{ + # TODO projects can also be given in the form [groupId:]artifactId. + local ret=1 projects + + index=$words[(i)-f] + [[ $index -gt ${#words} ]] && index=$words[(i)--file] + [[ $index -gt ${#words} ]] && pom_file=pom.xml || pom_file=$words[$index+1] + while [[ -f ${pom_file:a:h:h}/pom.xml ]]; do pom_file=${pom_file:a:h:h}/pom.xml; done + + if [[ -f $pom_file ]]; then + setopt localoptions extendedglob + projects=(${pom_file:h}/*/**/pom.xml~*target\/*) + projects=(${projects#.\/}) + [[ ${#projects} -gt 0 ]] && _values "$@" 'project' "${projects[@]:h}" && ret=0 + fi + + return ret +} + +_mvn_properties() +{ + # TODO Complete some very common props like -DskipTests, etc. + local ret=1 + _message -e property-names 'property name' && ret=0 + return ret +} + +_mvn "$@"