Commit a8e0a26b authored by Lubomir Bulej's avatar Lubomir Bulej

Sanitized the build system before merging the new DiSL agent:

- The build.xml file has been cleaned up extensively.
- The build system for examples has been simplified and cleaned up.
- There is support for binary DiSL releases with examples and documentation.
- Examples should be used with a binary DiSL distribution (see output/dist).
- Moved the disl.py launcher out of the examples subdirectory.
- Test suite Runner has been modified to accept locations and native library names from outside.
parent 53650b5c
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src"/> <classpathentry kind="src" output="output/build/disl" path="src"/>
<classpathentry kind="src" path="src-re-disp"/> <classpathentry kind="src" output="output/build/shvm" path="src-re-server"/>
<classpathentry kind="src" path="src-re-server"/> <classpathentry kind="src" output="output/build/util" path="src-util"/>
<classpathentry kind="src" path="src-agent-java"/> <classpathentry kind="src" output="output/build/test" path="src-test"/>
<classpathentry kind="src" path="src-test"/> <classpathentry kind="src" output="output/build/shvm-dispatch" path="src-re-disp"/>
<classpathentry kind="src" path="src-util"/> <classpathentry kind="src" output="output/build/disl-agent" path="src-agent-java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="build/eclipse-dynamicbypass.jar"/> <classpathentry kind="lib" path="output/eclipse-dynamicbypass.jar"/>
<classpathentry kind="lib" path="lib/asm-debug-all-4.1.jar"/> <classpathentry kind="lib" path="lib/asm-debug-all-4.1.jar"/>
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/> <classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
<classpathentry kind="lib" path="lib/junit-4.11.jar"/> <classpathentry kind="lib" path="lib/junit-4.11.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="output/build/disl"/>
</classpath> </classpath>
================================================================================ ================================================================================
= DiSL = DiSL
================================================================================ ================================================================================
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
...@@ -205,7 +206,7 @@ ...@@ -205,7 +206,7 @@
================================================================================ ================================================================================
= LIBRARIES = LIBRARIES
================================================================================ ================================================================================
Licenses for libraries used in the project are in the lib directory.
Licenses for the libraries used in the project are in the lib directory.
DiSL DiSL
==== ====
DiSL is a Java bytecode instrumentation framework intended for observation DiSL is a Java bytecode instrumentation framework intended for observation of
of programs executing in the Java Virtual Machine. It has been mainly used programs executing in the Java Virtual Machine. It has been mainly used for
for development of dynamic program analysis instrumentations, but it can be development of dynamic program analysis instrumentations, but it can be used
used equally well to develop instrumentations for, e.g. runtime performance equally well to develop instrumentations for, e.g. runtime performance
monitoring, or other tasks not bent on altering program execution. monitoring, or other tasks not bent on altering program execution.
DiSL is inspired by AOP, but in contrast to mainstream AOP languages, it DiSL is inspired by AOP, but in contrast to mainstream AOP languages, it
features an open join point model where any region of bytecodes can serve as features an open join point model where any region of bytecodes can serve as a
a join point (i.e., code location to be instrumented). DiSL also reconciles join point (i.e., code location to be instrumented). DiSL also reconciles
high-level language concepts, such as the pointcut/advice programming model high-level language concepts, such as the pointcut/advice programming model
found in AOP, with high expressiveness, and efficiency of bytecode found in AOP, with high expressiveness, and efficiency of bytecode
manipulation performed using low-level libraries such as ASM. As a result, manipulation performed using low-level libraries such as ASM. As a result,
...@@ -45,23 +45,29 @@ javahome/var.local.tmp file to javahome/var.local and modify it to set the ...@@ -45,23 +45,29 @@ javahome/var.local.tmp file to javahome/var.local and modify it to set the
JAVA_HOME variable to point to the root of the JDK installation you want to JAVA_HOME variable to point to the root of the JDK installation you want to
use. use.
Finally, to compile DiSL, run the "ant" command in the root directory. Finally, to compile DiSL, run the "ant" command in the top-level directory.
You can create javadoc documentation by running "ant javadoc".
DOCUMENTATION =============
EXAMPLES ======== Please look at http://disl.projects.ow2.org/xwiki/bin/view/Main/Doc.
For the basic instrumentation example, please look in the example directory. After building DiSL, you will find the API documentation and an introduction
Also the src-test directory contains simple examples of DiSL features. to instrumenting applications with DiSL in the output/dist/doc directory.
DOCUMENTATION ============= EXAMPLES ========
Please look at http://disl.projects.ow2.org/xwiki/bin/view/Main/Doc. For a set of simple examples showcasing basic DiSL features, please look in
the "examples" directory. Note that the "examples" are meant to be used with
DiSL a distribution directory layout, which will be created in the
"output/dist" directory after buildig DiSL.
In addition, the "src-test" directory contains additional examples used for
testing other DiSL features -- these can be run using Ant.
USER ERRORS =========== USER ERRORS ===========
If you get a Java error during instrumentation or running your application, If you get a Java error during instrumentation or running your application,
please look at USERERRORS document describing most common problems. please look at the USER_ERRORS document describing most common problems.
...@@ -6,16 +6,18 @@ located in the ch.usi.dag.disl.test.junit package and complex tests in the ...@@ -6,16 +6,18 @@ located in the ch.usi.dag.disl.test.junit package and complex tests in the
ch.usi.dag.disl.test.suite package that invoke all parts of the framework ch.usi.dag.disl.test.suite package that invoke all parts of the framework
(client, server, ...) and verify computed results. (client, server, ...) and verify computed results.
All tests are performed calling All tests are executed by running
- ant test $ ant test
To use the for debuging purpose during development a single suite test (for To use the tests for debuging purposes during development, a single test suite
example "after" test) can be invoked calling (for example, "after") can be executed by running
- ant suite-test -Dtest.name=after $ ant test -Dtest.name=after
Parameters specified to ant at command line starting with "-Ddisl." are passed Properties passed to Ant on the command line starting with "-Ddisl.",
to the test. For example to show tests output specify "-Ddislserver.", and "-Ddislreserver." are passed to the test. For example,
- -Ddisl.test.verbose=true to save the outputs produced by the test suites, use
-Ddisl.test.verbose=true
When built with "ant prepare-test" tests can be also run directly. They are When built with "ant prepare-test" tests can be also run directly. They are
packed in the "build-test" directory. packed in the "build-test" directory.
...@@ -28,12 +30,12 @@ Implementation is a bit tricky in few aspects. ...@@ -28,12 +30,12 @@ Implementation is a bit tricky in few aspects.
Firstly, building of suite test apps and instrumentations is handled by a Firstly, building of suite test apps and instrumentations is handled by a
scripted target that lists all directories in a "ch.usi.dag.disl.test.suite" scripted target that lists all directories in a "ch.usi.dag.disl.test.suite"
package and one by one "app" and "instr" package are jared. package, and produces "app" and "inst" jars for each suite.
Secondly, when running the instances of client and server using Process API Secondly, when running the instances of client and server using Process API,
never forget to clear environment variables as inherited classpath could cause never forget to clear environment variables as inherited classpath could cause
serious troubles. serious trouble.
If a suite instr test contains a manifest file it's used and if none is present
"DiSLClass" is default for "DiSL-Classes" attribute.
If a suite instr source directory contains a manifest file, it will be used
when packaging the instrumentation into a jar, otherwise a default manifest
will be used, with the "DiSL-Classes" attribute set to "DiSLClass".
...@@ -12,7 +12,7 @@ from subprocess import * ...@@ -12,7 +12,7 @@ from subprocess import *
# CONSTANTS # CONSTANTS
###################################################################### ######################################################################
# default disl_home value, relative to the script # default disl_home value, relative to the script
DEFAULT_DISL_HOME = "../../" DEFAULT_DISL_HOME = os.path.relpath ("%s/.." % os.path.dirname(__file__))
# string to be substituted by the actual value of DISL_HOME in paths # string to be substituted by the actual value of DISL_HOME in paths
VARIABLE_DISL_HOME = "${DISL_HOME}" VARIABLE_DISL_HOME = "${DISL_HOME}"
...@@ -34,7 +34,7 @@ def disl_home(): ...@@ -34,7 +34,7 @@ def disl_home():
# LIB_SUFFIX # LIB_SUFFIX
###################################################################### ######################################################################
def lib_suffix(): def lib_suffix():
if platform.system() == "Darwin": if platform.system() == "Darwin":
return ".jnilib" return ".jnilib"
else: else:
return ".so" return ".so"
...@@ -44,11 +44,11 @@ def lib_suffix(): ...@@ -44,11 +44,11 @@ def lib_suffix():
###################################################################### ######################################################################
def general_parser(parser): def general_parser(parser):
# positional variant of i for simplicity # positional variant of i for simplicity
# both cannot be set at once # both cannot be set at once
parser.add_argument("instr", parser.add_argument("inst",
default=None, default=None,
nargs="?", nargs="?",
help="path to jar containing disl instrumentation code, same as '-i'") help="path to jar containing DiSL instrumentation code, same as '-i'")
# positional variant of c_app for simplicity # positional variant of c_app for simplicity
# both cannot be set at once # both cannot be set at once
...@@ -65,18 +65,18 @@ def general_parser(parser): ...@@ -65,18 +65,18 @@ def general_parser(parser):
parser.add_argument("-cs", parser.add_argument("-cs",
action="store_true", action="store_true",
default=True, default=True,
help="set to start client and server") help="start client and DiSL server")
parser.add_argument("-cse", parser.add_argument("-cse",
action="store_true", action="store_true",
default=False, default=False,
help="set to start client, server and remote evaluation") help="start client, DiSL server and Shadow VM server")
parser.add_argument("-i", parser.add_argument("-i",
dest="instrumentation", dest="instrumentation",
default=None, default=None,
metavar="PATH", metavar="PATH",
help="path to jar containing disl instrumentation code, same as 'instr'") help="path to jar containing DiSL instrumentation code, same as 'inst'")
parser.add_argument("-t", parser.add_argument("-t",
dest="test_dir", dest="test_dir",
...@@ -140,14 +140,14 @@ def server_parser(parser): ...@@ -140,14 +140,14 @@ def server_parser(parser):
default=[], default=[],
metavar="A", metavar="A",
nargs="+", nargs="+",
help="java options of the server") help="options for the DiSL server JVM")
group.add_argument("-s_args", group.add_argument("-s_args",
action="append", action="append",
default=[], default=[],
metavar="A", metavar="A",
nargs="+", nargs="+",
help="arguments to the server application") help="DiSL server arguments")
group.add_argument("-s_out", group.add_argument("-s_out",
default=None, default=None,
...@@ -194,7 +194,7 @@ def server_parser(parser): ...@@ -194,7 +194,7 @@ def server_parser(parser):
help="listening network port") help="listening network port")
return return
###################################################################### ######################################################################
# EVALUATION_PARSER # EVALUATION_PARSER
...@@ -207,14 +207,14 @@ def evaluation_parser(parser): ...@@ -207,14 +207,14 @@ def evaluation_parser(parser):
default=[], default=[],
nargs="+", nargs="+",
metavar="A", metavar="A",
help="java options of the evaluation server") help="options for the Shadow VM server JVM")
group.add_argument("-e_args", group.add_argument("-e_args",
action="append", action="append",
default=[], default=[],
nargs="+", nargs="+",
metavar="A", metavar="A",
help="arguments to the evaluation server application") help="Shadow VM server arguments")
group.add_argument("-e_out", group.add_argument("-e_out",
default=None, default=None,
...@@ -251,11 +251,12 @@ def documentation_parser(parser): ...@@ -251,11 +251,12 @@ def documentation_parser(parser):
parser.formatter_class=argparse.RawTextHelpFormatter parser.formatter_class=argparse.RawTextHelpFormatter
parser.description = """ parser.description = """
This script is a DiSL client application, server and evaluation server starter. This is a DiSL application, DiSL instrumentation server, and Shadow VM server launcher.
By default a client application that will be instrumented and the server that By default, the script starts a DiSL instrumentation server VM and a client application
will instrument the application will be started. To start remote evaluation VM that will be instrumented using DiSL. For instrumentations using remote analysis
server too. Specify '-cse' option. execution in Shadow VM, the Shadow VM server needs to be started as well, which needs
to be specified explicitly using the '-cse' option.
To pass option like arguments (starting with dash) one must either use equal To pass option like arguments (starting with dash) one must either use equal
sign or positional variant of the argument if it's present. For example sign or positional variant of the argument if it's present. For example
...@@ -272,9 +273,9 @@ variable. ...@@ -272,9 +273,9 @@ variable.
EXAMPLES: EXAMPLES:
To execute the example application run following: To execute the example application run following:
./disl.py -- instr/build/disl-instr.jar -jar app/build/example-app.jar ./disl.py -- build/example-inst.jar -jar build/example-app.jar
or or
./disl.py -cs -i=instr/build/disl-instr.jar -c_app=-jar c_app=app/build/example-app.jar ./disl.py -cs -i=build/example-inst.jar -c_app="-jar build/example-app.jar"
""" """
###################################################################### ######################################################################
...@@ -284,8 +285,8 @@ def make_parser(): ...@@ -284,8 +285,8 @@ def make_parser():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
general_parser(parser) general_parser(parser)
client_parser(parser) client_parser(parser)
server_parser(parser) server_parser(parser)
evaluation_parser(parser) evaluation_parser(parser)
documentation_parser(parser) documentation_parser(parser)
return parser return parser
...@@ -346,15 +347,15 @@ def parse_arguments(parser): ...@@ -346,15 +347,15 @@ def parse_arguments(parser):
if args.e_port is not None: if args.e_port is not None:
args.e_opts+= ["-Ddislreserver.port="+args.e_port] args.e_opts+= ["-Ddislreserver.port="+args.e_port]
# supply instrumentation from positional instr if set # supply instrumentation from positional 'inst' if set
if args.instrumentation is not None and args.instr is not None: if args.instrumentation is not None and args.inst is not None:
parser.error("-i and instr set both") parser.error("-i and -inst set both")
if args.instr is not None: if args.inst is not None:
args.instrumentation = args.instr args.instrumentation = args.inst
# supply c_app from positional app if set # supply c_app from positional app if set
if args.c_app is not None and args.app is not None: if args.c_app is not None and args.app is not None:
parser.error("-c_app and app set both") parser.error("-c_app and -app set both")
if args.app is not None: if args.app is not None:
args.c_app = args.app args.c_app = args.app
...@@ -398,7 +399,7 @@ def run(cmd, out, err): ...@@ -398,7 +399,7 @@ def run(cmd, out, err):
if err is not None: if err is not None:
err_f = open(err, "w") err_f = open(err, "w")
process = Popen(cmd, stdout=out_f, stderr=err_f, shell=False) process = Popen(cmd, stdout=out_f, stderr=err_f, shell=False)
return process return process
...@@ -407,12 +408,12 @@ def run(cmd, out, err): ...@@ -407,12 +408,12 @@ def run(cmd, out, err):
###################################################################### ######################################################################
def run_server(args, parser): def run_server(args, parser):
if args.instrumentation is None: if args.instrumentation is None:
parser.error("argument instr (-i) is required to run the client") parser.error("argument instr (-i) is required to run the client")
try_kill(".server.pid") try_kill(".server.pid")
s_jar = args.disl_home+"/build/disl-server.jar" s_jar = args.disl_home+"/lib/disl-server.jar"
s_class = "ch.usi.dag.dislserver.DiSLServer" s_class = "ch.usi.dag.dislserver.DiSLServer"
s_cmd = ["java"] s_cmd = ["java"]
s_cmd+= args.s_opts s_cmd+= args.s_opts
...@@ -435,12 +436,12 @@ def run_server(args, parser): ...@@ -435,12 +436,12 @@ def run_server(args, parser):
###################################################################### ######################################################################
def run_evaluation(args, parser): def run_evaluation(args, parser):
if args.instrumentation is None: if args.instrumentation is None:
parser.error("argument instr (-i) is required to run the client") parser.error("argument instr (-i) is required to run the client")
try_kill(".evaluation.pid") try_kill(".evaluation.pid")
e_jar = args.disl_home+"/build/dislre-server.jar" e_jar = args.disl_home+"/lib/dislre-server.jar"
e_class = "ch.usi.dag.dislreserver.DiSLREServer" e_class = "ch.usi.dag.dislreserver.DiSLREServer"
e_cmd = ["java"] e_cmd = ["java"]
e_cmd+= args.s_opts e_cmd+= args.s_opts
...@@ -464,15 +465,15 @@ def run_evaluation(args, parser): ...@@ -464,15 +465,15 @@ def run_evaluation(args, parser):
###################################################################### ######################################################################
def run_client(args, parser): def run_client(args, parser):
if args.c_app is None: if args.c_app is None:
parser.error("argument app (-c_app) is required to run the client") parser.error("argument app (-c_app) is required to run the client")
if args.instrumentation is None: if args.instrumentation is None:
parser.error("argument instr (-i) is required to run the client") parser.error("argument instr (-i) is required to run the client")
cagent = args.disl_home+"/build/libdislagent"+lib_suffix() cagent = args.disl_home+"/lib/libdislagent"+lib_suffix()
eagent = args.disl_home+"/build/libdislreagent"+lib_suffix() eagent = args.disl_home+"/lib/libdislreagent"+lib_suffix()
jagent = args.disl_home+"/build/disl-agent.jar" jagent = args.disl_home+"/lib/disl-agent.jar"
dispatch = args.disl_home+"/build/dislre-dispatch.jar" dispatch = args.disl_home+"/lib/dislre-dispatch.jar"
c_cmd = ["java"] c_cmd = ["java"]
c_cmd+= args.c_opts c_cmd+= args.c_opts
...@@ -492,7 +493,7 @@ def run_client(args, parser): ...@@ -492,7 +493,7 @@ def run_client(args, parser):
#print c_cmd #print c_cmd
client = run(c_cmd, args.c_out, args.c_err) client = run(c_cmd, args.c_out, args.c_err)
client.wait() client.wait()
# let server and evaluation finish # let server and evaluation finish
...@@ -532,6 +533,7 @@ def main(): ...@@ -532,6 +533,7 @@ def main():
###################################################################### ######################################################################
# ENTRY_POINT # ENTRY_POINT
###################################################################### ######################################################################
if __name__ == "__main__": if __name__ == "__main__":
main() main()
lib.dir=lib
asm.lib=asm-debug-all-4.1.jar asm.lib=asm-debug-all-4.1.jar
asm.path=lib/${asm.lib} asm.path=${lib.dir}/${asm.lib}
junit.core.lib=junit-4.11.jar junit.core.lib=junit-4.11.jar
junit.core.path=lib/${junit.core.lib} junit.core.path=${lib.dir}/${junit.core.lib}
junit.hamcrest.lib=hamcrest-core-1.3.jar junit.hamcrest.lib=hamcrest-core-1.3.jar
junit.hamcrest.path=lib/${junit.hamcrest.lib} junit.hamcrest.path=${lib.dir}/${junit.hamcrest.lib}
ant-contrib.lib=ant-contrib-0.6.jar ant-contrib.lib=ant-contrib-0.6.jar
ant-contrib.resource=net/sf/antcontrib/antlib.xml ant-contrib.resource=net/sf/antcontrib/antlib.xml
ant-contrib.path=lib/${ant-contrib.lib} ant-contrib.path=${lib.dir}/${ant-contrib.lib}
src.bin=bin
src.disl=src src.disl=src
src.dynbypass=src-dynbypass src.disl.bypass=src-dynbypass
src.dynbypass.act=src-dynbypass-act src.disl.bypass.dynamic=src-dynbypass-act
src.agent.java=src-agent-java src.disl.agent=src-agent-c
src.agent.c=src-agent-c src.disl.agent.java=src-agent-java
src.reserver=src-re-server src.shvm=src-re-server
src.redispatch=src-re-disp src.shvm.dispatch=src-re-disp
src.reagent=src-re-agent src.shvm.agent=src-re-agent
src.test=src-test src.test=src-test
src.util=src-util src.util=src-util
src.doc=doc
src.doc.jdoc=${src.doc}/javadoc
src.doc.intro=${src.doc}/intro
src.examples=examples
out.dir=output
bin=bin build=${out.dir}/build
build=build build.util=${build}/util
build.thread=build-thread build.disl=${build}/disl
build.afterbootstrap=build-abs build.disl.thread=${build.disl}-thread
build.test=build-test build.disl.bypass=${build.disl}-bypass
dist=dist build.disl.agent.java=${build.disl}-agent
test=test build.shvm=${build}/shvm
build.shvm.dispatch=${build.shvm}-dispatch
build.test=${build}/test
build.test.jars=${build.test}-jars
build.doc=${build}/doc
build.doc.intro=${build.doc}-intro
extendedthread.path=${build}/extendedthread.jar dist=${out.dir}/dist
dist.bin=${dist}/bin
dist.lib=${dist}/lib
dist.doc=${dist}/doc
dist.doc.jdoc=${dist.doc}/javadoc
dist.doc.intro=${dist.doc}/intro
dist.examples=${dist}/examples
instr.jar.name=disl-instr.jar release=${out.dir}/release
release.bin.zip=${release}/disl-${disl.version}-bin.zip
release.bin.tar=${release}/disl-${disl.version}-bin.tar.bz2
release.src.zip=${release}/disl-${disl.version}-src.zip
release.src.tar=${release}/disl-${disl.version}-src.tar.bz2
This diff is collapsed.
lib.path=../../../build
dislserver.path=${lib.path}/disl-server.jar
dislre.server.path=${lib.path}/dislre-server.jar
dislre.dispatch.path=${lib.path}/dislre-dispatch.jar
asm.lib=asm-debug-all-4.0.jar
asm.path=${lib.path}/${asm.lib}
instr.jar.name=disl-instr.jar
<project name="example-app" default="package" basedir=".">
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
</javac>
</target>
<target name="package" depends="compile">
<mkdir dir="build" />
<jar basedir="bin" destfile="build/example-app.jar">
<manifest>
<attribute name="Main-Class" value="Main" />
</manifest>
</jar>
</target>
<target name="clean">
<delete dir="bin" />
<delete dir="build" />
</target>
</project>
<project name="disl-example" default="prepare-all" basedir=".">
<target name="prepare-all">
<ant antfile="app/build.xml" target="package" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="package" useNativeBasedir="true" />
</target>
<target name="clean">
<ant antfile="app/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="clean" useNativeBasedir="true" />
</target>
<target name="run" depends="prepare-all">
<exec executable="../disl.py">
<arg value="-cse" />
<!-- <arg value="-c_out=client.out" /> -->
<!-- <arg value="-e_out=evaluation.out" /> -->
<arg value="--" />
<arg value="instr/build/disl-instr.jar" />
<arg value="-jar" />
<arg value="app/build/example-app.jar" />
</exec>
</target>
</project>
<project name="example-disl" default="package" basedir=".">
<property file="../../build.properties" />
<path id="buildpath">
<pathelement location="${asm.path}" />
<pathelement location="${dislserver.path}" />
<pathelement location="${dislre.server.path}" />
<pathelement location="${dislre.dispatch.path}" />
</path>
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
<classpath refid="buildpath" />
</javac>
</target>
<target name="package" depends="compile" description="create instrumentation package">
<mkdir dir="build"/>
<jar jarfile="build/${instr.jar.name}"
basedir="bin"
excludes="MANIFEST.MF"
manifest="src/MANIFEST.MF">
</jar>
</target>
<target name="clean">
<delete dir="bin" />
<delete dir="build" />
</target>
</project>
<project name="example-app" default="package" basedir=".">
<target name="compile">
<mkdir dir="bin" />
<javac srcdir="src" destdir="bin" debug="true" includeAntRuntime="false">
</javac>
</target>
<target name="package" depends="compile">
<mkdir dir="build" />