Project Definition
Intro
Project definition files are Lua scripts in which tasks, build environments, and tools are defined. When Cobble executes the project definition files, the functions for defining these project building blocks are available as globals in the lua environment.
Project Definition Functions
task
function - Define a task
task(task_def)
Arguments
task_def
: table - task definition propertiesname
: string - the task nameactions
: table - A list ofaction
s that define the execution logic for the task.default
: bool | nil - whether the task is a default task for the project. Whencobl run
is given a project name, the default tasks for that project are run. If no tasks are defined as default for a project, passing the project name tocobl run
runs all tasks in the project. (default=false)always_run
: bool | nil - If true, the task will always be run if selected, regardless of whether its dependencies and artifacts are up-to-date. (default=false)interactive
: bool | nil - If true, child processes launched by this task can attach to stdin. Note that interactive tasks cannot run in parallel. (default=false)stdout
: "always" | "never" | "on_fail" | nil - When to display stdout output from the task (default="on_fail")stderr
: "always" | "never" | "on_fail" | nil - When to display stderr output from the task (default="on_fail")output
: "always" | "never" | "on_fail" | nil - Setting this property will set bothstdout
andstderr
properties. If eitherstdout
orstderr
properties are present, they will take precedence over the value provided byoutput
.env
: string | table | nil - If provided, the named action environment is available to all actions in the task. A table mapping an environment alias to an environment name is also valid, however only a single environment can be specified at the task level.clean
: table | nil - A list ofaction
s to run when the task is selected in acobl clean
command.deps
: table | nil - A mapping of dependency type to a list of dependenciesdirs
: table | nil - A list of directory dependency paths. When a task has a directory dependency, the directory tree is walked and modification time of directories are checked to detect any file additions or deletions within the directory tree. The directory dependency is considered out of date if the most recent directory modification time differs from what was recorded from the last run of the task.files
: table | nil - A list of file dependency paths. File dependencies are compared by contents.vars
: table | nil - A list of variable dependencies. As with TOML, variables within nested structures can be referenced using dot notation. For example, thepython.version
variable refers to theversion
variable inside thepython
table, (which would exist inside thevars
table incobble.toml
.)tasks
: table | nil - A list of task dependency namescalc
: table | nil - A list of tasks to execute for calculating dependencies. The calc task's output, (i.e. the return value of the tasks last action,) should match the same structure as thedeps
property for task definitions, with the exception that calc dependencies producing additional calc dependencies is not supported. Calculated results will be combined and added to the statically declared dependencies.
artifacts
: table | nil - A mapping of artifact type to a list of artifactsfiles
: table | nil - A list of file artifact pathscalc
: table | nil - A list of tasks to execute for calculating artifacts. The calc task's output should match the same structure as theartifacts
property for task definitions, with the exception that calc artifacts producing additional calc artifacts is not supported. Calculated results will be combined and added to the statically declared artifacts.
Returns
nil
env
function - Define an action environment
env(env_def)
Arguments
env_def
: table - action environment definition propertiesname
: string - The action environment namesetup_task
: task_def | string - The task to execute to set up the action environment, (e.g. "npm install"). Ifsetup_task
is a string, it is interpreted as a task name reference. Ifsetup_task
is a task definition, a new task will be created along with the action environment. Alltask_def
properties are supported except forname
. The setup task will be given the same name as the action environment.action
: action_def - An action that will run a command in the action environment, (e.g. "npm exec"). For function actions, the arguments passed to the action are available inc.args
. For actions defined using a table, the args are appended to the table and passed to the tool or action environment referenced by the action.
Returns
nil
tool
function - Define an external tool
tool(tool_def)
Arguments
tool_def
: table - Tool definition propertiesname
: string - The tool name. Unlike tasks and build environments, tool names are global, and are not combined with a project name to create a full name.check
: action_def - Anaction
for checking whether the tool is correctly installed, (correct version, etc.). If the check fails, the check action should callerror
to raise an error.action
: action_def - Anaction
that will execute the tool. For function actions, the arguments passed to the action are available inc.args
. For actions defined using a table, the args are appended to the table and passed to the tool or build environment referenced by the action.
Returns
nil
project_dir
function - Add a project in a subdirectory
project_dir(path)
Arguments
path
: string - The path to the project
Returns
nil
project
function - Add a named project in the same directory as the current project
project(name, project_def_cb)
Arguments
name
: string - The name of the subprojectproject_def_cb
: function - A function that, when called, createstask
,env
, andtool
definitions for the subproject.
Built-in Cmd Tool
Cobble provides one built-in tool: the cmd
tool. The cmd
tool uses Cobble's built-in cmd
module to execute a command in a subprocess, passing in the action context's project.dir
, out
, and err
properties as the cwd
, out
, and err
arguments to the cmd
function, respectively.
Actions
Actions can be defined using a table or function. See details below for the different ways to define an action.
Arg-list Actions
An arg-list action is defined using a Lua table with an optional tool
or env
property defining the tool or action environment that should be used to execute the action. (If omitted, the cmd
tool is used.)
The remaining table entries are passed to the referenced tool or environment as arguments.
If the action itself receives arguments, (e.g. if the action is defined in a tool or action environment,) those arguments are appended to the arguments defined by the action before passing them to the referenced tool or environment. This allows easy definition of tools or environments that simply accept arguments and append them to a particular command to be executed.
When an action is defined using an arg-list, the resulting action will only return a value if it is invoked as part of a tool or action environment. This is to avoid excess information from unintentionally being included in the task output. If a task should have outputs, its last action must be an action function.
Example
tool {
name = "npm",
-- Action that runs "npm <args>" when the tool is invoked.
-- Same as { tool = "cmd", "npm" }
action = { "npm" }
}
env {
name = "npm_exec",
setup_task = {
actions = {
-- Action that runs "npm install" using the tool defined above
{ tool = "npm", "install" }
}
},
-- Action that runs "npm exec -- <args>" when the env is invoked
action = { tool = "npm", "exec", "--" }
}
task {
name = "lint",
actions = {
-- Action that runs "npm exec -- eslint src/" using the env defined above
{ env = "npm_exec", "eslint", "src/" }
}
}
Action Functions
Action functions are defined using a Lua function. The function is passed in a single argument: an "action context", which provides useful information and functionality to the action.
An action function can be defined as a standalone function, or in a table alongside tool or action environment references. Unlike arg-list actions, action functions can have any number of tool or environment references, which will then make those tools and environments available in the action context passed to the action.
Examples
local tblext = require("tblext")
tool {
name = "npm",
-- Action that runs "npm <args>" when the tool is invoked
-- The Cobble built-in module "tblext" is used to append the passed-in arguments to the { "npm" } table
action = function (c)
return c.tool.cmd { tblext.extend({ "npm" }, c.args) }
end
}
env {
name = "npm_env",
setup_task = {
actions = {
-- Action that runs "npm install" using the tool defined above
{
tool = "npm",
function (c) return c.tool.npm { "install" } end
}
}
},
-- Action that runs "npm exec -- <args>" when the env is invoked
action = {
tool = "npm"
function (c) return c.tool.npm { tblext.extend({ "exec", "--" }, c.args) end
}
}
task {
name = "lint",
actions = {
-- Action that runs "npm exec -- eslint src/" using the env defined above
{
env = "npm_env",
function (c) return c.env.npm_env { "eslint", "src/" } end
}
}
}
Action Context
The action context passed to action functions has the following properties:
tool
: table - A table mapping tool names to tool invocation functions. Contains an entry for each tool available to the actionenv
: table - A table mapping build environment names to invocation functions. Contains an entry for each build environment available to the actionaction
: action - A copy of the action definition that defines the current actionfiles
: table - A table containing file dependencies of the task that the action belongs to. Table keys are file paths as they are declared in the task. Each value is a table containing apath
andhash
property, which provides both the path of the file relative to the workspace root and the fingerprint hash of the file contents.tasks
: table - A table containing information about task dependencies of the task that the action belongs to. Table keys are task dependency names as they were declared in the action's task. Each value is a table containing anoutput
and afiles
property.output
contains the output returned by the task, andfiles
is a table with the tasks file artifact paths and the keys, with the values containing apath
andhash
property for the artifact.vars
: table - A table containing information about variable dependencies of the task that the action belongs to. Table keys are variable names as they were declared in the action's task. Each value is the value of that variable.project
: table - A table containing information about the project in which the currently executing task is defined.dir
: string - The project directory in which the currently executing task is defined.
args
: A table containing arguments passed to the action. If the action is part of a tool or action environment being invoked,args
contains the arguments passed to the tool or environment. If the action belongs to a task,args
contains the return value of the previous action executed in the same task, if there is one.print
/println
: A function to send text to stdout. (Note that this function is preferred over the Luaprint
function, since it manages buffering and ensuring that a task's output gets printed out together in the console, instead of being interleaved with the output of other tasks being run in parallel.)eprint
/eprintln
: A function to send text to stderr. (Note that this function is preferred over the Luaprint
function for the same reasons as with theprint
function.)
Action Execution
When Cobble executes tasks, it distributes tasks among multiple threads, each with their own Lua environment. This presents a challenge for actions functions: the action function implementation must be copied from the Lua environment in which the action was defined into the Lua environment responsible for executing the task. To accomplish this, action functions are extracted from the Lua environment into an in-memory representation, along with any external local variables referenced by the function, (i.e "upvalues"). No global variables are extracted. This results in a few limitations that are not present in a typical Lua environment:
Module References
Upvalue references to modules in an action function can cause large amounts of code to be extracted along with the action function itself. It is recommended to declare separate local variables for the individual module members that are used by the action function if they are to be referenced as upvalues. Alternatively, you can require
the module from within the action function. For example:
local mymod = require("my.large.module")
task {
name = "bad_example",
actions = {
-- This action will cause the entire "mymod" module to
-- be extracted along with the action!
function (c) mymod.dowork() end
}
}
local dowork = mymod.dowork
task {
name = "better_example",
actions = {
-- This will only require mymod's "dowork" function to
-- be extracted along with the action
function (c) dowork() end
}
}
task {
name = "best_example",
actions = {
-- This function has no upvalues to be extracted.
function (c)
local m = require("my.large.module")
m.dowork()
end
}
}
Additionally, any references to native modules other than those to Cobble's built-in modules will cause action extraction to fail.
Action Return Values
When a task's last action returns a value, that value is converted to JSON and stored as the task's output. The task's output will be made available to other tasks that have that task as a dependency.
Note: Conversion from Lua object to JSON has some constraints to be aware of: Lua makes no distinction between maps and arrays. Everything is a table. When converting a Lua table in a task's output to JSON, cobble checks whether the Lua table is a "sequence", (i.e. a collection of values with contiguous, integer keys.) If it is a sequence, cobble will convert the table into a JSON array. Otherwise, it will convert all keys to strings, (if they aren't strings already,) and convert the table into a JSON object, (i.e. map). Note that the Lua table { [1] = "frog", [2] = nil, [3] = "cat", [4] = "fish" }
is not a "sequence", because in Lua, nil
is interpreted as undefined
, and the value at index 2
is considered to be nonexistent. (Accessing index 5
would also return nil
.) For that reason, if you want to have an action return an array that can have null values in it, it's recommended to use a value other than nil
to represent those null values, such as false
.