Resolving Grails version automatically on Windows
A while ago we showed how it was possible to select automatically version of Grails to run just using a simple bash script. Trouble is, not all users use systems that recognize bash-scripts natively. One of such systems is, apparently, Windows platform. So today I’m going to show how it is possible to accomplish similar tasks on Windows using just the built-in functionality — the batch job processing files.
The bash script we showed earlier, used the knowledge embedded in the Grails-project file called application.properties to find out the version of Grails for the current project. Weirdly enough, batch files allow to do this as well (for the syntax used in properties file at least). Here is the snippet of code that does just that:
FOR /F "eol=# tokens=1,2 delims==" %%i IN (application.properties) DO (
IF "%%i" == "app.grails.version" (
SET GRAILS_VERSION=%%j
)
)
SET GRAILS_HOME=%GRAILS_PREFIX%%GRAILS_VERSION%
SET PATH=%GRAILS_HOME%\bin
CALL %GRAILS_HOME%\bin\grails.bat %*
Batch files have been around since DOS was released for the first time. But only with the latest versions of Windows can their full potential be unleashed, so that writing small programs (much like programs in ordinary programming language) became possible. Those programs are still somewhat limited, and writing some of them requires employing various rather awkward or not at all convenient quirks, so probably you still better off writing scripts on Groovy for your Grails-projects, but to get to the point of running Groovy-scripts, you have to choose first which version of Grails to use.
Main intent of the FOR
command was to list the files in a directory. Or directories in a directory. Or directories in a directory and all subdirectories of those directories (recursive listing). This would allow to run a number of tasks iteratively on the list of files/directories. Later the FOR
command evolved to allow processing of texts (specified either as strings or names of text files) as well. The latter is exactly what fits our needs. The command iterates over the lines in the file application.properties, dividing each string into two tokens. As the delimiter for the tokens the equals sign (=) is used. Lines that start with the pound sign (#) are treated as comments and ignored.
Inside the FOR
's body, the tokens are referenced as %%i
and %%j
— the first and second tokens respectively. Since all is needed is a property that specifies Grails version, namely app.grails.version
, the first token is compared with the name of the sought property, and if it matches, environment variable GRAILS_VERSION
is set to the value of the second token, which in this case holds the version number.
Then the variable GRAILS_HOME
is set to point to the proper location where Grails is installed. Just bear in mind that you have to set environment variable GRAILS_PREFIX
in such a way that a mere addition of version number (like 1.2) would produce correct path. In my case, this variable holds the value D:\Tools\Grails\
, so that addition of proper version number, say 1.2
, will produce the path D:\Tools\Grails\1.2
. Note that you don’t have to specify variable GRAILS_HOME
before running this script — it will set it automatically. Although you definitely may specify it — the script will modify it only for the duration of its run time (in other words, it uses local copy of all environment variables, the global ones are untouched).
Another environment variable that needs to be set inside the script is PATH
. Although later the full path to grails.bat is used, some versions of Grails don’t like it for some reason when the bin\ directory of Grails is not found on path. And since only Grails is going to be executed, it’s pretty normal to reset the PATH
variable altogether, so it includes only the path to Grails’s bin\ directory.
So far so good. Correct version of Grails is executed every time the above script is run in the directory that contains a Grails-project. Only that’s not the full story... You see, there are a variety of commands that can or have to be run outside the context of any Grails-related project. Here they are:
create-app
create-plugin
help
list-plugins
package-plugin
plugin-info
set-proxy
So how to choose a version of Grails when there is no application.properties around? The solution is simple: preset the GRAILS_VERSION
environment variable with the value of Grails version to be used by default. Then in the batch file test whether the file application.properties is present in the current directory. If it is, use information about version from the file as it was shown above (in this case the value held in GRAILS_VERSION
will be temporarily overwritten with a value read from the properties file). If there is no properties file, just use the variable GRAILS_VERSION
as it is.
IF EXIST .\application.properties (
REM Read version number from application.properties file using the FOR command
)
REM Set environment variables GRAILS_HOME and PATH, and run Grails
This works pretty well for both in-context and out-of-context runs. But hang on, because there is another type of Grails execution that needs addressing. Sometimes, it happens, you need to run Grails inside project’s directory, but with different version. More specifically — with the higher version of Grails than the project normally use. See what I mean? An upgrade
command.
Now, should I develop an auto-version script for Grails in something more substantial than batch files, I would certainly implement it in such a way that it would detect if the script is executed with the upgrade command and would automatically use the default version of Grails (defined in GRAILS_VERSION
environment variable). But alas, not only I’ve chosen not very substantial and graceful scripting language, there is another possible minor problem with such an approach. I wanted to be able to specify version directly in the command line, so that it would be possible to upgrade a project to some other version than default, or to use different version of Grails with any other command besides upgrade
.
So, I opted for defining my own command line argument -ver:
that specifies version number right after the colon. Here is the code that does this with the explanation following.
:setupArgs
IF "q%1" == "q" (
GOTO :doneStart
)
SET VER_PARAM=%1
IF "%VER_PARAM:~0,5%" == "-ver:" (
SET VERSION_OVERRIDE=%VER_PARAM:~5%
SHIFT
GOTO :setupArgs
)
SET CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
SHIFT
GOTO :setupArgs
:doneStart
IF NOT "%VERSION_OVERRIDE%q" == "q" (
SET GRAILS_VERSION=%VERSION_OVERRIDE%
)
The code above first checks if there are any command line arguments left. If all arguments have been processed (or there weren’t any from the start), it proceeds with normal execution; otherwise the code processes each argument one by one. The construct %VER_PARAM:~0,5%
extracts first five characters from each argument into a substring and compares that substring to the sought sequence — "-ver:"
. If it matches, variable VERSION_OVERRIDE
is set to the rest of the argument (i.e. whatever comes after -ver:
), then this whole argument is skipped. Otherwise, this command line argument is added to the end of the CMD_LINE_ARGS
environment variable. One way or the other, before next iteration all arguments are shifted so that the argument that was on the 2nd place is now on the 1st place, 3rd argument is shifted onto 2nd and so on, and the process repeats.
After all arguments have been processed, the VERSION_OVERRIDE
variable will contain version specified in the command line. So, the code above checks if this variable is defined and is not empty, and if it contains something, assigns that to GRAILS_VERSION
environment variable.
The reason for constructing another copy of command line in CMD_LINE_ARGS
variable is because version-override argument needs to be excluded — if it is passed to Grails, an error will happen since Grails does not recognize it. Because of that it is impossible to call Grails with %*
as its command line anymore. But luckily, it is possible to pass the contents of the variable we’ve just constructed. Like so:
CALL %GRAILS_HOME%\bin\grails.bat %CMD_LINE_ARGS%
The whole script file, with various additional checks, can be found at GitHub: http://gist.github.com/313862
Shameless plug
We develop mobile and web apps, you can hire us to work on your next project.