<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Yannick&#039;s Java Tech Blog &#187; bash</title>
	<atom:link href="http://welsch.lu/blog/category/bash/feed/" rel="self" type="application/rss+xml" />
	<link>http://welsch.lu/blog</link>
	<description></description>
	<lastBuildDate>Sat, 31 Jan 2015 13:22:02 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.2.18</generator>
	<item>
		<title>Gradle Bash integration for multi-project builds</title>
		<link>http://welsch.lu/blog/2015/01/gradle-bash-integration-for-multi-project-builds/</link>
		<comments>http://welsch.lu/blog/2015/01/gradle-bash-integration-for-multi-project-builds/#comments</comments>
		<pubDate>Sat, 31 Jan 2015 13:19:11 +0000</pubDate>
		<dc:creator><![CDATA[ywelsch]]></dc:creator>
				<category><![CDATA[bash]]></category>
		<category><![CDATA[gradle]]></category>

		<guid isPermaLink="false">http://welsch.lu/blog/?p=5</guid>
		<description><![CDATA[Working on Gradle-based enterprise builds requires familiarity with the usually quite large number of different project-specific tasks. Some sub-projects provide custom tasks to run maintenance scripts, others tasks to check additional properties of the build or to start and stop different components needed for the development life-cycle. Bash auto-completion helps with the burden of remembering &#8230; ]]></description>
				<content:encoded><![CDATA[<p>Working on Gradle-based enterprise builds requires familiarity with the usually quite large number of different project-specific tasks. Some sub-projects provide custom tasks to run maintenance scripts, others tasks to check additional properties of the build or to start and stop different components needed for the development life-cycle.</p>
<p>Bash auto-completion helps with the burden of remembering all the different tasks for the various sub-projects in a build. It also provides new ways to discover the features of a build in an exploratory way. In the following, I give a description on how to implement bash auto-completion, based on the following requirements:</p>
<ul>
<li>Fast auto-completion in the sub-second range. To compute the auto-completable suggestions, the Gradle project needs to be fully configured, which takes at least a few seconds for medium-sized builds. This means that the list of auto-completable suggestions needs to be precomputed.</li>
<li>The auto-completable suggestions to show should be customizable. Some tasks, even if available on the project, should perhaps not be shown. This might be tasks that are not relevant for the developer but used in later integration stages. Other parameters to the build might be interesting to show in addition to the restricted set of tasks. This might include other parameters that can be passed to the Gradle build, such as task parameters or options to set the Gradle output logging level.</li>
<li>No change to the Gradle wrapper scripts are needed.</li>
</ul>
<p>The solution presented is two-fold. First, we introduce the shorthand <code>gr</code> to run the Gradle wrapper from any directory of the multi-project build. Auto-completion is then implemented in the second step. Note that, although Bash is a Unix shell, the following also works for Bash ports on Windows such as MSYS (part for example of <a href="https://msysgit.github.io/">msysGit</a>).</p>
<h1>Gradle shorthand <code>gr</code></h1>
<p>There are different design choices to implement the shorthand <code>gr</code>, influenced by the features it should have. The implementation given here works as follows. It searches from the current directory upward until it finds a Gradle wrapper script which it then invokes.</p>
<p>We first implement the function <code>upsearch</code> that can search upward in a directory tree. All of the following goes into the Bash startup file <code>~/.bashrc</code>:</p>
<pre class="brush: bash; title: ; notranslate">
upsearch () { # adapted from https://gist.github.com/lsiden/1577473
  local the_test=$1
  local curdir=`pwd`

  while [[ &quot;`pwd`&quot; != '/' ]]; do
    if eval &quot;[[ $the_test ]]&quot;; then
      pwd
      cd &quot;$curdir&quot;
      return 0
    fi
    cd ..
  done
  cd &quot;$curdir&quot;
  return 1
}
</pre>
<p>We then implement a function <code>_findgradledir</code> that searches for the Gradle wrapper file named <code>gradlew</code>:</p>
<pre class="brush: bash; title: ; notranslate">
_findgradledir () {
  upsearch '-f gradlew'
}
</pre>
<p>The function <code>_gradlew</code>, implemented in the following, uses <code>_findgradledir</code> to find the wrapper file, which it then invokes.</p>
<pre class="brush: bash; title: ; notranslate">
_gradlew () {
  local gradle_project_dir=`_findgradledir`
  if [ -z &quot;$gradle_project_dir&quot; ]; then
    echo &quot;Gradle project directory not found&quot; 1&gt;&amp;2
    return 1
  fi
  &quot;$gradle_project_dir/gradlew&quot; -p &quot;$gradle_project_dir&quot; &quot;$@&quot;
}
</pre>
<p>Last, we define the command <code>gr</code>. Its implementation is directly given by the function <code>_gradlew</code>. We can also pass default parameters to the wrapper invocation. For example, to improve startup and execution time of the Gradle invocation, we add the <code>--daemon</code> flag that activates the <a href="https://gradle.org/docs/current/userguide/gradle_daemon.html">Gradle daemon</a>:</p>
<pre class="brush: bash; title: ; notranslate">
alias gr=&quot;_gradlew --daemon&quot;
</pre>
<h1>Auto-completion</h1>
<p>Implementing auto-completion requires not only some bash scripting but also some implementation on the Gradle side that gives us the list of auto-completable suggestions. We introduce a task named <code>bashCompletion</code> that generates the list of tasks in a multi-project build and writes them line-by-line into a text file. The <code>bashCompletion</code> task is defined in the root Gradle project (but could be put for example in an <a href="https://gradle.org/docs/current/userguide/init_scripts.html">init script</a> if it were to be used for many builds):</p>
<pre class="brush: groovy; title: ; notranslate">
task bashCompletion &lt;&lt; {
  def allTaskNames = []
  getAllTasks(true).each { proj, taskSet -&gt;
    taskSet.each {
      if (proj.parent) {
        allTaskNames += proj.name + ':' + it.name
      } else {
        allTaskNames += it.name
      }
    }
  }
  def commands = (defaultGradleCommands + allTaskNames).sort().unique().join('\n')
  file('gradle-autocomplete').text = commands
}
</pre>
<p>The presented implementation is a very basic one and many customizations are possible here. For example, task exclusion lists or other build parameters could be used. The auto-completable suggestions are stored sorted in a line-based text file so that it has a nice format for diffs when we put it into version control. More information on why this might be useful is provided in another blog article <a href="http://welsch.lu/blog/2015/01/integrating-gradle-with-eclipse/" title="Integrating Gradle with Eclipse for larger teams">here</a>.</p>
<p>On the Bash side (<code>~/.bashrc</code>), basic auto-completion is fairly trivial to implement. We use the previously defined function <code>_findgradledir</code> to find the text file of auto-completable suggestions created by our <code>bashCompletion</code> task:</p>
<pre class="brush: bash; title: ; notranslate">
_gradle_complete()
{
  local cur
  _get_comp_words_by_ref -n : cur

  local gradle_project_dir=`_findgradledir`
  if [ -z &quot;$gradle_project_dir&quot; ]; then
    return 1
  fi
  
  if [ ! -f &quot;$gradle_project_dir/gradle-autocomplete&quot; ]; then
    return 1
  fi
  
  local commands=$(cat &quot;$gradle_project_dir/gradle-autocomplete&quot; | tr '\n' ' ')
  COMPREPLY=( $(compgen -W &quot;$commands&quot; -- $cur) )
  __ltrim_colon_completions &quot;$cur&quot;
}
</pre>
<p>The line <code>_get_comp_words_by_ref -n : cur</code> is needed for bash completion to work with colons in task calls of sub-projects.</p>
<p>Finally we use the bash <code>complete</code> command to attach the implemented bash completion function to our Gradle shorthand <code>gr</code>:</p>
<pre class="brush: bash; title: ; notranslate">
complete -F _gradle_complete gr
</pre>
]]></content:encoded>
			<wfw:commentRss>http://welsch.lu/blog/2015/01/gradle-bash-integration-for-multi-project-builds/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
