P Y N T
v0.1.0
Pynt is a
scripting langauge designed to simplify running builds. It is similar
in
purpose to Ant (and the first version was similar in feel to Python,
hence the name). You can download it here.
The main benefits of Pynt over Ant are:
- Pynt is a
full scripting language with objects, methods, loops, conditionals and
variables
- You can
create and call methods on any Java object from within Pynt
- Parameterized
targets and muti-file support. Ant relies on subprojects to do these,
but this can cause the same target to be run multiple times. See Recursive Make
Considered Harmful for a much
better explanation of this problem.
- Pynt files
are not written in XML, and
are
much more concise than Ant files
The Pynt language
will most likely change drastically before the 1.0.0 release. Any feedback is
appreciated.
Sample:
Below is a pynt script that compiles pynt and runs its tests:
import files j2sdk antlr junit
method Package(name, depends=[], jars=[])
return this
end
# Define the dependencies between the packages
ast = Package("ast")
antlr = Package("antlr", [ast], ["lib\antlr-2.7.2-lib.jar"])
types = Package("types", [ast])
engine = Package("engine", [ast, types])
all = Package("junit", [ast, antlr, types, engine],
["lib\jdom-b8-lib.jar", "lib\xercesImpl.jar", "lib\jdom-b8-lib.jar",
"lib\junit-3.8.1-lib.jar", "lib\xom-1.0d16-lib.jar",
"lib\antlr-2.7.2-lib.jar"])
target Generate
print "Generating..."
return Antlr(
grammar = "../antlr/pynt.g",
output = "temp/src/org/pynt/antlr"
)
end
target Compile<package>
build Generate
classpath = package.jars
for depend in package.depends
result = build
Compile<depend>
classpath += [result.output]
end
print "Compiling " + package.name + "..."
filter = "org/pynt/" + package.name + "/*.java"
Copy(Scan("src", filter), "temp\" + package.name)
files = Scan("temp/" + package.name, filter) +
Scan("temp/src", filter)
return Javac(source="1.4", targ="1.4",
output="temp\" + package.name, files=files, classpath=package.jars)
end
target Test
result = build Compile<all>
print result.classpath + [result.output]
JUnit (suite = "org.pynt.junit.AllTests", classpath
= result.classpath + [result.output])
end
Intalling
And Running Pynt:
To install
Pynt,
just unzip the downloaded file. You can then run it from the command
line like this:
java
-jar <install dir>/pynt.jar
This will run
Pynt in "interactive mode", meaning you can enter commands at the
prompt
and Pynt will execute them immediately. For example:
pynt> print "Hello World"
Hello World
You can also
have Pynt execute the contents of a file. For example, if we put our
"print" statement in a file called "helloworld.pynt", we could run it
like this:
java
-jar <install dir>/pynt.jar
helloworld.pynt
To execute some
Pynt code directly from the command line, you can use the "-c" option
(you'll need to escape any special characters within the quotes):
java
-jar <install dir>/pynt.jar
-c "print \"Hello World\""
Constants and
Variables:
You can define a
variable in Pynt by assigning a value to a name:
pynt> msg = "Hello World"
pynt> print msg
Hello World
Variables cannot be defined more than once:
pynt> msg = "Foo"
pynt> msg = "Bar"
Variable
"msg" already exists
If you want the
ability to modify the value after it's been defined, you'll need to use
the "set" keyword to override the defintion:
pynt> msg = "Foo"
pynt> set msg = "Bar"
pynt> print msg
Hello World
Numbers:
You can
create a java.math.BigDecimal object using a series of digits.
pynt> print 100
100
Strings:
You can create
a
java.lang.String object by placing text between double qutoes. Within the string,
curly brackets can be used to reference other variables:
pynt> module = "framework"
pynt> print "Compiling
{module}"
Compiling
framework
Files:
Under construction
FileSets:
Under construction
Sets:
You can
use curtly brackets to create a set:
myjars = { "foo.jar",
"bar.jar" }
Lists:
You can
use squate brackets to create a list:
myjars = [ "foo.jar",
"bar.jar" ]
Java
Objects:
Normal Java
objects can be loaded and instantiated in Pynt. The class needs to be
loaded first, then you can create an instance of the class using the
"new" method:
pynt> load java.util.Date
pynt> print Date.new()
Mon Apr 21 13:36:27 GMT 2003
Methods can be
invoked the way you'd expect:
pynt> load java.util.Date
pynt> load java.text.DateFormat
pynt> print
DateFormat.getDateInstance().format(Date.new())
Apr 30, 2003
All classes
(including those in java.lang) must be explicitly imported before use.
Unlike Java, you cannot use fully qualified names.
The "from" statement can be used to load classes from jars and
directories that aren't part of the system classpath (classes that
you've just compiled, for example). The "from" statement creates a new
classloader, and all "import" statements within the "from" block will
use that classloader. For example:
pynt> import com.acme.tools.MyTool
from "mytool.jar"
pynt> MyTool.new().run()
Pynt Objects:
You cannot
define classes in Pynt. However, you can use the "this" keyword to
return an object representing the current stack frame. For example:
method Person(name)
return this
end
pynt> person = Person("Horatio McAllister")
pynt> print person.name
Horatio
McAllister
This
can be particular handy when chaining together commands, where the
output of each command is the input to the next command. For example,
assuming our Javac and Jar functions end with a "return this", we can
use the output of the Javac as an input to the Jar:
javac = Javac(output="build", ...)
jar = Jar(input=javac.output, ...)
Methods:
You can defined methods on Pynt objects by using the "method" keyword.
method PrintTwice(name)
print name
print name
end
PrintTwice("Hello
World")
Hello World
Hello World
Targets and
Builds:
Targets are
similar to methods. However, they cannot be invoked directly. What
looks
like an invocation actually creates a reference to the target with the
specified arguments. These references must be passed into a "build"
statement to run the targets:
target Compile<dir>
print
"Compiling " + dir
...
end
build
Compile<"src">
The brackets on a target with no arguments are optional (unlike the
parens on a method, which are madatory):
target Compile
print
"Compiling"
....
end
build comple
Targets
can also build other targets. This is basically how you represent
dependencies between targets:
target Compile
...
end
target Test
build Compile
...
end
The key difference between methods and targets is that within a build
(the first call to a "build" statement starts a build, and the build
ends when it returns), a target will never be invoked more than once
with the same parameters. For example:
target Compile<module>
print module
end
target All
build
compile<"foo">
build
compile<"bar">
build
compile<"foo">
end
pynt> build All
foo
bar
Note that the
"compile" target only gets built once with the "foo" argument, even
though it's called twice from the "all" target.
Projects:
Pynt code
can be spread across multiple files, and each file is called a
"project". You can reference other projects by using the "load"
command.
For example, let's say we had a file called "printer.pynt":
method PrintTwice(name)
print name
print name
end
We can reference the method in this file as follows:
pynt> import printer
pynt> PrintTwice("Hello World")
Hello World
Hello
World
This works for methods, targets, constants, and variables. You can basically think of the load
statement as creating a reference to the other project's "root" object.
Pynt will only load each project once, so if multiple projects import a
common project, they will all get references to the same root object.
By default Pynt looks for project files in the current directory and in
the directory where Pynt is installed. This can be overridden by using
the "-p" command line option however.