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: - - + + @@ -551,9 +551,9 @@ Here are the options that you can pass to ./kobaltw: - + - td> + - + @@ -592,6 +592,14 @@ New versions found: + + + + + + + @@ -600,7 +608,7 @@ New versions found: - + @@ -613,27 +621,20 @@ New versions found: - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
Use this option if you are trying to build a project whose Build.kt is not in kobalt/src.
--checkVersions--checkVersions Booleanfalsefalse 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:
     
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.Specify the log level. The default level is 1. Level 0 will quiet everything and 2 and 3 will display increasingly verbose output.
--noIncrementalBooleanfalseTurn 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'sThis 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.Display information about the given id. Display which repo this artifact can be found in and the whole graph of its dependencies.
--resolveMaven id
(e.g. "com.beust:kobalt:0.228")
N/ADisplay information about the given id.Display which repo this artifact can be found in and the whole graph of its dependencies.
--tasksBooleanfalseList the tasks available.Note that the available tasks will vary depending on which projects are in your build file.
--updateBooleanfalseUpdate 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.
--tasksBooleanfalseList the tasks available.Note that the available tasks will vary depending on which projects are in your build file.
--updateBooleanfalseUpdate 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

diff --git a/getting-started/index.html b/getting-started/index.html index e8df501..afb0aef 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -51,13 +51,13 @@ $ which kobaltw

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
 

2. Initialize your project

@@ -129,7 +129,7 @@ val jcommander = project {

 ./kobaltw assemble
-

5. IDEA users: generate IDEA files

+

5. IDEA users: generate IDEA files

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

6. IDEA users: sync your build file

- 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...).

diff --git a/idea-plug-in/index.html b/idea-plug-in/index.html index ba7b668..06a3971 100644 --- a/idea-plug-in/index.html +++ b/idea-plug-in/index.html @@ -72,7 +72,7 @@

- 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 @@ --> - -

- - - -

How to write a Kobalt plug-in

- -
-

Tutorial

-

- 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. -

+ +
+ + + +

How to write a Kobalt plug-in

+ +
+

Tutorial

+

+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. +

+

Setting up IDEA

+

Launch configuration

+

+ 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 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. -

-

+ // 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"
 
-                

Parts

-

-

    -
  • kobalt-plugin.xml. A file that describes all the components (called "plug-in actors") of your plug-in, such as contributors.
  • -
  • Directives. Kotlin functions that users of your plug-in can invoke in their build file, such as project or dependencies. These functions typically configure some data that your plug-in will later use to perform its functions.
  • -
  • Tasks. These tasks are invoked from the command line and ask your plug-ins to perform certain actions.
  • -
  • Properties. Plug-ins can export properties and read properties from other plug-ins.
  • -
-

- -

kobalt-plugin.xml

-

- 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: -

+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"). +

+

Local dependencies

+

+ 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-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 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

+

+

    +
  • kobalt-plugin.xml. A file that describes all the components (called "plug-in actors") of your plug-in, such as contributors.
  • +
  • Directives. Kotlin functions that users of your plug-in can invoke in their build file, such as project or dependencies. These functions typically configure some data that your plug-in will later use to perform its functions.
  • +
  • Tasks. These tasks are invoked from the command line and ask your plug-ins to perform certain actions.
  • +
  • Properties. Plug-ins can export properties and read properties from other plug-ins.
  • +
+

+ +

kobalt-plugin.xml

+

+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 {
-

Selection process

-

- 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: -

+

Selection process

+

+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

-

- 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

-

- 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

+

+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

+

+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: -

-
name
-
The name of the task, which will be used to invoke it from the command line.
-
description
-
The description of this command, which will be displayed if the user invokes the usage for the kobaltw command.
-
runBefore
-
A list of all the tasks that this task should run prior to.
-
runAfter
-
A list of all the tasks that should run before this task does.
-
alwaysRunAfter
-
A list of all the tasks that will always be run after this task if it's invoked.
-
-

-

- 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 exampleTaskResult
runBefore = "compile" +

+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: +

+
name
+
The name of the task, which will be used to invoke it from the command line.
+
description
+
The description of this command, which will be displayed if the user invokes the usage for the kobaltw command.
+
runBefore
+
A list of all the tasks that this task should run prior to.
+
runAfter
+
A list of all the tasks that should run before this task does.
+
alwaysRunAfter
+
A list of all the tasks that will always be run after this task if it's invoked.
+
+

+

+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 exampleTaskResult
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

-

- 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: -

+
+

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 @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

-

- 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 properties: project-specific properties.
  • -
  • Plug-in properties: general properties that are applicable to no project - in particular.
  • -
-

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

+

+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 properties: project-specific properties.
  • +
  • Plug-in properties: general properties that are applicable to no project + in particular.
  • +
+

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

-

- 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")
+
+

Plug-in properties

+

+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")
 }
 
-

Documenting properties

-

- Plug-ins that define properties should annotate them with the @ExportedPluginProperty or - @ExportedProjectPropertyannotation: -

+

Documenting properties

+

+ Plug-ins that define properties should annotate them with the @ExportedPluginProperty or + @ExportedProjectPropertyannotation: +

-    companion object {
-        @ExportedProjectProperty
-        const val BUILD_DIR = "buildDir"
+companion object {
+@ExportedProjectProperty
+const val BUILD_DIR = "buildDir"
 
-
- -
-
- - - - - - - - - -
- \ No newline at end of file +
+ +
+
+ + + + + + + + + +
+ \ No newline at end of file