I have some code that I revised.
When I try to just run Ant in the directory it fails with missing classes. I can specify the location to the existing classes by using the -lib option to ant. The compile then works fine, however dist ZIP file that is created appears to have missing libraries, as when I try to run it, I see errors relating to missing classes which are the classes that I specified with the -lib option, so this is probably due to the way I have used the -lib option.
How can I force the regular Ant command to include the additional classes specified with the -lib command?
You can write a target that will copy your lib directory/files in your zip file.
Let's say create a temp dir then copy your files then execute target for copying lib directory and then zip temp dir.
<target name="copyLib">
<copy todir="${temp.dir}">
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
</copy>
</target>
Update paths and call this target into your create zip target.
Compiler task could look like this:
<javac srcdir="${base}/src"
destdir="${base}/classes"
classpath="${base}/lib">
</javac>
And the zip task could look like this:
<zip
destfile="${base}/dist.jar"
basedir="${base}/classes"
includes="..."
excludes="...">
</zip>
So sources are compiled in classes and zipped in a jar, but libraries used for compile are not included in the jar, they are runtime dependencies.
I would suggest that you declare your paths at the top of your ANT scripts as follows, using a fileset.
<path id="build.path">
<fileset dir="lib" includes="*.jar"/>
</path>
Less error prone compared to forcing users to specify the correct "-lib" parameter.
Finally the same fileset can then be used to include the same jars inside the zip file you're creating:
<zip destfile="${dist.dir}/mycode.zip">
..
..
<fileset dir="lib" includes="*.jar"/>
</zip>
Related
For a variety of reasons (regardless of whether or not this is a wise idea) we have a project with several packages (say one, two, three) that are similar in structure. There is one additional common package.
I want the build to fail when something in each of those packages imports something that isn't in common, e.g. because you accidentally accepted the wrong import completion, or because Eclipse silently brought over the wrong imports while copy-pasting bug fixes from one into two.
What works is copying all the source files, except for each package, over to a temporary folder and attempting to build the result:
<target name="enforce-no-cross-imports">
<phony-build without="one"/>
<phony-build without="two"/>
<phony-build without="three"/>
</target>
<macrodef name="phony-build">
<attribute name="without" />
<sequential>
<echo message="Checking there are no cross imports to the #{without} package." />
<mkdir dir="${java.io.tmpdir}/my-awesome-project/phony-build-#{without}/src" />
<mkdir dir="${java.io.tmpdir}/my-awesome-project/phony-build-#{without}/bin" />
<copy todir="${java.io.tmpdir}/my-awesome-project/phony-build-#{without}/src">
<fileset dir="${src.dir}">
<include name="**/*.java"/>
<exclude name="**/#{without}/"/>
</fileset>
</copy>
<javac srcdir="${java.io.tmpdir}/my-awesome-project/phony-build-#{without}/src"
destdir="${java.io.tmpdir}/my-awesome-project/phony-build-#{without}/bin"
classpathref="classpath" encoding="UTF-8" nowarn="on"/>
<delete dir="${java.io.tmpdir}/my-awesome-project" />
</sequential>
</macrodef>
However, when the build DOES break, the error obviously occurs inside ${java.io.tmpdir}.
enforce-no-cross-imports:
[echo] Checking there are no cross imports to the one package.
[mkdir] Created dir: C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\src
[mkdir] Created dir: C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\bin
[copy] Copying 47 files to C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\src
[javac] Compiling 47 source files to C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\bin
[javac] C:\Users\me\AppData\Local\Temp\my-awesome-project\phony-build-one\src\my\awesome\project\two\very\long\path\SomeController.java:43: package my.awesome.project.one.very.long.path.SomeConstantsClass does not exist
[javac] public static final String TEST = my.awesome.project.one.very.long.path.SomeConstantsClass.TEST;
As a result I can't click on the file name to jump directly to the issue. This adds way too much friction to the compilation process: it's bad enough that I'm effectively tripling the project build times.
What I thought I could do instead is not copy the files over and use the excludes attribute on the javac task instead:
excludes: Comma- or space-separated list of files (may be specified using wildcard patterns) that must be excluded; no files (except default excludes) are excluded when omitted.
...except this does not cause the build to break. What happens is that javac will not compile the other packages, but it will still look at them for confirmation. Turning on verbose shows this log line:
[javac] [checking my.awesome.project.one.very.long.path.SomeConstantsClass]
includeDestClasses seemed like another helpful flag, but setting it to false didn't help. <compilerarg value="-implicit:none"/> also was not helpful.
How can I tell javac that it really positively does need to exclude those files from the build, even if they are right there?
Alternatively, how can I take the output of javac and run find-replace on it so that the console output shows the correct path instead?
You could use the compilewithwalls or verifydesign tasks of ant-contrib, but the safest way to enforce separation like this is to isolate the modules into separate source directories and compile them independently. You'd first build the common module, then build each of the other modules with the compiled classes of "common" on their classpaths but not the classes of any of the other modules. This is the default approach when you're building with a higher level build tool like Maven. If you have a directory structure like this:
build.xml
lib
library JAR files
common
src
classes
one
src
classes
two
src
classes
then you could structure the build as
<macrodef name="compile-module">
<attribute name="module" />
<element name="depends" implicit="true" optional="true"/>
<sequential>
<javac srcDir="#{module}/src" destDir="#{module}/classes"
encoding="UTF-8" nowarn="on">
<classpath>
<path refid="classpath" />
<depends/>
</classpath>
</javac>
</sequential>
</macrodef>
<compile-module name="common" />
<compile-module name="one">
<pathelement location="common/classes" />
</compile-module>
<compile-module name="two">
<pathelement location="common/classes" />
</compile-module>
<compile-module name="three">
<pathelement location="common/classes" />
</compile-module>
You mention Eclipse in the question, so you'd need to do something similar there. Eclipse allows the root of one project to be inside another, so you could create one main Eclipse project "myapp-common" pointed at your project root with just common/src as its source directory (and common/classes as the corresponding output directory), then create separate "myapp-one", "myapp-two" etc. projects rooted at the one, two, etc. folders, each depending on the "myapp-common" project. That way you would never get Eclipse offering the "wrong" auto-completion, as project one simply cannot see project two or vice versa.
I am new to apache ant and I am currently working on an apache Ant project. I Just started out, imported the project into workspace and tried to run the build.xml. I added all the libraries that come with the original project to the build path. I am having the following problem. Please someone else wrote the code and I am supposed to improve it. The directories this is all about exist in the project directory.
BUILD FAILED
C:\workspace\MyApp\build.xml:83: srcdir "C:\workspace\MyApp\${compile.javac.srcdir}" does not exist!
The error code is referencing the following part of the build.xml file
<target name="compile.default" depends="init">
<javac fork="yes" srcdir="${compile.javac.srcdir}" destdir="${compile.javac.destdir}" includes="${compile.javac.include}" excludes="${compile.javac.exclude}" classpath="${compile.javac.classpath}" debug="${compile.javac.debug}" optimize="${compile.javac.optimize}" deprecation="${compile.javac.deprecation}" verbose="${compile.javac.verbose}">
</javac>
<copy todir="${compile.javac.destdir}">
<fileset dir="${compile.javac.srcdir}" includes="${compile.copy.include}" excludes="${compile.copy.exclude}"/>
</copy>
</target>
<target name="compile" depends="init,compile.default" description="Compile all java source">
</target>
<!--+++++++++++++++-->
<!-- lib target(s) -->
<!--+++++++++++++++-->
<target name="lib.default" depends="init,compile">
<xmlbean schema="config/schemas/validate/1.0/validate.xsd" destfile="lib/glx-beans.jar" classpath="lib/xbean.jar:lib/jsr173_1.0_api.jar" />
<jar jarfile="${lib.filename}">
<fileset dir="${lib.srcdir}" excludes="${lib.exclude}" />
</jar>
</target>
<target name="lib" depends="init,compile,lib.default" description="Create all Project Libraries">
</target>
Would you please tell me what I am missing?
The ${compile.javac.srcdir} isn't defined. There are a few possibilities:
This is defined not in the build.xml, but in some sort of properties file. See if you have something like <property file="..."/> in your build script. My recommendation is to have all properties defined in the build.xml file, and use a properties file to override those settings. This way, the only build file that a developer needs in the build.xml file and doesn't have to worry about setting up a separate build.porperties file.
This is defined in the build.xml file under a particular task, but you forgot to say that your target where you use thisis dependent upon this task.
One of the things you can do is use the -d parameter when running Ant. I run the following command when running Ant with the -d parameter:
$ and -d 2>&1 | tee ant.out
I can then look at ant.out and see if somehow I didn't define that particular property. Maybe I had the wrong capitalization or misspelled the property name. For example, it's very likely I'll define the property as copmile.javac.srcdir because I don't know how to spell. Looking at the -d output can quickly point these types of errors out.
By the way, you shouldn't have all of your tasks dependent upon init since they're dependent upon compile.default anyway:
<target name="compile.default" depends="init">
....
</target>
<target name="compile" depends="compile.default">
....
</target>
<target name="lib" depends="compile,lib.default">
....
</target>
If I run the target lib, it will see compile is dependent upon compile.default which is dependent upon init. Thus, your build will run init, then compile.default, then compile, then 'lib.defaultand finallylib`.
If the init task is just setting up properties, you can do that outside of any task. Then, these properties will be setup before any task is executed. This way, they're not forgotten. If your init is also creating directories, you may want to move those <mkdir/> tasks in front of the task where that directory is used. For example, you may want to make the destdir uses in javac before the <javac/> task.
I find assigning default properties outside of any task, and creating directories before they are needed to simplify the build.xml. Plus, you're not creating a whole flock of unused directories if the user is merely compiling and not packaging the jar/war/etc.
I have a Java project with all source files in the src folder. This src folder contains several packages. Some of these packages/folders do not include any Java code. They just contain some images that I use in the application as icons.
The directory structure is similar to the following.
src/
com/
example/
pk1/ # This contains only some Java files.
pk2/ # This will contain some XML and Java code.
im1/ # This subfolder will contain just the images
Now, when I use the javac task, it compiles all the Java files into class files and stores them in the destination directory that I specify. However, an non-java files remain as is in the source folder.
I referred this Question on SO. However, I think in my case I want to include all non-Java resource files (without the need to explicitly specifying what files to be moved) automatically into the build directory (one that contains all the class files) while following the same directory structure as in source.
Pardon me if I am missing something very trivial, but how do I ask Ant to do this?
Use the ant copy task to copy your resources into the classes directory (or wherever your .class files are stored). By default, ant keeps the directory structure.
For example:
<copy todir="classes">
<fileset dir="com/example">
<exclude name="**/*.java"/>
</fileset>
</copy>
Just to complete the previous answer, the block
<copy todir="classes">
<fileset dir="com/example">
<exclude name="**/*.java"/>
</fileset>
</copy>
goes before the task javac
<javac [...]
and inside the target compile
<target name="compile" [...]
then
<target name="compile" [...]>
<copy todir="&&&" [...]>
[...]
</copy>
<javac srcdir="[...]" destdir="&&&" >
</javac>
</target>
in my case the attribute "todir" of tag "copy" it's the same of the attribute "destdir" of tag "javac"; I wanted to detailing the answer in order to give you a resolution as immediate as possible.
Is it possible to combine two jar files such that in an applet tag I can simply do something like
archive="jarjar.jar/jar1.jar"... ...archive="jarjar.jar/jar2.jar"... instead of
archive="jar1.jar"... ...archive="jar2.jar"...
I need to only have one jar file so putting two jar files in a folder will not help me.
Sure, just extract the two jar files and recreate a new one
$ mkdir tmp
$ (cd tmp; unzip -uo ../jar1.jar)
$ (cd tmp; unzip -uo ../jar2.jar)
$ jar -cvf combined.jar -C tmp .
The stuff with tmp ensures that the two existing jars are extracted into a clean directory and then the new one made from that.
Be aware that you may also need to merge any manifest.mf files contained therein, and if there are any also include the '-m' option in that file command.
Use zipgroupfileset with the Ant Zip task
<zip destfile="out.jar">
<zipgroupfileset dir="lib" includes="*.jar"/>
</zip>
Might help you.
If you are using gradle, just add the following to build.gradle. No plugins required. If you need special options, then go with Fatjar plugin, as initialZero suggests.
task superSimpleJar(type: Jar) {
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
For Android project, add this to app/build.gradle and run "gradlew superSimpleJar". Find jar in build/libs/app-all.jar
task superSimpleJar(type: Jar) {
baseName = project.name + '-all'
from {
configurations.compile.findAll {
it.getName() != 'android.jar'
}.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project name="zip-test" default="zip" basedir=".">
<target name="zip">
<zip destfile="out.jar">
<zipgroupfileset dir="." includes="*.jar"/>
</zip>
</target>
</project>
save this code in build.xml file and keep it in same folder where all the jar files to be combined are kept. Open cmd, give path of folder and run command : ant zip.
It will generate out.jar which is combination of all jars.
Just unzip both jar files, then zip the results into one zip file, and rename this to jar again.
But as adarshr said: Better use the jar command for that.
Extract both jars and create a new one works. (Use jar commands shown above).
One caveat about manifest file is that you want to extract the jar whose manifest file you want to retain in the last.
I know it's an old question and I just wanted to add my two cents (no permission to comment yet, so creating a new answer).
I do see the value in sumanth.donthula's answer as the problem for all of us merging jars will be how to deal with the manifest files. In my case I wanted to merge some library files (mainly generated web service client code) into the jar of an application written by me. It was OK to replace the manifests with the one of my own jar.
The simplest way of doing this is taking care of the order in which you unzip the original files (as Alnitak and sumanth.donthula noted).
I wanted to use the zip ant task (thank you, ykombinator, for the idea). It turned out that the only way of controlling the order of compressing/packaging is renaming the files. See my ant target below.
The output directory in my example is called codemodule.dir (I created a FileNet code module). The rest of the names are self-explaining. The important step is renaming the application jar to 0_... to be the 1st in order. This way its manifest will be retained as the duplicate attribute of the zip ant task is set to preserve.
<target name="merge_jars">
<delete dir="${codemodule.dir}" quiet="true" />
<mkdir dir="${codemodule.dir}" />
<copy todir="${codemodule.dir}">
<fileset dir="${lib.dir}" includes="*.jar"/>
<fileset dir="${basedir}" includes="${app-name}.jar"/>
</copy>
<move file="${codemodule.dir}/${app-name}.jar" tofile="${codemodule.dir}/0_${app-name}.jar"/>
<zip destfile="${codemodule.dir}/${app-name}-fat.jar" duplicate="preserve">
<zipgroupfileset dir="${codemodule.dir}">
<include name="*.jar"/>
</zipgroupfileset>
</zip>
We have quite a big tree of source code, parts of it are deployed as two separate jar files. We need an easy control of what goes to which jar.
So far we do this by <exclude name="" /> and <include name="" /> tags, but this is quite inconvenient. The best option would be a separate config file with all the packages listed which we could comment out when needed, say with a '#' character.
Does something similar exist or do we have to write a new ant task that reads such a file and runs a <jar> task?
The best option would bee to seperate the code into different modules which can be build on their own (of course with dependencies to each other). Doing this also makes cyclic dependencies obvious and gives you the chance to optimize your code base.
ANT includes and excludes can be managed with external files and referenced in a fileset using includesfile and excludesfile attributes.
includesfile the name of a file; each
line of this file is taken to be an
include pattern.
excludesfile the
name of a file; each line of this file
is taken to be an exclude pattern.
For example:
<jar destfile="${dist}/lib/app1.jar">
<fileset dir=".">
<includesfile name="app1.properties"/>
</fileset>
</jar>
<jar destfile="${dist}/lib/app2.jar">
<fileset dir=".">
<includesfile name="app2.properties"/>
</fileset>
</jar>