I have a webapp to be ran on Tomcat6/JbossAS 5 and, for versioning purposes, I have several AND targets that will perform a set of operations to define a constant with version number and build datetime.
The targets perform the following:
Delete the Release.java
Copy the Release.template to Release.java with the versioning info properly set as a constant (public static final String)
The thing is that, the constant references are replaced on the code before compilation. For instance, where I have
log.debug("Release: " + Release.info);
I can see, using java decompiler, the following (let's assume that "TO_BE_DEFINED" is the initial value for Relase.info):
log.debug("Release: TO_BE_DEFINED");
The targets are the following:
<target name="build-config" >
<!-- config -->
<property name="project.name" value="MY PROJECT" />
<property name="release.prefix" value="MPROJECT" />
<property name="release.num" value="1.0" />
<property name="release.info.file.path" value="org/my/project/Release" />
<tstamp>
<format property="release.date" pattern="yyyy-MM-dd HH:mm:ss z" />
</tstamp>
<!-- end config -->
<property name="release.version" value="${release.prefix}-${release.num}" />
<property name="release.info" value="${release.version} ${release.date}" />
</target>
<target name="build-replace">
<filter token="release.info" value="${release.version} ${release.date}" />
<delete file="${src.dir}/${release.info.file.path}.java" />
<copy file="${src.dir}/${release.info.file.path}.template" tofile="${src.dir}/${release.info.file.path}.java" filtering="true" />
</target>
<target name="build-myproj-1.0.jar" depends="init,buil-config,build-replace,compile">
<mkdir dir="${dist.dir}"/>
<jar compress="true" jarfile="${dist.dir}/MPROJECT-1.0.jar">
<fileset dir="${build.classes.dir}"
excludes="META-INF/*.*, WEB-INF/classes/*.xml"
includes="**/*.class"
/>
<manifest>
<section name="${project.name}">
<attribute name="Release-Version" value="${release.version}" />
<attribute name="Release-Date" value="${release.date}" />
</section>
</manifest>
</jar>
</target>
and my class is defined as
public abstract class Release {
/** Release info */
public static final String INFO = "TO_BE_DEFINED";
}
I understand that this may be a little bit confusing and if I'm missing some information please feel free to ask
Compile-time constants are replaced in client code when the client code is compiled.
To get the client code to pick up the new value of the constant, either recompile everything, replace the field with a getter, or use one of the strategies from this question
Related
I have a ant build script which creates a war file. The file content are as follows.
<?xml version="1.0" encoding="UTF-8"?>
<project name="TestProj" default="war" basedir=".">
<property name="project-name" value="${ant.project.name}" />
<property name="builder" value="IaasTeam" />
<property name="war-file-name" value="${project-name}.war" />
<property name="source-directory" value="src" />
<property name="classes-directory" value="build/classes" />
<property name="web-directory" value="WebContent" />
<property name="web-xml-file" value="WebContent/WEB-INF/web.xml" />
<property name="lib.dir" value="WebContent/WEB-INF/lib" />
<property name="catalina.home" value="../../outside/project/lib"/>
<tstamp prefix="build-info">
<format property="current-date" pattern="d-MMMM-yyyy" locale="en" />
<format property="current-time" pattern="hh:mm:ss a z" locale="en" />
</tstamp>
<property name="build-directory" value="build" />
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
<fileset dir="${catalina.home}" includes="**/*.jar"/>
</path>
<target name="clean">
<delete dir="build"/>
</target>
<target name="compile">
<mkdir dir="build/classes"/>
<javac includeantruntime="false" srcdir="src" destdir="build/classes" classpathref="classpath" />
</target>
<target name="war" depends="clean,compile">
<mkdir dir="${build-directory}" />
<delete file="${build-directory}/${war-file-name}" />
<war warfile="${build-directory}/${war-file-name}" webxml="${web-xml-file}">
<classes dir="${classes-directory}" />
<fileset dir="${web-directory}">
<!-- Need to exclude it since webxml is an attribute of the war tag above -->
<exclude name="WEB-INF/web.xml" />
</fileset>
<manifest>
<attribute name="Built-By" value="${builder}" />
<attribute name="Built-On" value="${build-info.current-date}" />
<attribute name="Built-At" value="${build-info.current-time}" />
</manifest>
</war>
</target>
I am using Jenkins as a build server (this is hosted on different machine kind of DEV environment).
I also use Gitlab as a repository and after pushing the latest code I have a hook for Jenkins job which gets triggered automatically and calls this build.xml.
Now the issues here is that when I run this script on my local machine everything works well but when Jenkins execute this it fails during the compilation phase giving me below error.
compile:
[mkdir] Created dir: /app/infra/jenkins/workspace/TestProj/build/classes
[javac] Compiling 49 source files to /app/infra/jenkins/workspace/TestProj/build/classes
BUILD FAILED
/app/infra/jenkins/workspace/TestProj/build.xml:27: /app/infra/jenkins/outside/project/lib does not exist.
The reason for this issue is the build server does not have any directoy called outside/project/lib.
The only reason of adding this directory in my build.xml is to have the container specific jar files ready for compiling.
How can I fix this issue?
Do I need to copy container specific jars on my build server? Or is there any way to tell Jenkins that not to copy this external jars but just use them for compilation.
Where would Jenkins find the jars? They need to be accessible otherwise your build will fail. If you don't want to have the files checked in (which is very sensible), you could use Apache Ivy to download them for you.
This is the most common way of handling the situation you're having. Using a dependency management framework like Ivy (or Maven, or similar) will save you a lot of headaches down the line. I recommend you have a look at their tutorial. After you set it up, your ant build will take care of downloading the files you need.
I have an ant build file in which:
<target name="foo">
<apply executable="bar" parallel="true">
<fileset dir="." includes="*.xxx" />
<srcfile prefix="--session " />
</apply>
</target>
This will call bar with a single argument of --session a.xxx for each file of the type .xxx in the current directory. How can I get ant to invoke bar with two arguments for each file of the type .xxx?
In the ideal situation bar would receive two arguments, --session and a.xxx for each file of the type .xxx.
It's a little late but I hope anyone who faces this problem in the future can consider my solution and hopefully solve your problem.
For my example, I am trying to execute yuicompressor with multiple options operating on multiple input files:
<apply executable="java" parallel="true">
<fileset dir="./js-ori">
<include name="**/*.js" />
</fileset>
<arg line="-jar" />
<arg path="yuicompressor.jar" />
<arg line="--nomunge" />
<arg line="--preserve-semi" />
<srcfile />
<arg line="-o" />
<arg line="'.js$:.min.js'" />
</apply>
Since apply command with 'parallel="true"' will only apply the options on the first input file, the result is not what is intended. Instead of having ANT pass in the input files, pass in the input files manually using < arg line >.
<!-- Get the fileset as a string -->
<fileset id="myFileSet" dir=".">
<include name="js/**/*.js" />
<exclude name="js/lib/**" />
</fileset>
<property name="fileset" refid="myFileSet" />
<!-- Replace all ';' of the string of files from fileset to your intended options -->
<replaceStringWithRegExp string="${fileset}"
searchPattern=";"
replacementPattern=" --nomunge --preserve-semi "
property="result"/>
<!-- We will disallow <apply> to take in input files by itself since we are already appending the input files manually. -->
<!-- Therefore we add 'addsourcefile="false"' -->
<apply executable="java" parallel="true" addsourcefile="false">
<fileset dir="./js">
<include name="**/*.js" />
<exclude name="lib/**" />
</fileset>
<arg line="-jar" />
<arg path="yuicompressor-2.4.7.jar" />
<arg line="-o" />
<arg line="'.js$:.min.js'" />
<arg line="${result}" /> <!-- pass in the string of options and file names -->
</apply>
This is my solution for executing yuicompressor once with multiple input files and options. I am unsure about other execution of .jar files but the solution should have a similar concept. If you find any part of the code that could be improved, please sound it out to me! Thank you!
Note: If you don't want to install ant-contrib for , you can use the replaceStringWithRegExp function created by Giorgio Ferrara in the link below.
Reference
Thanks Giorgio Ferrara for the replaceStringWithRegExp function!
You can use an extra <arg/> tag to include --session as a separate argument:
<target name="foo">
<apply executable="bar" parallel="false">
<fileset dir="." includes="*.xxx" />
<arg value="--session" />
<srcfile />
</apply>
</target>
There are a couple of questions to this topic already, but none of them seem to work properly.
Here is a list of them:
Bulding an multi-platform SWT application using Ant
Build multi-platform executable for a SWT application (Eclipse)
Build multi-platform executable for a SWT application using maven
SWT jar for different platform
Create cross platform Java SWT Application
My requirement is to build an ant script that creates one jar per platform, i.e. one for Windows x86, one for Windows x64, Linux x86/x64 and so on.
Does anyone have any further insight?
Using the aforementioned methods, I was not able to produce a workable solution. It either ended with the SWT jar file not automatically being loaded or it not being included in the classpath.
If someone can come up with a working example (ideally including the complete source code), that would be grand!
Right, so I finally came up with a solution that I successfully tested on three platforms.
The two magic components are the jar-in-jar-loader and a proper build script.
The build script with comments can be found here:
<project name="RandomApp" basedir="." default="clean-build">
<property name="src.dir" value="src" />
<!-- Define the necessary paths -->
<property name="build.dir" value="bin_temp" />
<property name="lib.dir" value="lib" />
<property name="lib.deploy.dir" value="lib_swt" />
<property name="classes.dir" value="${build.dir}/classes" />
<property name="jar.dir" value="${build.dir}/jar" />
<property name="img.dir" value="img" />
<property name="res.dir" value="res" />
<!-- Define the main class -->
<property name="main-class" value="org.baz.desktop.randomapp.gui.RandomApp" />
<path id="base-classpath">
<fileset dir="${lib.dir}" includes="**/*.jar" />
</path>
<!-- Define the class path -->
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar" />
<fileset dir="${lib.deploy.dir}" includes="**/swt_win32_x64.jar" />
</path>
<!-- Clean previously built files -->
<target name="clean">
<delete dir="${build.dir}" />
</target>
<!-- Compile the project -->
<target name="compile">
<mkdir dir="${classes.dir}" />
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="false" />
</target>
<macrodef name="createclasspath">
<attribute name="name" />
<attribute name="swtlib" />
<sequential>
<pathconvert property="#{name}.classpath" pathsep=" ">
<path refid="base-classpath" />
<fileset dir="${lib.deploy.dir}" includes="**/swt_#{swtlib}.jar" />
<mapper>
<chainedmapper>
<flattenmapper />
<globmapper from="*.jar" to="*.jar" />
</chainedmapper>
</mapper>
</pathconvert>
</sequential>
</macrodef>
<!-- Define classpath and create the jar folder -->
<target name="pre_jar" depends="compile">
<!-- Linux 32bit -->
<createclasspath name="win86" swtlib="win32_x86" />
<!-- Linux 64bit -->
<createclasspath name="win64" swtlib="win32_x64" />
<!-- Windows 32bit -->
<createclasspath name="linux86" swtlib="linux_gtk_x86" />
<!-- Windows 64bit -->
<createclasspath name="linux64" swtlib="linux_gtk_x64" />
<!-- MacOS 32bit -->
<createclasspath name="macos86" swtlib="macos_x86" />
<!-- MacOS 64bit -->
<createclasspath name="macos64" swtlib="macos_x64" />
<mkdir dir="${jar.dir}" />
</target>
<macrodef name="createjar">
<attribute name="swtlib" />
<attribute name="swtclasspath" />
<sequential>
<jar destfile="${jar.dir}/${ant.project.name}_#{swtlib}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader" />
<attribute name="Rsrc-Main-Class" value="${main-class}" />
<attribute name="Class-Path" value="." />
<attribute name="Rsrc-Class-Path" value="./ #{swtclasspath}" />
</manifest>
<zipgroupfileset dir="${lib.dir}" includes="**/jar-in-jar-loader.jar" />
<zipfileset dir="${lib.deploy.dir}" includes="**/swt_#{swtlib}.jar" />
<zipfileset dir="${lib.dir}" includes="**/*.jar" excludes="**/jar-in-jar-loader.jar" />
</jar>
</sequential>
</macrodef>
<!-- Create the jar files -->
<target name="jar" depends="pre_jar">
<!-- Linux 32bit -->
<createjar swtlib="linux_gtk_x86" swtclasspath="${linux86.classpath}" />
<!-- Linux 64bit -->
<createjar swtlib="linux_gtk_x64" swtclasspath="${linux64.classpath}" />
<!-- Windows 32bit -->
<createjar swtlib="win32_x86" swtclasspath="${win86.classpath}" />
<!-- Windows 64bit -->
<createjar swtlib="win32_x64" swtclasspath="${win64.classpath}" />
<!-- MacOS 32bit -->
<createjar swtlib="macos_x86" swtclasspath="${macos86.classpath}" />
<!-- MacOS 64bit -->
<createjar swtlib="macos_x64" swtclasspath="${macos64.classpath}" />
</target>
<target name="clean-build" depends="clean,jar" />
</project>
What it basically does is define a base classpath without any SWT library.
Then it creates platform specific classpaths using the base one and adding the appropriate platform SWT library.
The jar task then creates a separate jar for each platform using these classpaths and the jar-in-jar-loader.
And that's it, a fully automated way of generating jars for each (supported) platform.
I've created an example project that people can download and test out. It's an easy starting point for a multi-platform application.
https://www.dropbox.com/s/ianrbl4bn0fmsdi/SWTApplication.7z
Update:
I've managed to significantly shorten the ant script by making proper use of macrodef :)
Can i set an variable in available tag like this?
<available file="${inf.root}/schema_params/#{componame}-schema.sql"
type="file"
variable="schema.file" />
because when i use property instead of variable in available tag, Its value is immutable. But i want to change it at run time.Below is my code.i want to copy 1 file checking through my component list. if the file exists, i have to copy and move it. else i have to skip the logic. But whats happening is, if i dont use this code,
<var name="schema.params.file" unset="true"/>
<property name="schema.params.file" value="false"/>
<var name="scripts.dir" unset="true"/>
<property name="scripts.dir" value="false"/>
in the first iteration if schema.params.file,scripts.dir is set to true if files exist, it is not overided in the next iteration even though if file doenot exist. so i have added above code.but now always the values are set to false again by above code. how can i overcome the issue of overiding these 2 schema.params.file,scripts.dir in every iteration?
<for list="${t1.#{componentname}}" param="installableid" delimiter="${line.separator}">
<sequential>
<available file="${infinys.root}/schema_params/#{componentname}-schema_params.sql"
type="file"
property="schema.params.file" />
<available file="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts"
type="dir" property="scripts.dir"/>
<if>
<and>
<equals arg1="true" arg2="${schema.params.file}" />
<equals arg1="true" arg2="${scripts.dir}" />
</and>
<then>
<copy file="${infinys.root}/schema_params/#{componentname}-schema_params.sql"
todir="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts"
failonerror="false" />
<move file="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts/#{componentname}-schema_params.sql"
tofile="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts/schema_params.sql"
failonerror="false"/>
<chmod file="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts/schema_params.sql" perm="775"/>
<var name="schema.params.file" unset="true"/>
<property name="schema.params.file" value="false"/>
<var name="scripts.dir" unset="true"/>
<property name="scripts.dir" value="false"/>
</then>
</if>
</sequential>
</for>
Can i set an variable in available tag like this?
Yes, you can.
Macro names are changed with each iteration. The <var/> task is simply a way to unset and reset a property in Ant. It's part of the Ant-Contrib project. You don't need to unset and reset the property:
<var name="schema.params.file" unset="true"/>
<property name="schema.params.file" value="false"/>
You could do this in a single statement:
<var name="schema.params.file" value="false"/>
Their use is highly discouraged since it breaks Ant's immutable property idea. However, I've find that I too use <var/> a lot when going through <for/> loops and sometimes <macrodef>. Newer versions of Ant allow you to localize properties, so I suspect the <var/> task will soon no longer be needed.
Another thing which may make things a bit easier is that you can use <if/> tests with <available/>
<if>
<then>
<and>
<available file="${infinys.root}/schema_params/#{componentname}-schema_params.sql"
type="file"/>
<available file="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts"
type="dir"/>
</and>
<sequencial>
....
</sequential/>
</then>
</if>
Doing this may make your code a bit cleaner and easier to understand. It will also eliminate the need to unset properties in the first place.
Ant doesn't allow overwriting properties. You still can use macrodef but that's another story (see How to over-write the property in Ant?).
What you can do is move the internals of your cycle to the separate target like (can't see why you copy a file to another location and immediately move it to another another location, so I just replaced this copy-move with one copy)
<target name="cycle-body">
<available file="${schema.params.file.name}" type="file" property="schema.params.file" />
<available file="${scripts.dir.name}" type="dir" property="scripts.dir"/>
<if>
<and>
<equals arg1="true" arg2="${schema.params.file}" />
<equals arg1="true" arg2="${scripts.dir}" />
</and>
<then>
<copy file="${schema.params.file.name}"
tofile="${scripts.dir.name}/schema_params.sql" failonerror="false"/>
<chmod file="${scripts.dir.name}/schema_params.sql" perm="775"/>
</then>
</if>
</target>
and call it like
<for list="${t1.#{componentname}}" param="installableid" delimiter="${line.separator}">
<antcall target="cycle-body">
<param name="schema.params.file.name" value="${infinys.root}/schema_params/#{componentname}-schema_params.sql" />
<param name="scripts.dir.name" value="${stage.dir}/#{componentname}/#{installableid}/schema/install/scripts" />
</acntcall>
</for>
I have a property, app.version, which is set to 1.2.0 (and, of course, always changing) and need to create zip file with name "something-ver-1_2_0". Is this possible?
You can use the pathconvert task to replace "." with "_" and assign to a new property:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<property name="app.version" value="1.2.0"/>
<pathconvert property="app.version.underscore" dirsep="" pathsep="" description="Replace '.' with '_' and assign value to new property">
<path path="${app.version}" description="Original app version with dot notation" />
<!--Pathconvert will try to add the root directory to the "path", so replace with empty string -->
<map from="${basedir}" to="" />
<filtermapper>
<replacestring from="." to="_"/>
</filtermapper>
</pathconvert>
<echo>${app.version} converted to ${app.version.underscore}</echo>
</project>
Another approach is to filter the version number from a file to a property using a regular expression, as suggested in this example:
<loadfile srcfile="${main.path}/Main.java" property="version">
<filterchain>
<linecontainsregexp>
<regexp pattern='^.*String VERSION = ".*";.*$'/>
</linecontainsregexp>
<tokenfilter>
<replaceregex pattern='^.*String VERSION = "(.*)";.*$' replace='\1'/>
</tokenfilter>
<striplinebreaks/>
</filterchain>
</loadfile>
Since the property app.version is always changing i assume you don't want to hard code it into the properties files, rather pass it when you do the build. Further to this answer, you can try the following on the command line;
ant -f build.xml -Dapp.version=1.2.0
changing app.version to the one required then.
Edit:
Understood better your question from the feedback. Unfortunately ant does not have string manipulation tasks, you need to write you own task for this. Here is a close example.
It's possible using the zip task
<zip zipfile="something-ver-${app.version}.zip">
<fileset basedir="${bin.dir}" prefix="bin">
<include name="**/*" />
</fileset>
<fileset basedir="${doc.dir}" prefix="doc">
<include name="**/*" />
</fileset></zip>
For more information about the zip task: http://ant.apache.org/manual/Tasks/zip.html
Properties can't be changed but antContrib vars (http://ant-contrib.sourceforge.net/tasks/tasks/variable_task.html ) can.
Here is a macro to do a find/replace all on a var:
<macrodef name="replaceVarText">
<attribute name="varName" />
<attribute name="from" />
<attribute name="to" />
<sequential>
<local name="replacedText"/>
<local name="textToReplace"/>
<local name="fromProp"/>
<local name="toProp"/>
<property name="textToReplace" value = "${#{varName}}"/>
<property name="fromProp" value = "#{from}"/>
<property name="toProp" value = "#{to}"/>
<script language="javascript">
project.setProperty("replacedText",project.getProperty("textToReplace").split(project.getProperty("fromProp")).join(project.getProperty("toProp")));
</script>
<ac:var name="#{varName}" value = "${replacedText}"/>
</sequential>
</macrodef>
Then call the macro like:
<ac:var name="newFileName" value="${app.version}"/>
<current:replaceVarText varName="newFileName" from="." to="_" />
<echo>Filename will be ${newFileName}