Running Windows batch files from eclipse using workspace-relative paths

Every now and then even a windows user might feel the urge to run some batch processes.
In eclipse, batch files can be run by right clicking on the file | Open With | System Editor. This runs the batch file in a new console window.
However, the execution of the batch file starts working in the eclipse installation directory. That is, path that are relative to the folder of the batch file are now invalid.
The (seemingly) simplest solution might be to just use absolute file paths. However, this might turn out a bad idea. For example, when sharing the project – as others might store their workspace on a different position within their file systems.

So, basically, there are three ways to avoid this problem.

  1. Changing the batch file’s working directory.
  2. Using eclipse’s external tools
  3. Alternative: Using Ant’s exec task
  4. Integrate the shell into eclipse using Wicked Shell Plugin

 

  1. Changing the batch file’s working directory
  2. Add the following line to the beginning of your batch file:

    @cd /D %~dp0
    

    This will change the working directory of to the path of the batch file. So every subsequent command in the batch file is relative to the path of the batch file itself.
    Note: the /D-switch makes sure the file path is changed even though it resides on a different partition.
    You can verify this behavior by executing the current batch file in eclipse:

    :: Right now we're in the eclipse working directory
    @echo %cd%
    @cd /D %~dp0
    :: We changed the directory to the script location
    @echo %cd%
    :: We now can use workspace relative paths!
    @pause
    

    This will put out the path to the eclipses installation and then the path to the batch file.
     

  3. Using eclipse’s external tools
  4. This approach offers the advantage that the batch file’s output is shown on the eclipse console view. However, a launch configuration is needed.
    The following steps are necessary:

    1. In the menu, click Run | External Tools or on the drop down menu next to External Tools symbol.
    2. Click External Tools configurations….
    3. Click Program External Tools symbol, then New Launch Configuration symbol.
    4. Choose Location and Working Directory within your workspace.
    5. (If you want to persist the launch configuration (e.g. for sharing among your team), go to the Common tab, press the radio button Shared file and enter a path, such as /<project>/launch or whatever suits.)
    6. Finally, hit Run and check eclipse’s console view.

     

  5. Using Ant’s exec task
  6. First of all we need an Ant build file:

    • Right click on the project | New | New symbolFile.
    • In the upcoming dialog enter a file name ending in “.xml” and press Finish. Note that if you name the file “build.xml”, you will have less trouble running the file later.
    • Within the editor paste an Ant-script, such as this:
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE project>
      
      <project name="runBatch" basedir="." default="runBatch">
      
      	<target name="runBatch">
      		<echo>Running batch file directly</echo>
      		<exec executable="test.bat">
      			<!-- <arg value="-p someParam" /> -->
      		</exec>
      
      		<echo>Running batch file using cmd</echo>
      		<exec executable="cmd">
      			<arg value="/c" />
      			<arg value="test.bat" />
      			<!-- <arg value="-p someParam" /> -->
      		</exec>
      	</target>
      </project>
      

      As you can see, there are two ways of running a batch file from ant: Using the exec task directly or using the exec task to run cmd.exe and passing the batch file to this process using the /c-switch (as described in the exec task’s manual). Which one you use, is up to you. Either way, you can pass parameters to the batch file using the <arg> tag.

    Now that we have a build file, what will we do with it? If you called it “build.xml” earlier, just right click the file | Run As (or hit Ctrl + F11 from the editor). Then click Ant symbol Ant Build an that’s it!
     
    (If you ignored my warning and chose a different file name, you’ll need a run configuration to execute the Ant build. This is pretty much the same as described above for using eclipse’s external tools to execute the batch file directly. Except that you have to click on Ant symbolAnt Build instead of Program.

  7. Using Wicked Shell Plugin
  8. If you’re using the command line a lot, the Wicked Shell might be interesting to you. It allows for using the command line from within eclipse. It also provides a comfortable way of running batch files. See this blog post for more info.
    It you’re running eclipse on an OS other than Windows, Wicked Shell works as well. It always runs the system’s default shell, i.e. bash for Linux and OS X.

  
So, if you want to run batch files from eclipse, just choose one of the approaches described above. Personally, I prefer the first approach, because it comes with the least overhead. No additional launch configuration needed, just double click the batch file and that’s it.

For this post I used the eclipse icons published here. As mentioned there, they are part of the Eclipse Project and licensed under the EPL.

Generating and customizing JUnit and Code Coverage reports with Ant

Recently, I had a hard time finding a solution for automatic JUnit and Code Coverage report generation within an Ant build. Nobody using good old Ant anymore?
Maybe generating these reports using Maven is piece of cake (I don’t know) but as I suppose there are still people who are using (or have to use) Ant, I suppose my solution might be interesting for at least some people.

Solution overview

  • JUnit 4.1
  • JUnit reports (HTML): Apache Ant 1.7.1 (included in Eclipse 3.7.2 helios) / Ant 1.8.3.
  • JUnit reports (PDF): 1.0
  • Code Coverage and reports: JaCoCo 0.5.6

JUnit tasks in Ant

JUnit tests can be executed using the Ant task junit.
The test results are output as XML files. This structured text data can then be transformed to some different format for the purpose of visualization. Eclipse, for instance, uses JUnit’s XML-output to create the notorious green and red bars. Of course, its also possible to generate a report from the “raw” test results. For this purpose, Ant provides the task junitreport, which generates a HTML-report. You can even choose if you prefer to have the report in a single HTML-file (e.g. if you intend to attach the report to an email) or using frames (which is more comfortable to read). If you are interested in generating PDF, you should have a look at the 3rd-party task junitpdfreport.
Both tasks use XSL to transform the XML files, so they can easily be customized.

Measuring Code coverage with Ant

One of the most popular tools for measuring code coverage is called EMMA. There also is a plugin that integrates EMMA in eclipse: EclEmma. Does that have anything to do with creating reports using Ant? Yeah! The EclEmma-guys created a code coverage library which features Ant tasks for measuring code coverage and for creating reports that visualize the results JaCoCo. How nice is that!

 

Measuring the code coverage with Ant and JaCoCo is as easy as it can be: Just wrap whatever JUnit tests you want to be know the code coverage about in the JaCoCo task, but don’t forget to declare the jacoco namespace in the build.xml file:

<project name="AntTestReporting-test" basedir="." default="all-test" xmlns:jacoco="antlib:org.jacoco.ant">
	<!-- ... -->

	<!-- Java Code Coverage -->
	<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
		<classpath path="lib/ant/jacoco/lib/jacocoant.jar" />
	</taskdef>

	<!-- ... -->

	<!-- Run all tests -->
	<jacoco:coverage destfile="${test.data.dir}/jacoco.exec">
		<junit printsummary="true" haltonfailure="false" fork="yes" forkmode="once">
			<jvmarg value="${xms}" />
			<jvmarg value="${xmx}" />
			<!-- <jvmarg value="${log4j.config}" /> -->
			<classpath refid="classpath.test" />
			<formatter type="xml" />
			<batchtest todir="${test.data.dir}">
				<fileset dir="${build.dir}">
					<!-- Exclude inner classes -->
					<exclude name="**/*$*.class" />
					<include name="**/*Test.class" />
				</fileset>
			</batchtest>
		</junit>
	</jacoco:coverage>
</project>

Note: Why forking the tests? This allows for passing JVM arguments to the tests, for example a special configuration of the logging-framework for testing purpose only. Also, Increasing the initial heap size (xms) as well as the maximal heap size (xmx) of the JVM that runs the tests in combination with running all tests in the same (forked) JVM (forkmode=”once”) reduced the time to perform the unit tests drastically (at least for me).

Let’s generate some reports!

Now, for the actual generation of reports, we need to import the Ant-build file for the PDF-reports (if necessary).

 

Then we can go ahead generating our reports.

 

Note that if you’re not interested in customizing the reports you can leave out the parameters for the styledirs, they aren’t mandatory.

 

For demonstration purpose, this code generates three different kinds of JUnit reports:

  • HTML without frames (single file),
  • HTML with frames (multiple files) and
  • PDF

Usually, you might just need one of them.

<!-- PDF-Reports for JUnit -->
<import file="lib/ant/junitpdfreport/build-junitpdfreport.xml" />

<!-- Generate HTML report
- junit-noframes.html -> Single page HTML-report
- index.html -> HTML-report using frames (several files, but more comfortable to read)-->
<junitreport todir="${test.data.dir}">
	<fileset dir="${test.data.dir}">
		<include name="TEST-*.xml" />
	</fileset>
	<report styledir="test/etc/junitreport" format="noframes" todir="${test.reports.dir}" />
	<report styledir="test/etc/junitreport" format="frames" todir="${test.reports.dir}" />
</junitreport>

<!-- Generate PDF report -->
<junitpdfreport todir="${test.reports.dir}" styledir="../../../test/etc/junitpdfreport/default">
	<fileset dir="${test.data.dir}">
		<include name="TEST-*.xml" />
	</fileset>
</junitpdfreport>

<!-- Generate Code Coverage report
See: http://www.eclemma.org/jacoco/trunk/doc/ant.html -->
<jacoco:report>
	<executiondata>
		<file file="${test.data.dir}/jacoco.exec" />
	</executiondata>

	<structure name="AntTestReporting">
		<classfiles>
			<fileset dir="${build.dir}">
				<include name="**/*.class" />
				<!-- Exclude classes necessary for testing only from the code coverage report-->
				<exclude name="**/*Test*.class" />
				<!-- Exclude inner classes -->
				<exclude name="**/*$*.class" />
			</fileset>
		</classfiles>
	</structure>

	<html destdir="${coverage.reports.dir}" />
</jacoco:report>

Customizing reports

By now, our Ant build creates uniformly looking reports. But what if we want them to look differently? Or maybe we want to add some more information during the build? All this can be done by using different XSL-stylesheets.

 

To prove the point, we will generate a timestamp during build and include this in the title of the JUnit HTML and PDF reports.

 

As the JUnit-XML files contain all Ant properties defined during the build process. We will first create our timestamp and then extracted it from the XML during the generation of the reports.

<!-- Create the time stamp -->
<tstamp>
	<format property="lastUpdated" pattern="yyyy-MM-dd HH:mm:ss" />
</tstamp>

What is left to be done is adapting the XSL-Sheets. In the above code examples we already have explicitly mentioned a path to the XSL files (the styledir attribute). A good way to start customization is to modify the sheet provided by Ant, for example this one. This sheet creates the multi-document HTML report with frames. The same applies to the customization of the single-document HTML and PDF reports.

Finally, let’s add the timestamp to the title:

junit-frames.xsl

<xsl:param name="TITLE">Unit Test Results. Build time: <xsl:value-of select="//property[@name='lastUpdated']/@value"/></xsl:param>

… and that’s it!

Result

How do the reports look like? I created a small demo project and generated the following reports:

Sources

To complete the picture, here’s the complete build script. Actually, the script is split up in two files, to keep things simpler:

  • build.xml – contains the “normal” build
  • build-test.xml – uses build.xml to execute the build and then builds and executes the tests, finally generating the reports.

You can pull the demo-project mentioned above from my github repository. It’s and Eclipse project but you should also be able compile it with Ant from the command line, like this:
ant -f build-test.xml.

BTW: Beware of this issue: this issue. I ran into this problem using win7 and either JDK6_32 as well as JDK7_03. I got rid of it by using Ant 1.8.3. To run an external copy of Ant from eclipse, all you have to do is executing the launch config located in the archive under launch/build-test-external.launch by right clicking on it > Run As > build-test-external.

build.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>

<project name="AntTestReporting" basedir="." default="jar">

	<!-- general -->
	<property name="DEBUG" value="true" />
	<property name="VERBOSE" value="true" />
	<property name="TARGET" value="1.6" />

	<!-- folder -->
	<property name="build.dir" value="bin" />
	<property name="src" value="src" />
	<property name="lib" value="lib" />

	<!-- classpath -->
	<path id="classpath">
		<fileset dir="${lib}">
			<include name="**/*.jar" />
		</fileset>
	</path>

	<!-- targets -->
	<target name="clean">
		<delete dir="${build.dir}" />
	</target>

	<target name="compile" depends="clean">
		<mkdir dir="${build.dir}" />
		<mkdir dir="${build.dir}/build" />

		<!-- Create the time stamp -->
		<tstamp>
			<format property="lastUpdated" pattern="yyyy-MM-dd HH:mm:ss" />
		</tstamp>

		<javac target="${TARGET}" debug="${DEBUG}" verbose="${VERBOSE}" classpathref="classpath" optimize="true" destdir="${build.dir}">
			<src path="${src}" />
		</javac>

		<fileset id="srcFiles" dir="${src}">
		   	<exclude name="**/*.java"/>
			<exclude name="**/*.html"/>
			<include name="**/*.*" />
		</fileset>

		<copy todir="${build.dir}">
			<fileset refid="srcFiles"/>
		</copy>

	</target>

	<!-- <target name="jar" depends="compile, test">  -->
	<target name="jar" depends="compile">

		<jar jarfile="${build.dir}/build/${ant.project.name}.jar" basedir="${build.dir}">
			<manifest>
				<attribute name="Build-Time" value="${lastUpdated}" />
				<attribute name="Main-Class" value="com.some.pckge.SomeClass"/>
			</manifest>
		</jar>

		<!-- Remove contents of build dir after packaging -->
		<!-- <delete>
		   <fileset dir="${build.dir}">
		   	<include name="**/*.*" />
		   </fileset>
		</delete> -->
	</target>

</project>

build-test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE project>
<project name="AntTestReporting-test" basedir="." default="all-test" xmlns:jacoco="antlib:org.jacoco.ant">

	<import file="build.xml" />

	<!-- PDF-Reports for JUnit -->
	<import file="lib/ant/junitpdfreport/build-junitpdfreport.xml" />

	<!-- Java Code Coverage -->
	<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
		<classpath path="lib/ant/jacoco/lib/jacocoant.jar" />
	</taskdef>

	<property name="test.build.dir" location="bin/test" />
	<property name="test.src" location="test" />
	<property name="test.data.dir" location="${test.build.dir}/testResults" />
	<property name="reports.dir" location="${test.build.dir}/reports" />
	<property name="test.reports.dir" location="${reports.dir}/junit" />
	<property name="coverage.reports.dir" location="${reports.dir}/coverage" />

	<property name="xms" value="-Xms256m" />
	<property name="xmx" value="-Xmx1024m" />
	<!-- <property name="log4j.config" value="-Dlog4j.configuration=file:/${base.dir}/test/log4j-test.properties" /> -->

	<path id="classpath.test">
		<pathelement location="${build.dir}" />
		<fileset dir="${lib}">
			<include name="**/*.jar" />
		</fileset>
	</path>

	<target name="compile-test" depends="compile">
		<mkdir dir="${test.build.dir}" />

		<javac destdir="${build.dir}" srcdir="${test.src}" includeantruntime="false">
			<classpath refid="classpath.test" />
		</javac>

		<fileset id="srcFiles" dir="${test.src}">
			<exclude name="**/*.java" />
			<exclude name="**/*.html" />
			<exclude name="**/*.xsl" />
			<include name="**/*.*" />
		</fileset>

		<copy todir="${test.build.dir}">
			<fileset refid="srcFiles" />
		</copy>
	</target>

	<target name="clean-compile-test">
		<delete>
			<fileset dir="${test.build.dir}" includes="**/*.*" />
		</delete>
	</target>

	<target name="test" depends="compile-test">
		<mkdir dir="${test.data.dir}" />

		<!-- Run all tests -->
		<jacoco:coverage destfile="${test.data.dir}/jacoco.exec">
			<junit printsummary="true" haltonfailure="false" fork="yes" forkmode="once">
				<jvmarg value="${xms}" />
				<jvmarg value="${xmx}" />
				<!-- <jvmarg value="${log4j.config}" /> -->
				<classpath refid="classpath.test" />
				<formatter type="xml" />
				<batchtest todir="${test.data.dir}">
					<fileset dir="${build.dir}">
						<!-- Exclude inner classes -->
						<exclude name="**/*$*.class" />
						<include name="**/*Test.class" />
					</fileset>
				</batchtest>
			</junit>
		</jacoco:coverage>

		<!-- Generate HTML report
			- junit-noframes.html -> Single page HTML-report
			- index.html -> HTML-report using frames (several files, but more comfortable to read)-->
		<junitreport todir="${test.data.dir}">
			<fileset dir="${test.data.dir}">
				<include name="TEST-*.xml" />
			</fileset>
			<report styledir="test/etc/junitreport" format="noframes" todir="${test.reports.dir}" />
			<report styledir="test/etc/junitreport" format="frames" todir="${test.reports.dir}" />
		</junitreport>

		<!-- Generate PDF report -->
		<junitpdfreport todir="${test.reports.dir}" styledir="../../../test/etc/junitpdfreport/default">
			<fileset dir="${test.data.dir}">
				<include name="TEST-*.xml" />
			</fileset>
		</junitpdfreport>

		<!-- Generate Code Coverage report
			See: http://www.eclemma.org/jacoco/trunk/doc/ant.html -->
		<jacoco:report>
			<executiondata>
				<file file="${test.data.dir}/jacoco.exec" />
			</executiondata>

			<structure name="AntTestReporting">
				<classfiles>
					<fileset dir="${build.dir}">
						<include name="**/*.class" />
						<!-- Exclude classes necessary for testing only from the code coverage report-->
						<exclude name="**/*Test*.class" />
						<!-- Exclude inner classes -->
						<exclude name="**/*$*.class" />
					</fileset>
				</classfiles>
			</structure>

			<html destdir="${coverage.reports.dir}" />
		</jacoco:report>
	</target>

	<target name="all-test" depends="test" />
</project>