3 # Creates and upload a git module tarball
6 # This script is intended to run on any platform supported by X.Org.
7 # Basically, it should be able to run in a Bourne shell.
13 #------------------------------------------------------------------------------
14 # Function: check_local_changes
15 #------------------------------------------------------------------------------
17 check_local_changes
() {
18 git
diff --quiet HEAD
> /dev
/null
2>&1
21 echo "Uncommitted changes found. Did you forget to commit? Aborting."
23 echo "You can perform a 'git stash' to save your local changes and"
24 echo "a 'git stash apply' to recover them after the tarball release."
25 echo "Make sure to rebuild and run 'make distcheck' again."
27 echo "Alternatively, you can clone the module in another directory"
28 echo "and run ./configure. No need to build if testing was finished."
35 #------------------------------------------------------------------------------
36 # Function: check_option_args
37 #------------------------------------------------------------------------------
39 # perform sanity checks on cmdline args which require arguments
41 # $1 - the option being examined
42 # $2 - the argument to the option
44 # if it returns, everything is good
50 # check for an argument
51 if [ x
"$arg" = x
]; then
53 echo "Error: the '$option' option is missing its required argument."
59 # does the argument look like an option?
60 echo $arg |
$GREP "^-" > /dev
/null
63 echo "Error: the argument '$arg' of option '$option' looks like an option itself."
70 #------------------------------------------------------------------------------
71 # Function: check_gpgkey
72 #------------------------------------------------------------------------------
74 # check if the gpg key provided is known/available
78 # if it returns, everything is good
83 $GPG --list-keys "$arg" &>/dev
/null
86 echo "Error: the argument '$arg' is not a known gpg key."
93 #------------------------------------------------------------------------------
94 # Function: check_modules_specification
95 #------------------------------------------------------------------------------
97 check_modules_specification
() {
99 if [ x
"$MODFILE" = x
]; then
100 if [ x
"${INPUT_MODULES}" = x
]; then
102 echo "Error: no modules specified (blank command line)."
110 #------------------------------------------------------------------------------
111 # Function: generate_announce
112 #------------------------------------------------------------------------------
117 Subject: [ANNOUNCE] $pkg_name $pkg_version
121 `git log --no-merges "$tag_range" | git shortlog`
127 for tarball
in $tarbz2 $targz $tarxz; do
129 https://$host_current/$section_path/$tarball
130 MD5: `$MD5SUM $tarball`
131 SHA1: `$SHA1SUM $tarball`
132 SHA256: `$SHA256SUM $tarball`
133 PGP: https://${host_current}/${section_path}/${tarball}.sig
139 #------------------------------------------------------------------------------
140 # Function: read_modfile
141 #------------------------------------------------------------------------------
143 # Read the module names from the file and set a variable to hold them
144 # This will be the same interface as cmd line supplied modules
148 if [ x
"$MODFILE" != x
]; then
149 # Make sure the file is sane
150 if [ ! -r "$MODFILE" ]; then
151 echo "Error: module file '$MODFILE' is not readable or does not exist."
154 # read from input file, skipping blank and comment lines
157 if [ x
"$line" = x
]; then
161 if echo "$line" |
$GREP -q "^#" ; then
164 INPUT_MODULES
="$INPUT_MODULES $line"
170 #------------------------------------------------------------------------------
171 # Function: print_epilog
172 #------------------------------------------------------------------------------
176 epilog
="======== Successful Completion"
177 if [ x
"$NO_QUIT" != x
]; then
178 if [ x
"$failed_modules" != x
]; then
179 epilog
="======== Partial Completion"
181 elif [ x
"$failed_modules" != x
]; then
182 epilog
="======== Stopped on Error"
186 echo "$epilog `date`"
188 # Report about modules that failed for one reason or another
189 if [ x
"$failed_modules" != x
]; then
190 echo " List of failed modules:"
191 for mod
in $failed_modules; do
199 #------------------------------------------------------------------------------
200 # Function: process_modules
201 #------------------------------------------------------------------------------
203 # Loop through each module to release
204 # Exit on error if --no-quit was not specified
207 for MODULE_RPATH
in ${INPUT_MODULES}; do
208 if ! process_module
; then
209 echo "Error: processing module \"$MODULE_RPATH\" failed."
210 failed_modules
="$failed_modules $MODULE_RPATH"
211 if [ x
"$NO_QUIT" = x
]; then
219 #------------------------------------------------------------------------------
220 # Function: get_section
221 #------------------------------------------------------------------------------
222 # Code 'return 0' on success
223 # Code 'return 1' on error
224 # Sets global variable $section
227 local full_module_url
229 # Obtain the git url in order to find the section to which this module belongs
230 full_module_url
=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
231 if [ $?
-ne 0 ]; then
232 echo "Error: unable to obtain git url for remote \"$remote_name\"."
236 # The last part of the git url will tell us the section. Look for xorg first
237 module_url
=`echo "$full_module_url" | $GREP -o "/xorg/.*"`
238 if [ $?
-eq 0 ]; then
239 module_url
=`echo $module_url | cut -d'/' -f3,4`
241 # The look for mesa, xcb, etc...
242 module_url
=`echo "$full_module_url" | $GREP -o -e "/mesa/.*" -e "/xcb/.*" -e "/xkeyboard-config" -e "/nouveau/xf86-video-nouveau" -e "/libevdev" -e "/wayland/.*" -e "/evemu"`
243 if [ $?
-eq 0 ]; then
244 module_url
=`echo $module_url | cut -d'/' -f2,3`
246 echo "Error: unable to locate a valid project url from \"$full_module_url\"."
247 echo "Cannot establish url as one of xorg, mesa, xcb, xf86-video-nouveau, xkeyboard-config or wayland"
253 # Find the section (subdirs) where the tarballs are to be uploaded
254 # The module relative path can be app/xfs, xserver, or mesa/drm for example
255 section
=`echo $module_url | cut -d'/' -f1`
256 if [ $?
-ne 0 ]; then
257 echo "Error: unable to extract section from $module_url first field."
261 if [ x
"$section" = xmesa
]; then
262 section
=`echo $module_url | cut -d'/' -f2`
263 if [ $?
-ne 0 ]; then
264 echo "Error: unable to extract section from $module_url second field."
266 elif [ x
"$section" != xdrm
] && [ x
"$section" != xmesa
]; then
267 echo "Error: section $section is not supported, only libdrm and mesa are."
272 if [ x
"$section" = xwayland
]; then
273 section
=`echo $module_url | cut -d'/' -f2`
274 if [ $?
-ne 0 ]; then
275 echo "Error: unable to extract section from $module_url second field."
283 # Function: sign_or_fail
284 #------------------------------------------------------------------------------
286 # Sign the given file, if any
287 # Output the name of the signature generated to stdout (all other output to
289 # Return 0 on success, 1 on fail
295 $GPG -b $GPGKEY $1 1>&2
296 if [ $?
-ne 0 ]; then
297 echo "Error: failed to sign $1." >&2
305 #------------------------------------------------------------------------------
306 # Function: process_module
307 #------------------------------------------------------------------------------
308 # Code 'return 0' on success to process the next module
309 # Code 'return 1' on error to process next module if invoked with --no-quit
315 echo "======== Processing \"$top_src/$MODULE_RPATH\""
317 # This is the location where the script has been invoked
318 if [ ! -d $MODULE_RPATH ] ; then
319 echo "Error: $MODULE_RPATH cannot be found under $top_src."
323 # Change directory to be in the git module
325 if [ $?
-ne 0 ]; then
326 echo "Error: failed to cd to $MODULE_RPATH."
330 # ----- Now in the git module *root* directory ----- #
332 # Check that this is indeed a git module
333 # Don't assume that $(top_srcdir)/.git is a directory. It may be
334 # a gitlink file if $(top_srcdir) is a submodule checkout or a linked
336 if [ ! -e .git
]; then
337 echo "Error: there is no git module here: `pwd`"
341 # Determine what is the current branch and the remote name
342 current_branch
=`git branch | $GREP "\*" | sed -e "s/\* //"`
343 remote_name
=`git config --get branch.$current_branch.remote`
344 remote_branch
=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
345 echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
349 if [ $?
-ne 0 ]; then
354 # Check for uncommitted/queued changes.
356 if [ $?
-ne 0 ]; then
360 if [ -e configure
]; then
361 echo "Error: the git repository contains configure"
362 echo "Did you forget to run git clean -fXd (and git clean -fxd) ?"
366 # Create tmpdir for the build
367 build_dir
=`mktemp -d -p . build.XXXXXXXXXX`
368 if [ $?
-ne 0 ]; then
369 echo "Error: could not create a temporary directory for the build."
370 echo "Do you have coreutils' mktemp ?"
374 echo "Info: generating configure."
375 autoreconf
--force --install >/dev
/null
376 if [ $?
-ne 0 ]; then
377 echo "Error: failed to generate configure."
382 if [ $?
-ne 0 ]; then
383 echo "Error: failed to cd to $MODULE_RPATH/$build_dir."
388 # Using ../ here feels a bit nasty, yet $top_src is an absolute path. Thus
389 # it will get propagated in the generated sources, which we do not want.
390 ..
/configure
>/dev
/null
391 if [ $?
-ne 0 ]; then
392 echo "Error: failed to configure module."
397 # Run 'make dist/distcheck' to ensure the tarball matches the git module content
398 # Important to run make dist/distcheck before looking in Makefile, may need to reconfigure
399 echo "Info: running \"make $MAKE_DIST_CMD\" to create tarballs:"
400 ${MAKE} $MAKEFLAGS $MAKE_DIST_CMD > /dev
/null
401 if [ $?
-ne 0 ]; then
402 echo "Error: \"$MAKE $MAKEFLAGS $MAKE_DIST_CMD\" failed."
407 # Find out the tarname from the makefile
408 pkg_name
=`$GREP '^PACKAGE = ' Makefile | sed 's|PACKAGE = ||'`
409 pkg_version
=`$GREP '^VERSION = ' Makefile | sed 's|VERSION = ||'`
410 tar_name
="$pkg_name-$pkg_version"
411 targz
=$tar_name.
tar.gz
412 tarbz2
=$tar_name.
tar.bz2
413 tarxz
=$tar_name.
tar.xz
415 [ -e $targz ] && ls -l $targz ||
unset targz
416 [ -e $tarbz2 ] && ls -l $tarbz2 ||
unset tarbz2
417 [ -e $tarxz ] && ls -l $tarxz ||
unset tarxz
419 if [ -z "$targz" -a -z "$tarbz2" -a -z "$tarxz" ]; then
420 echo "Error: no compatible tarballs found."
425 # wayland/weston/libinput tag with the version number only
427 if [ x
"$section" = xwayland
] ||
428 [ x
"$section" = xweston
] ||
429 [ x
"$section" = xlibinput
]; then
430 tag_name
="$pkg_version"
433 # evemu tag with the version number prefixed by 'v'
434 if [ x
"$section" = xevemu
]; then
435 tag_name
="v$pkg_version"
439 siggz
="$(sign_or_fail ${targz})"
440 gpgsignerr
=$
((${gpgsignerr} + $?
))
441 sigbz2
="$(sign_or_fail ${tarbz2})"
442 gpgsignerr
=$
((${gpgsignerr} + $?
))
443 sigxz
="$(sign_or_fail ${tarxz})"
444 gpgsignerr
=$
((${gpgsignerr} + $?
))
445 if [ ${gpgsignerr} -ne 0 ]; then
446 echo "Error: unable to sign at least one of the tarballs."
451 # Obtain the top commit SHA which should be the version bump
452 # It should not have been tagged yet (the script will do it later)
453 local_top_commit_sha
=`git rev-list --max-count=1 HEAD`
454 if [ $?
-ne 0 ]; then
455 echo "Error: unable to obtain the local top commit id."
460 # Check that the top commit looks like a version bump
461 git
diff --unified=0 HEAD^ |
$GREP -F $pkg_version >/dev
/null
2>&1
462 if [ $?
-ne 0 ]; then
463 # Wayland repos use m4_define([wayland_major_version], [0])
464 git
diff --unified=0 HEAD^ |
$GREP -E "(major|minor|micro)_version" >/dev
/null
2>&1
465 if [ $?
-ne 0 ]; then
466 echo "Error: the local top commit does not look like a version bump."
467 echo " the diff does not contain the string \"$pkg_version\"."
468 local_top_commit_descr
=`git log --oneline --max-count=1 $local_top_commit_sha`
469 echo " the local top commit is: \"$local_top_commit_descr\""
475 # Check that the top commit has been pushed to remote
476 remote_top_commit_sha
=`git rev-list --max-count=1 $remote_name/$remote_branch`
477 if [ $?
-ne 0 ]; then
478 echo "Error: unable to obtain top commit from the remote repository."
482 if [ x
"$remote_top_commit_sha" != x
"$local_top_commit_sha" ]; then
483 echo "Error: the local top commit has not been pushed to the remote."
484 local_top_commit_descr
=`git log --oneline --max-count=1 $local_top_commit_sha`
485 echo " the local top commit is: \"$local_top_commit_descr\""
490 # If a tag exists with the tar name, ensure it is tagging the top commit
491 # It may happen if the version set in configure.ac has been previously released
492 tagged_commit_sha
=`git rev-list --max-count=1 $tag_name 2>/dev/null`
493 if [ $?
-eq 0 ]; then
494 # Check if the tag is pointing to the top commit
495 if [ x
"$tagged_commit_sha" != x
"$remote_top_commit_sha" ]; then
496 echo "Error: the \"$tag_name\" already exists."
497 echo " this tag is not tagging the top commit."
498 remote_top_commit_descr
=`git log --oneline --max-count=1 $remote_top_commit_sha`
499 echo " the top commit is: \"$remote_top_commit_descr\""
500 local_tag_commit_descr
=`git log --oneline --max-count=1 $tagged_commit_sha`
501 echo " tag \"$tag_name\" is tagging some other commit: \"$local_tag_commit_descr\""
505 echo "Info: module already tagged with \"$tag_name\"."
508 # Tag the top commit with the tar name
509 if [ x
"$DRY_RUN" = x
]; then
510 git tag
$GPGKEY -s -m $tag_name $tag_name
511 if [ $?
-ne 0 ]; then
512 echo "Error: unable to tag module with \"$tag_name\"."
516 echo "Info: module tagged with \"$tag_name\"."
519 echo "Info: skipping the commit tagging in dry-run mode."
523 # --------- Now the tarballs are ready to upload ----------
525 # The hostname which is used to connect to the development resources
526 hostname
="annarchy.freedesktop.org"
528 # Some hostnames are also used as /srv subdirs
529 host_fdo
="www.freedesktop.org"
530 host_xorg
="xorg.freedesktop.org"
531 host_dri
="dri.freedesktop.org"
532 host_mesa
="mesa.freedesktop.org"
533 host_wayland
="wayland.freedesktop.org"
535 # Mailing lists where to post the all [Announce] e-mails
536 list_to
="xorg-announce@lists.x.org"
538 # Mailing lists to be CC according to the project (xorg|dri|xkb)
539 list_xorg_user
="xorg@lists.x.org"
540 list_dri_devel
="dri-devel@lists.freedesktop.org"
541 list_mesa_announce
="mesa-announce@lists.freedesktop.org"
542 list_mesa_devel
="mesa-dev@lists.freedesktop.org"
544 list_xkb
="xkb@listserv.bat.ru"
545 list_xcb
="xcb@lists.freedesktop.org"
546 list_nouveau
="nouveau@lists.freedesktop.org"
547 list_wayland
="wayland-devel@lists.freedesktop.org"
548 list_input
="input-tools@lists.freedesktop.org"
550 host_current
=$host_xorg
551 section_path
=archive
/individual
/$section
552 srv_path
="/srv/$host_current/$section_path"
553 list_cc
=$list_xorg_user
555 # Handle special cases such as non xorg projects or migrated xorg projects
556 # Nouveau has its own list and section, but goes with the other drivers
557 if [ x
"$section" = xnouveau
]; then
558 section_path
=archive
/individual
/driver
559 srv_path
="/srv/$host_current/$section_path"
560 list_cc
=$list_nouveau
563 # Xcb has a separate mailing list
564 if [ x
"$section" = xxcb
]; then
568 # Module mesa/drm goes in the dri "libdrm" section
569 if [ x
"$section" = xdrm
]; then
570 host_current
=$host_dri
572 srv_path
="/srv/$host_current/www/$section_path"
573 list_cc
=$list_dri_devel
574 elif [ x
"$section" = xmesa
]; then
575 host_current
=$host_mesa
576 mesa_version
=`echo $pkg_version | sed 's:-rc.*::'`
577 section_path
=archive
/$mesa_version
578 srv_path
="/srv/$host_current/www/$section_path"
579 list_to
=$list_mesa_announce
580 list_cc
=$list_mesa_devel
582 # Mesa uses separate folder for each release
583 echo "Info: creating mesa directory on web server:"
584 ssh $USER_NAME$hostname mkdir
-p $srv_path &>/dev
/null
585 if [ $?
-ne 0 ]; then
586 echo "Error: cannot create the path \"$srv_path\" on the web server."
592 # Module xkeyboard-config goes in a subdir of the xorg "data" section
593 if [ x
"$section" = xxkeyboard-config
]; then
594 host_current
=$host_xorg
595 section_path
=archive
/individual
/data
/$section
596 srv_path
="/srv/$host_current/$section_path"
600 if [ x
"$section" = xlibevdev
]; then
601 host_current
=$host_fdo
602 section_path
="software/$section"
603 srv_path
="/srv/$host_current/www/$section_path"
608 if [ x
"$section" = xwayland
] ||
609 [ x
"$section" = xweston
]; then
610 host_current
=$host_wayland
611 section_path
="releases"
612 srv_path
="/srv/$host_current/www/$section_path"
613 list_to
=$list_wayland
615 elif [ x
"$section" = xlibinput
]; then
616 host_current
=$host_fdo
617 section_path
="software/libinput"
618 srv_path
="/srv/$host_current/www/$section_path"
619 list_to
=$list_wayland
621 elif [ x
"$section" = xevemu
]; then
622 host_current
=$host_fdo
623 section_path
="software/evemu"
624 srv_path
="/srv/$host_current/www/$section_path"
629 # Use personal web space on the host for unit testing (leave commented out)
630 # srv_path="~/public_html$srv_path"
632 # Check that the server path actually does exist
633 echo "Info: checking if path exists on web server:"
634 ssh $USER_NAME$hostname ls $srv_path >/dev
/null
2>&1
635 if [ $?
-ne 0 ]; then
636 echo "Error: the path \"$srv_path\" on the web server does not exist."
641 # Check for already existing tarballs
642 for tarball
in $targz $tarbz2 $tarxz; do
643 echo "Info: checking if tarball $tarball already exists on web server:"
644 ssh $USER_NAME$hostname ls $srv_path/$tarball >/dev
/null
2>&1
645 if [ $?
-eq 0 ]; then
646 if [ "x$FORCE" = "xyes" ]; then
647 echo "Warning: overwriting released tarballs due to --force option."
649 echo "Error: tarball $tar_name already exists. Use --force to overwrite."
656 # Upload to host using the 'scp' remote file copy program
657 if [ x
"$DRY_RUN" = x
]; then
658 echo "Info: uploading tarballs to web server:"
659 scp
$targz $tarbz2 $tarxz $siggz $sigbz2 $sigxz $USER_NAME$hostname:$srv_path
660 if [ $?
-ne 0 ]; then
661 echo "Error: the tarballs uploading failed."
666 echo "Info: skipping tarballs uploading in dry-run mode."
667 echo " \"$srv_path\"."
670 # Pushing the top commit tag to the remote repository
671 if [ x
$DRY_RUN = x
]; then
672 echo "Info: pushing tag \"$tag_name\" to remote \"$remote_name\":"
673 git push
$remote_name $tag_name
674 if [ $?
-ne 0 ]; then
675 echo "Error: unable to push tag \"$tag_name\" to the remote repository."
676 echo " it is recommended you fix this manually and not run the script again"
681 echo "Info: skipped pushing tag \"$tag_name\" to the remote repository in dry-run mode."
684 MD5SUM
=`which md5sum || which gmd5sum`
685 SHA1SUM
=`which sha1sum || which gsha1sum`
686 SHA256SUM
=`which sha256sum || which gsha256sum`
688 # --------- Generate the announce e-mail ------------------
689 # Failing to generate the announce is not considered a fatal error
691 # Git-describe returns only "the most recent tag", it may not be the expected one
692 # However, we only use it for the commit history which will be the same anyway.
693 tag_previous
=`git describe --abbrev=0 HEAD^ 2>/dev/null`
694 # Git fails with rc=128 if no tags can be found prior to HEAD^
695 if [ $?
-ne 0 ]; then
696 if [ $?
-ne 0 ]; then
697 echo "Warning: unable to find a previous tag."
698 echo " perhaps a first release on this branch."
699 echo " Please check the commit history in the announce."
702 if [ x
"$tag_previous" != x
]; then
703 # The top commit may not have been tagged in dry-run mode. Use commit.
704 tag_range
=$tag_previous..
$local_top_commit_sha
708 generate_announce
> "$tar_name.announce"
709 echo "Info: [ANNOUNCE] template generated in \"$build_dir/$tar_name.announce\" file."
710 echo " Please pgp sign and send it."
712 # --------- Update the JH Build moduleset -----------------
713 # Failing to update the jh moduleset is not considered a fatal error
714 if [ x
"$JH_MODULESET" != x
]; then
715 for tarball
in $targz $tarbz2 $tarxz; do
716 if [ x
$DRY_RUN = x
]; then
717 sha1sum=`$SHA1SUM $tarball | cut -d' ' -f1`
718 $top_src/util
/modular
/update-moduleset.sh
$JH_MODULESET $sha1sum $tarball
719 echo "Info: updated jh moduleset: \"$JH_MODULESET\""
721 echo "Info: skipping jh moduleset \"$JH_MODULESET\" update in dry-run mode."
724 # $tar* may be unset, so simply loop through all of them and the
725 # first one that is set updates the module file
731 # --------- Successful completion --------------------------
737 #------------------------------------------------------------------------------
739 #------------------------------------------------------------------------------
740 # Displays the script usage and exits successfully
743 basename="`expr "//$0" : '.*/\([^/]*\)'`"
746 Usage: $basename [options] path...
748 Where "path" is a relative path to a git module, including '.'.
751 --dist make 'dist' instead of 'distcheck'; use with caution
752 --distcheck Default, ignored for compatibility
753 --dry-run Does everything except tagging and uploading tarballs
754 --force Force overwriting an existing release
755 --gpgkey <key> Specify the key used to sign the git tag/release tarballs
756 --help Display this help and exit successfully
757 --modfile <file> Release the git modules specified in <file>
758 --moduleset <file> The jhbuild moduleset full pathname to be updated
759 --no-quit Do not quit after error; just print error message
760 --user <name>@ Username of your fdo account if not configured in ssh
762 Environment variables defined by the "make" program and used by release.sh:
763 MAKE The name of the make command [make]
764 MAKEFLAGS: Options to pass to all \$(MAKE) invocations
769 #------------------------------------------------------------------------------
771 #------------------------------------------------------------------------------
774 # Choose which make program to use (could be gmake)
777 # Choose which grep program to use (on Solaris, must be gnu grep)
778 if [ "x$GREP" = "x" ] ; then
779 if [ -x /usr
/gnu
/bin
/grep ] ; then
780 GREP
=/usr
/gnu
/bin
/grep
786 # Find path for GnuPG v2
787 if [ "x$GPG" = "x" ] ; then
788 if [ -x /usr
/bin
/gpg2
] ; then
795 # Set the default make tarball creation command
796 MAKE_DIST_CMD
=distcheck
798 # Process command line args
802 # Use 'dist' rather than 'distcheck' to create tarballs
803 # You really only want to do this if you're releasing a module you can't
804 # possibly build-test. Please consider carefully the wisdom of doing so.
808 # Use 'distcheck' to create tarballs
810 MAKE_DIST_CMD
=distcheck
812 # Does everything except uploading tarball
816 # Force overwriting an existing release
817 # Use only if nothing changed in the git repo
821 # Allow user specified GPG key
823 check_option_args
$1 $2
828 # Display this help and exit successfully
833 # Release the git modules specified in <file>
835 check_option_args
$1 $2
839 # The jhbuild moduleset to update with relase info
841 check_option_args
$1 $2
845 # Do not quit after error; just print error message
849 # Username of your fdo account if not configured in ssh
851 check_option_args
$1 $2
857 echo "Error: unknown option: $1"
864 echo "Error: unknown option: $1"
870 if [ x
"${MODFILE}" != x
]; then
872 echo "Error: specifying both modules and --modfile is not permitted"
877 INPUT_MODULES
="${INPUT_MODULES} $1"
884 # If no modules specified (blank cmd line) display help
885 check_modules_specification
887 # Read the module file and normalize input in INPUT_MODULES
890 # Loop through each module to release
891 # Exit on error if --no-quit no specified
894 # Print the epilog with final status