diff --git a/documentation/index.html b/documentation/index.html
index 455d5bd..d1fa715 100644
--- a/documentation/index.html
+++ b/documentation/index.html
@@ -527,8 +527,8 @@ Here are the options that you can pass to ./kobaltw
:
Use this option if you are trying to build a project whose Build.kt is not in kobalt/src . |
|||||||
--checkVersions |
+ --checkVersions |
Boolean | -false | td> +false | Display all the new versions of your dependencies. | This option looks at all the dependencies found in your build file and then contacts all the Maven repositories in order to find out if any of these repos contains a newer version. If any are found, they are displayed:
@@ -579,7 +579,7 @@ New versions found: | |
--listTemplates |
+ --listTemplates |
N/A | List all the templates available. | @@ -592,6 +592,14 @@ New versions found:Specify the log level. | The default level is 1. Level 0 will quiet everything and 2 and 3 will display increasingly verbose output. | ||
--noIncremental |
+ Boolean | +false | +Turn off incremental builds. | +If this flag is specified, Kobalt will run all the tasks, even those that are incremental and would have + been skipped. | +|||
--plugins |
Comma-separated list of plugin id's | @@ -600,7 +608,7 @@ New versions found:This is similar to specifying these plug-in id's in a build file except that no build file is needed. | |||||
--pluginJarFiles |
+ --pluginJarFiles |
Comma-separated list of plugin jar files | Specify the plug-ins to load. | @@ -613,27 +621,20 @@ New versions found:Display information about the given id. | Display which repo this artifact can be found in and the whole graph of its dependencies. | ||
--resolve |
- Maven id (e.g. "com.beust:kobalt:0.228" ) |
- N/A | -Display information about the given id. | -Display which repo this artifact can be found in and the whole graph of its dependencies. | -|||
--tasks |
- Boolean | -false | -List the tasks available. | -Note that the available tasks will vary depending on which projects are in your build file. | -|||
--update |
- Boolean | -false | -Update Kobalt to the latest version available. | -Use this flag if Kobalt just notified you that a new version is available and you want to update. Another way of doing this is to edit kobalt/wrapper/kobalt-wrapper.properties manually. |
- |||
--tasks |
+ Boolean | +false | +List the tasks available. | +Note that the available tasks will vary depending on which projects are in your build file. | +|||
--update |
+ Boolean | +false | +Update Kobalt to the latest version available. | +Use this flag if Kobalt just notified you that a new version is available and you want to update. Another way of doing this is to edit kobalt/wrapper/kobalt-wrapper.properties manually. |
+
-Download the zip file, unzip it and add that directory to your $PATH
variable so that you can invoke the command kobaltw
.
+Download the zip file, unzip it and add the bin
directory to your $PATH
variable so that you can invoke the command kobaltw
:
cd yourLocation unzip kobalt-xxx.zip cd kobalt-xxx -export PATH=$PWD:$PATH +export PATH=$PWD/bin:$PATH
@@ -129,7 +129,7 @@ val jcommander = project {
./kobaltw assemble-
If you are planning to use IDEA to work on your project, you can ask Kobalt to generate all the IDEA files necessary to import your project:
@@ -147,7 +147,8 @@ If you are planning to use IDEA to work on your project, you can ask Kobalt to g
- If you're using Intellij IDEA, make sure you've installed the Kobalt plugin and then go to Kobalt -> Sync Build File
. Once the build file is synchronized, the errors should disappear
+ If you're using Intellij IDEA, make sure you've installed the Kobalt plugin and then go to Tools → Kobalt → Sync Build File
. Once the build file is synchronized, the errors should
+ disappear
and you can now use all the regular functions of IDEA on Build.kt
just like any other Kotlin
files (auto completion, jump to symbol, etc...).
- Install it and restart IDEA. If the plug-in was correctly installed, you should see a new menu called "Kobalt" juste before the "Help" menu: + Install it and restart IDEA. If the plug-in was correctly installed, you should see a new menu called "Kobalt" in the "Tools" menu:
diff --git a/plug-in-development/index.html b/plug-in-development/index.html
index a54a451..7995665 100644
--- a/plug-in-development/index.html
+++ b/plug-in-development/index.html
@@ -30,54 +30,120 @@
-->
-
- If you are curious to get a quick feel for what a Kobalt plug-in looks like, I suggest you go read how to - write and publish a plug-in in ten minutes and then you can come back here - and keep reading. -
+ ++If you are curious to get a quick feel for what a Kobalt plug-in looks like, I suggest you go read how to +write and publish a plug-in in ten minutes and then you can come back here +and keep reading. +
++ The simplest way to run your plug-in in your IDE is to create a main function in the main class of your + plug-in as follows: +
++fun main(argv: Array<String>) { + com.beust.kobalt.main(argv) +} ++
+ In order for this code to compile, you will have to switch the dependency of your plug-in from
+ kobalt-plugin-api
to just kobalt
, which is the actual application (and which
+ therefore contains the main()
entry point).
+
+ // Normal dependency + compile("com.beust:kobalt-plugin-api:$KOBALT_VERSION") -+Plug-in architecture -
-
- Plug-ins often produce files and data that other plug-ins need to use in order for a build to succeed. For example, - the Android plug-in needs to generate a file called
-R.java
and then make this file available at - compile time by the Java or Kotlin (or any other language) plug-in. Since plug-ins have no idea about what other - plug-ins are currently enabled and running, they can't directly talk to each other so instead of calling into - Kobalt, Kobalt calls into them. This is done by declaring various "actors" that Kobalt will invoke whenever - it needs the information that your plug-in produced. This is a design pattern often referred to as the - "Hollywood Principle": "Don't call us, we'll call you". -- These "actors" are exactly what the
- + // Development dependency + compile("com.beust:kobalt:$KOBALT_VERSION") +kobalt-plugin.xml
file describes. This file informs Kobalt about - the various ways in which your plug-in participates in the build system by specifying 1) plug-ins, 2) contributors - or 3) interceptors. -
+ You might find it convenient to leverage Kobalt's ability to use regular Kotlin variables to make things easier: +
++val dev = false +val kobaltDependency = if (dev) "kobalt" else "kobalt-plugin-api" -Parts -
-
project
or dependencies
. These functions typically configure some data that your plug-in will later use to perform its functions.
- The kobalt-plugin.xml
file (stored in META-INF
in the jar file of your plug-in) is mandatory and describes all the actors of your plug-in. This file contains a list of class names, each of which is expected to implement at least one of IPluginActor
's interfaces:
-
+ Then you can simply set the dev
to true
during development and back to false
+
when you are ready to publish your plug-in.
+
+
+ Then resync your build file in IDEA and your main()
function should now build and be launchable.
+ You can right click on that class file and select "Debug <your class name>", which will launch Kobalt
+ with your plug-in. You can set a breakpoint in one of your tasks or anywhere that gets invoked. Don't forget
+ to invoke this launch configuration with the regular parameters passed to Kobalt (e.g. "assemble"
).
+
+ In the process of building your plug-in, you will probably be invoking it from test projects and since
+ you will be making changes to your plug-in and generating jar files often, you might find it more convenient
+ to have these test projects point to your local jar file instead of the Maven one (which would require you
+ to upload your plug-in all the time). For this, you might find the file()
and homeDir
+ ()
directives convenient:
+
+ // Regular dependency + compile("com.example:myPlugin:0.1") + + // Development dependency + compile(file(homeDir("kotlin/myPlugin/kobaltBuild/libs/myPlugin-0.1.jar")) ++
+ With this latter configuration, simply build your plug-in to generate the jar file with ./kobaltw
+ assemble
, switch to your test project and invoke Kobalt on it so that your plug-in will get invoked
+ and you should see the latest version of your code being invoked.
+
+
+Plug-ins often produce files and data that other plug-ins need to use in order for a build to succeed. For example,
+the Android plug-in needs to generate a file called R.java
and then make this file available at
+compile time by the Java or Kotlin (or any other language) plug-in. Since plug-ins have no idea about what other
+plug-ins are currently enabled and running, they can't directly talk to each other so instead of calling into
+Kobalt, Kobalt calls into them. This is done by declaring various "actors" that Kobalt will invoke whenever
+it needs the information that your plug-in produced. This is a design pattern often referred to as the
+"Hollywood Principle": "Don't call us, we'll call you".
+
+These "actors" are exactly what the kobalt-plugin.xml
file describes. This file informs Kobalt about
+the various ways in which your plug-in participates in the build system by specifying 1) plug-ins, 2) contributors
+or 3) interceptors.
+
+
project
or dependencies
. These functions typically configure some data that your plug-in will later use to perform its functions.
+The kobalt-plugin.xml
file (stored in META-INF
in the jar file of your plug-in) is mandatory and describes all the actors of your plug-in. This file contains a list of class names, each of which is expected to implement at least one of IPluginActor
's interfaces:
+
<plugin-actors> <class-name>com.beust.kobalt.plugin.java.JavaPlugin</class-name> @@ -276,19 +342,19 @@ class JavaBuildGenerator: ITemplateContributor {-
- Several plug-ins might want to contribute to a specific task where only one participant should be allowed, - such as running tests or generating documentation. Even the simple task of compiling should probably only - ever be performed by no more than one plug-in for a given project. Therefore, when comes the time to - compile a project, - Kobalt needs to find which plug-in is the most suitable for that task and pick it. In order to do that, - plug-ins that contribute to tasks that can only be performed by one candidate need to declare their - affinity to that task for a given project. -
-- Contributors that want to participate in a selection process need to implement the following interface: -
++Several plug-ins might want to contribute to a specific task where only one participant should be allowed, +such as running tests or generating documentation. Even the simple task of compiling should probably only +ever be performed by no more than one plug-in for a given project. Therefore, when comes the time to +compile a project, +Kobalt needs to find which plug-in is the most suitable for that task and pick it. In order to do that, +plug-ins that contribute to tasks that can only be performed by one candidate need to declare their +affinity to that task for a given project. +
++Contributors that want to participate in a selection process need to implement the following interface: +
interface IProjectAffinity { /** @@ -356,19 +422,19 @@ public fun myConfig(init: Info.() -> Unit) = Info().apply { (Kobalt.findPlugin("my-plug-in") as MyPlugin).info = info this }-
- Obviously, you can choose any kind of API to communicate between the directive and its plug-in. In the code
- above, I chose to directly override the entire Info
field, but you could instead choose to call
- a function, just set one boolean instead of the whole object, etc...
-
- Tasks are provided by plug-ins and can be invoked from the command line, e.g. ./kobaltw assemble
. There are two kinds of tasks: static and dynamic.
-
- Static tasks are functions declared directly in your plug-in class and annotated with the @Task
annotation. Here is an example:
-
+Obviously, you can choose any kind of API to communicate between the directive and its plug-in. In the code
+above, I chose to directly override the entire Info
field, but you could instead choose to call
+a function, just set one boolean instead of the whole object, etc...
+
+Tasks are provided by plug-ins and can be invoked from the command line, e.g. ./kobaltw assemble
. There are two kinds of tasks: static and dynamic.
+
+Static tasks are functions declared directly in your plug-in class and annotated with the @Task
annotation. Here is an example:
+
@Task(name = "lineCount", description = "Count the lines", runBefore = arrayOf("compile")) fun lineCount(project: Project): TaskResult { @@ -376,96 +442,96 @@ fun lineCount(project: Project): TaskResult { return TaskResult() }-
- A Kobalt task needs to accept a Project
in parameter and return a TaskResult
, which indicates whether this task completed successfully.
-
- The @Task
annotation accepts the following attributes:
-
kobaltw
command.
- The difference between runAfter
and alwaysRunAfter
is subtle but important. runAfter
- is just a declaration of dependency. It's basically the reverse of runBefore
but it's useful in case
- you are not the author of the task you want to run before (if you were, you would just use the runBefore
- annotation on it). Since you can't say "a runBefore b"
because you don't own task "a",
- you say "b runAfter a"
.
-
- For example, compileTest
is declared as a runAfter
for the task compile
.
- This means that it doesn't make sense to run compileTest
unless compile
has run first.
- However, if a user invokes the task compile
, they probably don't want to invoke compileTest
,
- so a dependency is exactly what we need here: invoking compileTest
will trigger compile
- but not the other way around.
-
- However, there are times where you want to define a task that will always run after a given task.
- For example, you could have a signJarFile
task that should always be invoked if someone builds a jar
- file. You don't expect users to invoke that target explicitly, but whenever they invoke the assemble
- target, you want your signJarFile
target to be invoked. When you want such a task to always be invoked
- even if the user didn't explicitly request it, you should use alwaysRunAfter
.
- Note that there is no alwaysRunBefore
annotation since runBefore
- achieves the same functionality.
-
- Here are a few different scenarios to illustrate how the three attributes work for the task exampleTask
:
-
- Result of the command ./kobaltw --dryRun compile
-
Configuration for exampleTask |
- Result | - -||||||||||||
runBefore = "compile" | -
+
+A Kobalt task needs to accept a
+The
+The difference between
+For example,
+However, there are times where you want to define a task that will always run after a given task.
+For example, you could have a
+Here are a few different scenarios to illustrate how the three attributes work for the task
+Result of the command
Dynamic tasks-
- Dynamic tasks are useful when you want your plug-in to generate one or several tasks that depend on
- some other runtime information (therefore, you can't declare a method and put a |
+
+Dynamic tasks are useful when you want your plug-in to generate one or several tasks that depend on
+some other runtime information (therefore, you can't declare a method and put a @Task
+annotation on it). Plug-ins declare dynamic tasks by implementing the ITaskContributor
+intrface:
+
interface ITaskContributor { - fun tasksFor(context: KobaltContext) : List<DynamicTask> +fun tasksFor(context: KobaltContext) : List<DynamicTask> }-
- For example: -
++For example: +
override fun tasksFor(context: KobaltContext) = listOf( DynamicTask( @@ -476,15 +542,15 @@ override fun tasksFor(context: KobaltContext) = listOf( println("Running dynamicTask") TaskResult() }))-
- DynamicTask
mirrors the @Task
attributes: name
, description
and
- dependencies. The only addition is the closure
parameter, which specifics the code that will
- run if your task gets invoked. That closure needs to follow the same constraints that a @Task
method
- obeys: it takes a Project
parameter and returns a TaskResult
.
-
- Once you have implemented ITaskContributor
, you can see your dynamic task in the list of tasks and run it directly:
-
+DynamicTask
mirrors the @Task
attributes: name
, description
and
+dependencies. The only addition is the closure
parameter, which specifics the code that will
+run if your task gets invoked. That closure needs to follow the same constraints that a @Task
method
+obeys: it takes a Project
parameter and returns a TaskResult
.
+
+Once you have implemented ITaskContributor
, you can see your dynamic task in the list of tasks and run it directly:
+
$ ./kobaltw --tasks ===== kobalt-line-count ===== @@ -493,32 +559,38 @@ $ ./kobaltw --tasks $ ./kobaltw dynamicTask Running dynamictask-
- Properties are the mechanism that plug-ins can use to export values and also read values that other - plug-ins have exported. There are two kinds of properties that plug-ins can manipulate: -
-
- Project
instances have a property called projectProperties
that is an
- instance of the ProjectProperties
class. Plugins can put and get values on this
- object in order to store project specific properties.
-
+Properties
++Properties are the mechanism that plug-ins can use to export values and also read values that other +plug-ins have exported. There are two kinds of properties that plug-ins can manipulate: +
+
+Project
instances have a property called projectProperties
that is an
+instance of the ProjectProperties
class. Plugins can put and get values on this
+object in order to store project specific properties.
+
fun taskAssemble(project: Project) : TaskResult { - project.projectProperties.put(PACKAGES, packages) + project.projectProperties.put("packages", packages)-
- The PluginProperties
instance can be found on the KobaltContext
- object that your plug-in receives in its apply()
method. Once you have an instance of this
- class, you can read or write variables into it:
-
+ Another plug-in can then query this property as follows: +
++ val packages = project.projectProperties.put("packages") ++
+The PluginProperties
instance can be found on the KobaltContext
+object that your plug-in receives in its apply()
method. Once you have an instance of this
+class, you can read or write variables into it:
+
override fun apply(project: Project, context: KobaltContext) { // Export a property for other plug-ins to use @@ -527,33 +599,33 @@ override fun apply(project: Project, context: KobaltContext) { val sourceDir = context.pluginProperties.get("pluginName", "somePluginProperty") }-
- Plug-ins that define properties should annotate them with the @ExportedPluginProperty
or
- @ExportedProjectProperty
annotation:
-
+ Plug-ins that define properties should annotate them with the @ExportedPluginProperty
or
+ @ExportedProjectProperty
annotation:
+
- companion object { - @ExportedProjectProperty - const val BUILD_DIR = "buildDir" +companion object { +@ExportedProjectProperty +const val BUILD_DIR = "buildDir"-