Command-Line Tools

This section describes the Coco Platform command-line tools. These tools can be separately installed from Downloads.

coco

The coco tool provides command-line access to all of the core features of the Coco Platform, including validating Coco files, formally verifying them, and then generating code. The main purpose of the command-line tools is to enable Coco to be integrated into a larger build process, but the tools are also designed to be human-usable as well.

coco [«global options»] «subcommand» [«options»] [«input files»]
«global options»:
 

The options that can be used with every «subcommand». See Global Options.

«subcommand»:

One of the following subcommands:

format:Format Coco source files.
generate-cpp:Generates C++ code
graph-component:
 Creates an architecture diagram of an encapsulating component
graph-states:Creates a graph of a state machine
metadata:Outputs metadata about a Coco package
support-bundle:Generates a zip file suitable for emailing to support
transform:Transforms Coco source files using Coco’s semantically-aware transformations.
typecheck:Type check Coco source files.
«options»:

Options specific to the chosen «subcommand».

«input files»:

The set of files to be processed.

The coco tool will either operate in package or standalone mode, depending on whether a Coco.toml file can be found to describe the Coco package to operate on. By default, coco will search the current working directory and every parent directory for a Coco.toml file. If none is found, then the tool is operating in standlone mode, and will only perform the given subcommand on the specified files. In general, when operating in package mode, the subcommand will apply to all files in the package, unless otherwise specified, whereas in standalone mode, the subcommand will always operate on the specified files.

For example, suppose the current working directory is MyProject and this contains a Coco.toml file, then:

coco verify

will execute the verify subcommand on all of the Coco files belonging to MyProject. Suppose there are Coco files in MyProject/src. The following command:

coco verify src/M.coco src/N.coco

will execute the verify subcommand on the files named M.coco and N.coco in the subdirectory src.

Global Options

These options are common to every subcommand.

--colour <boolean>

Specify whether or not the console text output is coloured.

--diagnostic-format <option>

Specify the format of error and warning messages.

Option:

checkstyle-xml

Errors and warnings are output in the Checkstyle XML format.

Option:

console

Error and warnings are output in plain text.

Default:

console

--error-limit <limit>

Specify a limit for the number of errors to emit.

--import-path, -i <path>

Specify an additional directory to be searched for imported files. This option can be specified multiple times. If the tool is operating in package mode, then the given directory is used to search for dependencies of the current package, as specified by package.dependencies. If the tool is operating in standalone mode, this folder will be added to the back of the search path of Coco modules when resolving an import like import X.

--no-crash-reporter

Prevent the Coco Platform crash reporter from starting.

--no-license-server

Prevent the Cocotec license server from starting. If specified, it prevents a license server from being started. A valid license must already have been installed in the default location on your computer. If not, this option will prevent coco from executing.

--override-licenses <path>

Override the path to the file containing the Coco licenses. This option is only allowed together with the --no-license-server option. When specified, there must be a valid license in the specified directory.

Option:The name of the directory containing a valid license file to be used.
Default:The license file is searched for in the default location.
--override-preferences <path>

Override the default preferences file. By default, coco shares the preferences specified in Eclipse.

--package <path>

Specify the directory containing the package file. This means that:

$ coco --package $dir verify

is equivalent to:

$ cd $dir
$ coco verify

For example:

$ coco --package ~/src/pkg verify

If ~/src/pkg contains a Coco.toml file, then coco will verify all modules in the package.

--stats

Display timing and resource usage information.

--version

Display current version information.

--warning-limit <limit>

Specify a limit for the number of warnings to emit.

Subcommands

format

Automatically reformats all of the Coco files according to a specified style. In package mode, the settings specified in the [format] section will be used. In standalone mode, default formatting settings are used.

--verify

The specified files are not modified. Instead, the console output shows the changes that would be made to them to conform to the applicable format preference settings. In this case, the exit code of the tool will be 0 only when no changes need to be made, and will be 1 if the files are not correctly formatted.

Common generation options

--code-index-format <option>

Specify the format of the generated code index.

Option:

json

Minimised JSON (i.e. no linebreaks, no spaces etc).

Option:

pretty-json

Pretty-printed JSON.

Default:

json

--output-code-index <path>

If set to a path, then a code index of the generated code will be created.

See also

C++ Code Index for details on the C++ code index.

--output <path>, -o <path>

Specify the directory in which the generated C++ files are created. If the directory does not exist, it is automatically created. If it does exist, its contents are merged with any new files that are generated. If this option is not specified, then the default value is generated, relative to the current package.

--output-deps

Specify if gcc-style deps files with dependency information should be generated together with the generated files.

--output-empty-files

Coco will generate one header file and one implementation file for every Coco file processed, even if the file would be empty.

--output-unchanged-files

Normally the command-line tool will not update generated files whose contents have not changed. With this flag, the tool will touch files that have not changed if the timestamp of the .coco file from which they were generated is newer than the timestamp of the generated file. This flag is primarily for compatibility with timestamp-based build systems such as make.

See also

generator.cpp.outputUnchagedFiles

--restrict <path>

Only the specified header file or implementation file will be generated. Note that the given path must be the path of the file when it is output, and thus must start with the path given to --output.

generate-cpp

Generate C++ source files from the specified Coco files according to the preference settings in the [generator.cpp] section of applicable package file. In addition to the options below, the options from Common generation options are also permitted.

--header-file-suffix <string>

Equivalent to package.headerFileSuffix.

--header-for <path>

If path is a path to a .coco file, will cause the generator to only generate the header file corresponding to the given .coco file.

--include-prefix <path>

Prepend the specified path to all include paths in the generated code.

Option:The path to prepend all include paths in the generated code.
--mocks <option>

Specify whether or not mocks should be generated and if so, which format should be used.

Option:

none

No mocks are generated.

Option:

gmock

Generate mocks compatible with the MOCK_METHOD macros.

Option:

gmock-legacy

Generate mocks compatible with the old-style deprecated MOCK_METHODn macros.

Default:

none

--implementation-file-suffix <string>

Equivalent to package.implementationFileSuffix.

--implementation-for <path>

If path is a path to a .coco file, will cause the generator to only generate the implementation file corresponding to the given .coco file.

--output-runtime-header

Equivalent to package.outputRuntimeHeader.

graph-component

Produces a graphical representation of the architecture of a Coco component, like that produced in the Eclipse Architecture Diagram View. For example:

$ coco graph-component --component=Comp -o - src/M.coco

Will generate the architecture diagram of Comp in M.coco, and will write it to standard out.

--component <name>

The name of the component or port whose state machine should be graphed. This can be specified multiple times.

--hide-ports

Specifies that all ports should be hidden.

--multi

Specifies that multiple components can be graphed, which may result in multiple output files being produced.

--output <path>

The path to where to save the generated diagram. If this is not specified, then the file is placed alongside the .coco file. If this is set to ‘-‘, then the diagram is written to standard out.

graph-states

Produces a graphical representation of a state machine, like that produced in the Eclipse State Diagram View. For example:

$ coco graph-states --target=Comp -o - src/M.coco

This will generate the state diagram for the state machine of Comp in M.coco, and will write it to standard out.

--target <name>

The name of the component or port whose state machine should be graphed.

--format <option>
Option:

dot

The Graphviz’s dot format will be used.

Option:

svg

A SVG image will be produced.

Default:

svg

--hide-labels

Specify that all labels on transitions should be hidden in the generated diagram.

--output <path>

The path to where to save the generated diagram. If this is not specified, then the file is placed alongside the .coco file. If this is set to ‘-‘, then the diagram is written to standard out.

--separate-edges

Whether to produce a separate edge for each transition. By default transition edges are collapsed by generating one shared edge for all transitions between a given pair of states.

metadata

Outputs metdata about a Coco package, depending on the option that is selected:

--selected-tool-version

Prints the value of the tool.selectedVersion property to stdout, including interpreting any special values that are permitted as part of this property.

support-bundle

This command copies a project, together with all of its dependencies, and creates a single zip file that is suitable for sending to support@cocotec.io. This command is intended for sharing confidential projects with Cocotec, as in addition to bundling all the files together, the bundler also renames all Coco identifies to randomly chosen names in a semantically-preserving fashion, and removes all comments.

--output <path>

The path where the .zip file should be saved.

transform

Executes one of Coco’s semantically aware transformations. When operating in package mode, all files in the current package are transformed (depending on the transformation selected), whereas when operating in standalone mode, just the specified files are transformed.

The following options are common to all transformations:

--format <option>
Option:

none

No files are formatted.

Option:

modified-files

All modified files are formatted.

Default:

none

Specifies whether the transformed files should be automatically formatted using the Coco formatter.

--output-mode <option>
Option:

in-place

Files are overwritten with the modified contents.

Option:

preview

Modified files are printed to stdout.

Default:

in-place

Specifies where the results of the transformation should be saved.

--selection <name>

A fully qualified name to the declaration to apply the transformation to, or the string :all to specify that the transformation should be applied to all declarations that it makes sense to apply it to.

transform-flatten-port

This transformation converts a composite port into a leaf port. This can be useful when there are only a small number of functions and signals defined amongst the different child ports, and therefore the composite port only serves to complicate the representation.

As an example, given:

port Composite {
  port P1 {
    function f1() : Nil
  }
  port P2 {
    enum AnEnum {
      case AMember
      case AnotherMember
    }
    outgoing signal cb(y : AnEnum)
  }
  val p1 : P1
  val p2 : P2
  machine {
    state Operational {
      p.f1() = p2.cb(P2.AnEnum.AMember)
    }
  }
}

The transformation will produce:

port Composite {
  function f1() : Nil
  enum AnEnum {
    case AMember
    case AnotherMember
  }
  outgoing signal cb(y : AnEnum)
  machine {
    state Operational {
      f1() = cb(AnEnum.AMember)
    }
  }
}

In addition to rewriting the port, the transformation also updates all components that use the port to use the correct functions and signals.

To apply this transformation:

  • The port must be composite and have a state machine.
  • The nested ports must be defined within the main port, not use inheritance, and there must be exactly one instance of each child field.

transform-merge-signals

This transformation converts pairs of signals into a single signal that has a parameter of type Result in order to distinguish between the two cases. This transformation can be useful when there are many layers of a system that do not need to distinguish between the cases, and instead just pass on the signal to the layer above. In such cases, this transformation can eliminate a lot of redundancy.

As an example, given:

port P {
  function fn() : Nil
  outgoing signal fn_cb_OK()
  outgoing signal fn_cb_ERROR()
  machine M {
    fn() = nondet { fn_cb_OK(), fn_cb_ERROR() }
  }
}

This is transformed to:

port P {
  function fn() : Nil
  outgoing signal fn_cb(result : Result<Nil, Nil>)

  machine M {
    fn() = nondet { fn_cb(.Ok(nil)), fn_cb(.Error(nil)) }
  }
}

This transformation will attempt to identify pairs of signals of the form: x_cb_OK and x_cb_ERROR and will merge them into a single signal called x_cb. These suffixes can be customised using the following options:

--error-suffix <suffix>
Default:_cb_ERROR

This specifies a string that should be searched for to identify the error signal. This will be used as the error case of the Result.

--ok-suffix <suffix>
Default:_cb_OK

This specifies a string that should be searched for to identify the success signal. This will be used as the ok case of the Result.

--new-suffix <suffix>
Default:_cb

This is used to compute the name of the combined signal. More precisely, the name of the combined signal is calculated by removing the error-suffix, and then adding new-suffix.

transform-to-external-functions

This transformation converts a stateless port into a collection of external functions. This can be useful when the functions are simple (e.g. logging functions), as the resulting Coco model will be simpler, easier to maintain, and will have better performance at runtime.

As an example, consder the port:

port Composite {
  enum E {
    case X
    case Y
  }

  function fn(x : Bool) : E

  machine M {
    fn(_ : Bool) = Composite.E.X
  }
}

This is transformed to:

enum E {
  case X
  case Y
}

external function fn(x : Bool) : E = E.X

In addition to rewriting the port, the transformation also updates all components that have required ports of type P to instead use the external functions, and removes any such ports from encapsulating components.

In order to apply this transformation, the port must:

  • Not be composite, or contain signals, or inherit from another port.
  • Must have a stateless state machine (i.e. a state machine with no states and no variables), where every transition is an event transition whose handler is an expression.

typecheck

Checks that the specified Coco files are all type-correct. When operating in package mode, all files in the current package are typechecked, whereas when operating in standalone mode, just the specified files are typechecked.

verify

Verifies all of the specified Coco files. When operating in package mode, all files in the current package are verified, whereas when operating in standalone mode, just the specified files are verified.

--backend <option>

Specify which verification backend is to be used.

Option:

local

Verification is performed locally.

Option:

attempt-remote

If the remote verification service is available, verification is carried out using the remote service. Otherwise, it is carried out locally.

Option:

remote

Verification is carried out using the remote verification service only, raising an error if it is not reachable.

Default:

if a preference has been set in Eclipse, then that is used, otherwise local.

--benchmark

Output performance data that can be used for verification benchmark comparisons.

--ignore-cache

Ignores all cached verification results when using the remote verification backend.

--format <option>

Specify verification output format.

Option:

junit

Verification output is formatted as JUnit-compatible XML.

Option:

console

Verification output is formatted as text.

Default:

console

--quiet

Suppresses progress information that is normlaly output whilst the verification is running.

--max-local-jobs

The maximum number of verification jobs that should be executed concurrently when running verification on the local machine.

Using coco in CI pipelines

The commands described above should allow coco to be easily used as part of a continuous integration (CI) pipeline. For example:

  • coco format --verify can be used to validate that all Coco code conforms to the Coco standard style. This outputs a diff if it detects any violations.
  • coco --diagnostic-format=checkstyle-xml typechecker can be used to check that all Coco code is syntactically and type correct, and will output diagnostics in Checkstyle-compatible XML, as accepted by some CI tools.
  • coco verify --format=junit can be used to verify all models, and to output the verification results as JUnit-compatible XML, which many CI tools can display.
  • coco generate-cpp and other generation commands can be used to generate code as part of the build.

See the Coco command-line tool for further details on these options and other useful options.

Licensing

To use Coco as part of a continuous integration (CI) pipeline, each machine that coco executes on will require a license. Since CI machines are often ephemeral, it can be inconvienient to license them using the usual interactive commands. Instead, Coco can be configured to use a license token that is not tied to any specific machine, but can instead be exchanged by a specific machine for a real license when it is required by connecting to a license server. Further, this license server can either be run on-premise, or Cocotec’s licensing server can be used, providing the CI machines are able to access the internet.

To setup Coco to use a machine token:

  1. Contact support@cocotec.io to request a machine license token, unless your organisation already has an existing on-premise license server in which case please contact your local administrators. When contacting Cocotec Support, please indicate if you require a on-premise license server to be setup, or if your CI systems have access to the internet and can therefore use Cocotec’s licensing servers.

  2. Store the machine license token securely using whatever secret storage mechanism you use. For example, common CI providers have builtin solutions for storing secrets:

  3. Configure your CI pipeline to pass the authentication token to coco. The best way of doing this depends upon the number of times that you execute coco during the build:

    • coco is only executed once:

      Set the environment variable COCOTEC_AUTH_TOKEN to the value of machine token before executing coco. coco will read this environment variable and then connect to the appropriate license server to obtain a license.

    • coco is executed multiple times:

      To avoid the overhead of connecting to the license server everytime coco is invoked, it is instead best to obtain a license up-front, and then reuse the license for the rest of the pipeline. This can be done by firstly executing cocotec-licensing-server to obtain the license, and then passing the cached license to coco. For example:

      $ export COCO_LICENSE_CACHE_FILE=<path>
      $ cocotec-licensing-server --acquire=coco-platform --license-file=$COCO_LICENSE_CACHE_FILE
      ...
      $ coco --no-license-server --override-licenses=$COCO_LICENSE_CACHE_FILE
      

      This will obtain a license using cocotec-licensing-server and then save the path specified in $COCO_LICENSE_CACHE_FILE. When coco is executed later, it will use the license that was obtained earlier. To make this work, cocotec-licensing-server has to be supplied the machine authentication token. This can be done either by setting the COCOTEC_AUTH_TOKEN environment variable only when invoking cocotec-licensing-server:

      COCOTEC_AUTH_TOKEN=... cocotec-licensing-server --acquire=coco-platform --license-file=$COCO_LICENSE_CACHE_FILE
      

      or, if the authentication token is available in a file called cocotec_token:

      cocotec-licensing-server --acquire=coco-platform --license-file=$COCO_LICENSE_CACHE_FILE --auth-token=cocotec_token