From f24a883b22bae4b2dde124545c3aa2107202c343 Mon Sep 17 00:00:00 2001
From: Cedric Beust
- 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.
+
-
- 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
- These "actors" are exactly what the
+
+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
+These "actors" are exactly what the
-
- The
+
+The
- 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:
+
- 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
- Tasks are provided by plug-ins and can be invoked from the command line, e.g.
- Static tasks are functions declared directly in your plug-in class and annotated with the
+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
+Tasks are provided by plug-ins and can be invoked from the command line, e.g.
+Static tasks are functions declared directly in your plug-in class and annotated with the
- 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
+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 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
- For example:
-
+For example:
+
-
- Once you have implemented
+
+Once you have implemented
- 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:
-
-
+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:
+
+
- The
+The
- Plug-ins that define properties should annotate them with the
+ Plug-ins that define properties should annotate them with the How to write a Kobalt plug-in
-
- Tutorial
- How to write a Kobalt plug-in
+
+Tutorial
+Plug-in architecture
-
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".
- 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.
- Plug-in architecture
+
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".
+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.
+Parts
-
-
-
-
- project
or dependencies
. These functions typically configure some data that your plug-in will later use to perform its functions.kobalt-plugin.xml
-
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:
- Parts
+
+
+
+
+project
or dependencies
. These functions typically configure some data that your plug-in will later use to perform its functions.kobalt-plugin.xml
+
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 +276,19 @@ class JavaBuildGenerator: ITemplateContributor {
- Selection process
- Selection process
+
interface IProjectAffinity {
/**
@@ -356,19 +356,19 @@ public fun myConfig(init: Info.() -> Unit) = Info().apply {
(Kobalt.findPlugin("my-plug-in") as MyPlugin).info = info
this
}
- Info
field, but you could instead choose to call
- a function, just set one boolean instead of the whole object, etc...
- Tasks
- ./kobaltw assemble
. There are two kinds of tasks: static and dynamic.
- Static tasks
- @Task
annotation. Here is an example:
- Info
field, but you could instead choose to call
+a function, just set one boolean instead of the whole object, etc...
+Tasks
+./kobaltw assemble
. There are two kinds of tasks: static and dynamic.
+Static tasks
+@Task
annotation. Here is an example:
+
@Task(name = "lineCount", description = "Count the lines", runBefore = arrayOf("compile"))
fun lineCount(project: Project): TaskResult {
@@ -376,96 +376,96 @@ fun lineCount(project: Project): TaskResult {
return TaskResult()
}
- Project
in parameter and return a TaskResult
, which indicates whether this task completed successfully.
- @Task
annotation accepts the following attributes:
-
-
-
- kobaltw
command.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"
.
- 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.
- 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.
- exampleTask
:
- ./kobaltw --dryRun compile
-
-
-
+Configuration for
- exampleTask
Result
-
-
-
+runBefore = "compile"
-
+
+ Project
in parameter and return a TaskResult
, which indicates whether this task completed successfully.
+@Task
annotation accepts the following attributes:
+
+
+
+kobaltw
command.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"
.
+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.
+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.
+exampleTask
:
+./kobaltw --dryRun compile
+
+
+
- Configuration for
+ exampleTask
Result
+
+
+
- runBefore = "compile"
+
- kobalt-line-count:clean
kobalt-line-count:exampleTask
kobalt-line-count:compile
-
-
+ runAfter = "compile"
-
-
+ kobalt-line-count:clean
+
+
- runAfter = "compile"
+
+
- kobalt-line-count:clean
kobalt-line-count:compile
-
-
+ alwaysRunAfter = "compile"
-
-
+ kobalt-line-count:clean
+
+
- alwaysRunAfter = "compile"
+
+
- kobalt-line-count:clean
kobalt-line-count:compile
kobalt-line-count:exampleTask
- Dynamic tasks
- @Task
- annotation on it). Plug-ins declare dynamic tasks by implementing the ITaskContributor
- intrface:
- Dynamic tasks
+@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>
}
-
override fun tasksFor(context: KobaltContext) = listOf(
DynamicTask(
@@ -476,15 +476,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
.
- 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
.
+ITaskContributor
, you can see your dynamic task in the list of tasks and run it directly:
+
$ ./kobaltw --tasks
===== kobalt-line-count =====
@@ -493,32 +493,32 @@ $ ./kobaltw --tasks
$ ./kobaltw dynamicTask
Running dynamictask
- Properties
-
-
- Project properties
- 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
+
+
+Project properties
+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)
- Plug-in properties
- 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:
- Plug-in properties
+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 +527,33 @@ override fun apply(project: Project, context: KobaltContext) {
val sourceDir = context.pluginProperties.get("pluginName", "somePluginProperty")
}
- Documenting properties
- @ExportedPluginProperty
or
- @ExportedProjectProperty
annotation:
- Documenting properties
+ @ExportedPluginProperty
or
+ @ExportedProjectProperty
annotation:
+
- companion object {
- @ExportedProjectProperty
- const val BUILD_DIR = "buildDir"
+companion object {
+@ExportedProjectProperty
+const val BUILD_DIR = "buildDir"
-
-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
From e5289f0bace6e63df506401ba1eab2bb36c6476e Mon Sep 17 00:00:00 2001
From: Cedric Beust
+ 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") + // Development dependency + compile("com.beust:kobalt:$KOBALT_VERSION") ++
+ 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" + +val p = project { + // ... + + compile("com.beust:$kobaltDependency:$KOBALT_VERSION") +} ++
+ 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"
).
+
From 729205059e954e5ccacd4855922e338b9899a0ad Mon Sep 17 00:00:00 2001
From: Cedric Beust
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:
@@ -91,6 +92,26 @@ val p = project {
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.
+
From 8d935dba2f81ddc6920797264431740664b98a47 Mon Sep 17 00:00:00 2001
From: Cedric Beust
fun taskAssemble(project: Project) : TaskResult { -project.projectProperties.put(PACKAGES, packages) + project.projectProperties.put("packages", packages) ++
+ Another plug-in can then query this property as follows: +
++ val packages = project.projectProperties.put("packages")
From 7a7b6675b5be37bb725b18038b4e3f5150eb8b35 Mon Sep 17 00:00:00 2001
From: Cedric Beust
./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:
From 35f732c3b10bf194bd35d517dd6616d95ddc67f6 Mon Sep 17 00:00:00 2001 From: Cedric Beust
- 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:
From 09b0ca7c8e77cc8750ab659d39e4529a3fdfa6e0 Mon Sep 17 00:00:00 2001
From: Cedric Beust
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
From 0daf6782a903a776a278e4c25ea003f562ac8416 Mon Sep 17 00:00:00 2001
From: Cedric Beust 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
From b0e5923bf1c8ae6cd357ac6735f93300185f8ed6 Mon Sep 17 00:00:00 2001
From: Cedric Beust ./kobaltw
:
./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:
@@ -569,7 +569,7 @@ New versions found:
The parameter to this argument is a list of template names separated by commas, e.g.
"java,myProject"
. Each template will be invoked in order so they can generate their files.
-
+ --listTemplates
--listTemplates
N/A
List all the templates available.
@@ -583,7 +583,7 @@ New versions found:
The default level is 1. Level 0 will quiet everything and 2 and 3 will display increasingly verbose output.
-
+ --noIncremental
--noIncremental
Boolean
false
Turn off incremental builds.
@@ -598,7 +598,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.
@@ -611,20 +611,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.
-
-
- --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.Testing