*Important Note: Currently Jan 31st 2013 ybuilder is in reworking and has some serious bugs which make this version unusable. It is here just for testing. This should be fixed in a few days
Please use in the mean-time olderversions*
Ybuilder is a yeti yeti wrapper around ant combined with the dependency management of maven.
Custom buildscripts can be defined from ground up like with ant but written in yeti instead of ant-xml.There are built-in commands for common tasks like downloading dependencies, compiling the project, lauching the REPL with the right classpath etc.
On top of the ant-wrapper there is standard and cutomizable build-script
(module ybuilder.core.base
) with predefined
livecycle-targets (clean, compile, test etc), directories,
and maven based dependency- and classpath-management.
Ybuilder's functinonality is contained in a single all-in one jar. You can either download that directly and copy it in each project or use a special launcher jar.
For both alterantives Java7 is needed, preferable on the path.
Everything needed to run ybuilder is in the executable jar
ybuilder-lib-0.6.jar
.
Just download it from:
http://chrisichris.github.com/chrisis-maven-repo/ybuilder/lib/ybuilder-lib-0.6.jar
copy it to your project directory and rename it to ybuilder.jar
Now you can launch it with:
>java -jar ybuilder.jar
The advantage of this approache is that you will always use the same version of ybuilder for this single project, which makes the build more stable.
The disadvantage is that the ybuilder-lib-0.6.jar
has a size of about 4MB and
storing it with each project maybe too space consuming in your repo.
With the second alternative a launcher is used. The launcher is also an
executable jar called ybuilder.jar
but it is only about 4KB big.
The first time it is executed, the launcher downloads the
above ybuilder-lib-0.6.jar
to the userhome/.ybuilder/lib
directory and
than delegates every call to this jar.
For each project of the same user the launcher uses the same
ybuilder-lib-0.6.jar
and version.
You can download the launcher from this link:
http://chrisichris.github.com/chrisis-maven-repo/ybuilder/ybuilder.jar
Copy the launcher jar again to the root-directory of your project.
It can be run exactly like the lib jar with
>java -jar ybuilder.jar
The advantage of the launcher is that it is much smaller than the lib if it is stored with your project and that one update updates all of the launchers.
Because ybuilder is still in development you should quite regularly update.
For the direct usage just replace the latest version of the jar with a new one. The latest version is always found at the download link above.
To update the lib used with the launcher run the launcher with
>java -jar ybuilder.jar -update
Or if you want to update from a custom-url use
>java -jar ybuilder.jar -update http://custom_url_with_ybuilder/lib
This will redownload the ybuilder-lib-0.6.jar
to the ~/.ybuilder/lib
directory.
http://groups.google.com/group/yeti-lang
Every ybuilder project is - like an ant-project - made up of one or more targets which can depend on each other. Each target represent a unit of work ie. init, compile etc. which is executed only once during a build.
You run a ybuilder project using the ybuilder.jar
, which executes
the project.yeti
(the build-script). The build-script insantiates and
configures the targets of the project.
To try that out create the following project.yeti
file:
load ybuilder.core.build;
config = createBuildConfig();
_ = target config "hello" "world" [] do ap:
println "Hello World";
done;
run config;
In a command-line shell execute the build script:
>java -jar ybuilder.jar hello:world
Hello World!
What's going on here?
When the ybuilder.jar
is executed it the yeti.jar, ant.jar and ant-maven.jar
to the classpath and executes build-script project.yeti
.
The build-script first creates a config
structure. This
config structure is used to register the target using the target
function.
The target function is than used create the hello:world
target and register
it in the config structure. The hello:world
target prints "Hello World!"
when it is excuted.
The config
structure now contains the target. As a last step the
the config
structure is given to the run
function.
The run
function reads from the command-line-args the name of the target
to execute (hello:world
), looks up the target by name in the config
and finally executes it.
That's it.
All project.yeti
files are normal yeti programms. You can use all the
language constructs to construct them.
As seen the target function is used to create and register targets with config.
The target function takes as arguments:
- config: this is the config structure it registers the target with
- groupname: the groupname of the target
- name: the name of the target in the group
- a list of options:
- Depends target: A target which gets executed before this target
- Description string: A description of the target
- DependencyOf otherTarget: add this target to the depedency of the other target
- ShortName(): register this target also under its name only. So it can
be executed whithout the group prefix (ie
compile
instead oflivecyle:compile
)
- the target's function: This is executed when the target is run.
The target function in turn gets a
project
structure which contains commandline args, properties the targets already executed and an AntProject used for executing ant-tasks.
The target function retuns a target struct which can be used to reference the target.
To try it out:
To try that out create the following project.yeti
file:
load ybuilder.core.build;
config = createBuildConfig();
taskOne = target config "hello" "one"
[Description "Prints hello"] do ap:
println "Hello World!";
done;
taskTwo = target config "hello" "two"
[Depends one,
Description "greets target",
ShortName()] do p:
println "Hello Task";
done;
run config;
In a command-line shell execute the build script:
>java -jar ybuilder.jar two
Hello World!
Hello Task!
A target which is run when no target is named on the command line can be set on config
config.defaultTarget = config.livecycleTargets.compile;
If no default target is set. Help
is executed.
Ybuilder provides integration for Ant tasks.
The module yubilder.core.build
contains the function antTask
which is used
to define and execute an ant task very similar to what you would do in
xml.
Ybuilder tries to be as close to the xml-definitions of ant-tasks as possible, so that the noumerous ant-examples can be entered more or less one to one, with the same names and value of elements, attributes etc.
Elements are represented by the el elementname attributes sublements
function. Where elementname is a string, attributes is as hash<string,string> and
subelements is a list of other elements.
To be as close as possible to ant-xml Ybuilder does not instantiate/call the various task java-classes directly, but rather drives the Ant-SAXHandler to generate the tasks.
The antTask function is of the form:
antTask taskname attributes subelements project
where
- name: the xml-element name of the task
- attributes: a hash<string,string> for the xml-attributes for the task (names and values are exactly like in xml)
- a list of which is represent the xml-content of the task.
- an xmlelement is either a Text string | Element {name is string, attributes is map<string,string>, subelements is list structure. You use the el function to construct elements
- the project-structure: which is given to the target-function
For example the following ant-xml task definition:
<javac srcdir="src"
destdir="target/classes"
includes="**/*.jar"
fork="true">
<classpath>
<pathelement location="classes"/>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
</javac>
is expressed in yeti-code like this:
antTask "javac"
["srcdir" : "src",
"destdir": "target/classes",
"includes":"**/*.java",
"fork": "true"]
[el "classpath" [:]
[el "pathelement" ["location":"classes"],
el "fileset" ["dir":"lib"]
[el "include" ["name":"**/*.jar"][]]]]
project;
The task is immidiately executed when the function is called.
The project
struct (last argument) is either gotten from
the argument to the target function:
target config "group" "name" [] do project:
antTask "mkdir" ["dir":"temp"] [] project;
done;
or it can be created directly in the config-script, in which case the ant-task is executed immidiately when the config-script is evaluated.
antTask "mkdir" ["dir":"temp"] [] (createProject());
With the ybuilder.jar you basically execute targets, where targets are
sperated by ,
java -jar ybuilder.jar target-name [, target-name]*
i. e. to clean and compile your project type on the command line
java -jar ybuilder.jar clean, compile
You can also specify arguments for a target. The arguments come right after the targetname.
java -jar ybuilder.jar new myProjectName chrisichris/basic
Here new
is the target name and myProjectName
and chrisichris/basic
are
the arguments.
You can exclude certain targets form the exectuion with -x
java -jar ybuilder.jar compile -x "livecycle:init"
To get a list of targets use help
java -jar ybuilder.jar help
So far everything shown was a general purpose ant-wrapper, which does not build anything out of the box.
However because most yeti/java projects are quite similar Ybuilder has a
yeti-module (ybuilder.core.base
) which configures a standard yeti project.
It creates a standard directory-layout, registers targets for
maven-dependency-management, for compiling both java and yeti classes, for
testing, running and packaging.
It is important to note that the ybuilder.core.base
module does nothing
special it just uses the ybuilder.core.build
module exactly like
described in the previous section and creates this way a standard-project.
The easiest way to create a new yubilder project is to clone a template porject and costomize the project.yeti file (https://github.com/chrisichris/basic.ybtr) .
- clone the basic.ybtr project from github
git clone git://github.com/chrisichris/basic.ybtr.git
edit
project.yeti
file to set the projects name, artifact id etc and add dependenciesconfig = baseConfig "your-group-id", "your-artifact-id" "verion"; config.description := "First ybuilder based project";
config = createBaseConfig config [ dependency "org.yeti" "yeti" "0.9.3" [], dependency "org.apache.commons" "commons-lang3" "3.0.1" [], dependency "junit" "junit" "3.8.2" [TestScope()], dependency "javax.servlet" "servlet-api" "2.4" [ProvidedScope()], ];
run
java -jar ybuilder.jar targets
to get help and see that project.yeti compilesadd a source file to the src/ directory
file: src/foo.yeti: application foo.yeti;
println "Hello world!"; 0;
run it from source
java -jar ybuilder.jar runyeti foo
- compile it
java -jar ybuilder.jar compile
- run it the class
java -jar ybuilder.jar run foo
jar it
java -jar ybuilder.jar clean, jar
find the result in target/first-test.jar
When creating a new project ybuilder
initializes the following directory
structure
ybuilder.jar
<the ybuilder.jar file for the project>
projetct.yeti
<the project build configuration - (actually a yeti application)>
.gitignore
<default .gitignore - can be removed if git is not used>
src/
<all source files (ie .yeti .java) and resources go here>
test/
<all test source files and resource go here>
lib/
unmanaged/
<put custom .jar files which are not retrieved from maven repos in
here>
managed/
<.jar files loaded via dependencies are copied in here
this directory is managed by `ybuilder` do not modifie it>
ybuilder/
<dependenceise for the build-process ybuilder itself>
extlib/
<additional .jar files for the buildprocess provided directly>
extlib_managed/
<.jar files retrieved as defined in buildDependencies.yeti>
target/
<the build directory, everything in there gets cleanded on the clean
target>
If you do not want to put your resources in the src/
dir then you can add
resources/
<resources which get copied to the compile result>
test-resources/
<test resources>
project.yeti
is a normal yeti programm. In this yeti program first a config
struct is created and customized, than targets are registered based on this
config and finally the targets are run from the config.
Therefore to define the name etc first create a baseConfig and assign name etc to the vars:
config = baseConfig ();
config.name := "yebspec";
config.groupId :="org.yeb";
config.artifactId := "yebspec";
config.version := "0.1-alpha";
config.description := "Specification tests for yeti";
ybuilder
uses maven ant-tasks
for dependency resolution. It also uses the same build scopes and
classpathes as maven (compile, provided, test, runtime).
Dependencies together with the classpathes are also added to the config struct. To do that convienently there is a special helper function :
config = config with createBaseConfig config [
dependency "org.yeti" "yeti" "0.9.3" [],
dependency "org.apache.commons" "commons-lang3" "3.0.1" [],
dependency "junit" "junit" "3.8.2" [TestScope()],
dependency "javax.servlet" "servlet-api" "2.4" [ProvidedScope()]];
The scopes of the dependency is defined in the list of optional arguments. The
scopes are the same as in maven (CompileScope, TestScope, RuntimeScope,
SystemScope, ProvidedScope). The default scope is CompileScope
.
To retrieve the dependencies you must execute the retrieveDependencies target. Just adding the dependencies has no effect.
>java -jar ybuilder.jar retrieveDependencies
Ybuilder does not retrieve dependencies on each compile because this would take too long.
To add additional remote repositories add to the list of dependencies
remoteRepository id repositoryURL
ie
config = config with createBaseConfig config [
remoteRepository "springsource-release"
"http://repository.springsource.com/maven/bundles/release",
dependency "org.yeti" "yeti" "0.9.3" [],
dependency "org.apache.commons" "commons-lang3" "3.0.1" [],
dependency "junit" "junit" "3.8.2" [TestScope()],
dependency "javax.servlet" "servlet-api" "2.4" [ProvidedScope()]];
The chrisichris github repository is already included by default
If you do not want to use the maven dependency mechanism or you need artifacts
which are not in a maven-repo than copy the jars to lib/unmanaged
. ybuilder
will put all jars in this directory to all build-pathes.
Similar to maven the ybuilder.core.base
module defines different predefined
targets which form a linear dependency build-order.
Livecycles are just normal predefined targets.
These are thought to make extension of the core build-process easier. Ie
if yout want to add some target which depends on the result of the compile
process you just make it depend on the compile
livecycle.
- preClean
- clean
- cleanAll //which also cleans the libs
- validate
- initializeBuild
- initialize
- generateResources
- processResources
- compile
- processClasses
- generateTestResources
- processTestResources
- testCompile
- processTestClasses
- test
- preparePackage
- pre-doc
- doc
The main sourcode is in ybuilder/core
dir.
To build it use:
java -Xmx512M -cp "lib/unmanaged/*" yeti.lang.compiler.yeti project_ybuilder.yeti clean, jary
or on Windows use the ybuilder.bat file
ybuilder clean, jary
if you want the resulting ybulder-lib-0.6.jar
copied to user-home .ybuilder
repostiory for the launcher use the installjar
target instead of jary
ybuilder clean, installjar